diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index e35a574c5fa..9b10bb2606d 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -23,6 +23,7 @@ pipeline { } triggers { issueCommentTrigger('(?i)^\\/packaging$') + upstream('Beats/beats-beats-mbp/master') } parameters { booleanParam(name: 'macos', defaultValue: false, description: 'Allow macOS stages.') @@ -76,7 +77,19 @@ pipeline { } environment { HOME = "${env.WORKSPACE}" - PLATFORMS = "!darwin +linux/armv7 +linux/ppc64le +linux/s390x +linux/mips64" + PLATFORMS = [ + '+all', + 'linux/amd64', + 'linux/386', + 'linux/arm64', + 'linux/armv7', + 'linux/ppc64le', + 'linux/mips64', + 'linux/s390x', + 'windows/amd64', + 'windows/386', + (params.macos ? '' : 'darwin/amd64'), + ].join(' ') } steps { release() @@ -94,7 +107,10 @@ pipeline { } environment { HOME = "${env.WORKSPACE}" - PLATFORMS = "!defaults +darwin/amd64" + PLATFORMS = [ + '+all', + 'darwin/amd64', + ].join(' ') } steps { withMacOSEnv(){ @@ -109,11 +125,51 @@ pipeline { } def pushCIDockerImages(){ - sh(label: 'Push Docker image', script: ''' - if [ -n "$(command -v docker)" ]; then - docker images || true - fi - ''') + catchError(buildResult: 'UNSTABLE', message: 'Unable to push Docker images', stageResult: 'FAILURE') { + if ("${env.BEATS_FOLDER}" == "auditbeat"){ + tagAndPush('auditbeat-oss') + } else if ("${env.BEATS_FOLDER}" == "filebeat") { + tagAndPush('filebeat-oss') + } else if ("${env.BEATS_FOLDER}" == "heartbeat"){ + tagAndPush('heartbeat') + tagAndPush('heartbeat-oss') + } else if ("${env.BEATS_FOLDER}" == "journalbeat"){ + tagAndPush('journalbeat') + tagAndPush('journalbeat-oss') + } else if ("${env.BEATS_FOLDER}" == "metricbeat"){ + tagAndPush('metricbeat-oss') + } else if ("${env.BEATS_FOLDER}" == "packetbeat"){ + tagAndPush('packetbeat') + tagAndPush('packetbeat-oss') + } else if ("${env.BEATS_FOLDER}" == "x-pack/auditbeat"){ + tagAndPush('auditbeat') + } else if ("${env.BEATS_FOLDER}" == "x-pack/elastic-agent") { + tagAndPush('elastic-agent') + } else if ("${env.BEATS_FOLDER}" == "x-pack/filebeat"){ + tagAndPush('filebeat') + } else if ("${env.BEATS_FOLDER}" == "x-pack/metricbeat"){ + tagAndPush('metricbeat') + } + } +} + +def tagAndPush(name){ + def libbetaVer = sh(label: 'Get libbeat version', script: 'grep defaultBeatVersion ${BASE_DIR}/libbeat/version/version.go|cut -d "=" -f 2|tr -d \\"', returnStdout: true)?.trim() + if("${env.SNAPSHOT}" == "true"){ + libbetaVer += "-SNAPSHOT" + } + def oldName = "${DOCKER_REGISTRY}/beats/${name}:${libbetaVer}" + def newName = "${DOCKER_REGISTRY}/observability-ci/${name}:${libbetaVer}" + def commitName = "${DOCKER_REGISTRY}/observability-ci/${name}:${env.GIT_BASE_COMMIT}" + dockerLogin(secret: "${DOCKERELASTIC_SECRET}", registry: "${DOCKER_REGISTRY}") + retry(3){ + sh(label:'Change tag and push', script: """ + docker tag ${oldName} ${newName} + docker push ${newName} + docker tag ${oldName} ${commitName} + docker push ${commitName} + """) + } } def release(){ diff --git a/.ci/scripts/install-kind.sh b/.ci/scripts/install-kind.sh new file mode 100755 index 00000000000..dc83bb4cd2a --- /dev/null +++ b/.ci/scripts/install-kind.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -exuo pipefail + +MSG="parameter missing." +DEFAULT_HOME="/usr/local" +KIND_VERSION=${KIND_VERSION:?$MSG} +HOME=${HOME:?$DEFAULT_HOME} +KIND_CMD="${HOME}/bin/kind" + +mkdir -p "${HOME}/bin" + +curl -sSLo "${KIND_CMD}" "https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/kind-linux-amd64" +chmod +x "${KIND_CMD}" diff --git a/.ci/scripts/install-kubectl.sh b/.ci/scripts/install-kubectl.sh new file mode 100755 index 00000000000..d0b7080d188 --- /dev/null +++ b/.ci/scripts/install-kubectl.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -exuo pipefail + +MSG="parameter missing." +DEFAULT_HOME="/usr/local" +K8S_VERSION=${K8S_VERSION:?$MSG} +HOME=${HOME:?$DEFAULT_HOME} +KUBECTL_CMD="${HOME}/bin/kubectl" + +mkdir -p "${HOME}/bin" + +curl -sSLo "${KUBECTL_CMD}" "https://storage.googleapis.com/kubernetes-release/release/${K8S_VERSION}/bin/linux/amd64/kubectl" +chmod +x "${KUBECTL_CMD}" + diff --git a/.ci/scripts/install-terraform.sh b/.ci/scripts/install-terraform.sh new file mode 100755 index 00000000000..39aa684d0aa --- /dev/null +++ b/.ci/scripts/install-terraform.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +MSG="parameter missing." +TERRAFORM_VERSION=${TERRAFORM_VERSION:?$MSG} +HOME=${HOME:?$MSG} +TERRAFORM_CMD="${HOME}/bin/terraform" + +OS=$(uname -s | tr '[:upper:]' '[:lower:]') + +mkdir -p "${HOME}/bin" + +curl -sSLo - "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_${OS}_amd64.zip" > ${TERRAFORM_CMD}.zip +unzip -o ${TERRAFORM_CMD}.zip -d $(dirname ${TERRAFORM_CMD}) +rm ${TERRAFORM_CMD}.zip + +chmod +x "${TERRAFORM_CMD}" diff --git a/.ci/scripts/kind-setup.sh b/.ci/scripts/kind-setup.sh index 4ac8fb0f6c3..fa4f66dd6e6 100755 --- a/.ci/scripts/kind-setup.sh +++ b/.ci/scripts/kind-setup.sh @@ -1,18 +1,5 @@ #!/usr/bin/env bash set -exuo pipefail -MSG="parameter missing." -K8S_VERSION=${K8S_VERSION:?$MSG} -HOME=${HOME:?$MSG} -KBC_CMD="${HOME}/bin/kubectl" - -mkdir -p "${HOME}/bin" - -curl -sSLo "${KBC_CMD}" "https://storage.googleapis.com/kubernetes-release/release/${K8S_VERSION}/bin/linux/amd64/kubectl" -chmod +x "${KBC_CMD}" - -GO111MODULE="on" go get sigs.k8s.io/kind@v0.5.1 kind create cluster --image kindest/node:${K8S_VERSION} - -export KUBECONFIG="$(kind get kubeconfig-path)" kubectl cluster-info diff --git a/.ci/scripts/terraform-cleanup.sh b/.ci/scripts/terraform-cleanup.sh new file mode 100755 index 00000000000..f1051b9b20d --- /dev/null +++ b/.ci/scripts/terraform-cleanup.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -exuo pipefail + +DIRECTORY=${1:-.} + +FAILED=0 +for tfstate in $(find $DIRECTORY -name terraform.tfstate); do + cd $(dirname $tfstate) + if ! terraform destroy -auto-approve; then + FAILED=1 + fi + cd - +done + +exit $FAILED diff --git a/.gitignore b/.gitignore index 34266183ac2..fe3a1459213 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ x-pack/dockerlogbeat/temproot.tar *.test *.prof *.pyc + +# Terraform +*.terraform +*.tfstate* diff --git a/.travis.yml b/.travis.yml index 7717c0366f8..b9f903b023b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -111,23 +111,28 @@ jobs: # Metricbeat - os: linux before_install: .ci/scripts/travis_has_changes.sh metricbeat libbeat || travis_terminate 0 - env: TARGETS="-C metricbeat unit-tests coverage-report" + env: TARGETS="-C metricbeat unit-tests" go: $TRAVIS_GO_VERSION stage: test - os: linux - before_install: .ci/scripts/travis_has_changes.sh metricbeat libbeat vendor || travis_terminate 0 - env: TARGETS="-C metricbeat integration-tests-environment coverage-report" + before_install: .ci/scripts/travis_has_changes.sh metricbeat libbeat || travis_terminate 0 + install: + - .ci/scripts/install-kind.sh + - .ci/scripts/install-kubectl.sh + env: + - TARGETS="-C metricbeat integration-tests" + - K8S_VERSION=v1.17.2 + - KIND_VERSION=v0.7.0 go: $TRAVIS_GO_VERSION stage: test - os: linux - before_install: .ci/scripts/travis_has_changes.sh metricbeat libbeat vendor || travis_terminate 0 - env: TARGETS="-C metricbeat update system-tests-environment coverage-report" + before_install: .ci/scripts/travis_has_changes.sh metricbeat libbeat || travis_terminate 0 + env: TARGETS="-C metricbeat system-tests" go: $TRAVIS_GO_VERSION stage: test - - os: osx before_install: .ci/scripts/travis_has_changes.sh metricbeat libbeat || travis_terminate 0 - env: TARGETS="TEST_ENVIRONMENT=0 -C metricbeat testsuite" + env: TARGETS="-C metricbeat testsuite" go: $TRAVIS_GO_VERSION stage: test - os: linux @@ -137,6 +142,21 @@ jobs: stage: test - os: linux before_install: .ci/scripts/travis_has_changes.sh x-pack/metricbeat metricbeat libbeat || travis_terminate 0 + env: TARGETS="-C x-pack/metricbeat unit-tests" + go: $TRAVIS_GO_VERSION + stage: test + - os: linux + before_install: .ci/scripts/travis_has_changes.sh x-pack/metricbeat metricbeat libbeat || travis_terminate 0 + env: TARGETS="-C x-pack/metricbeat integration-tests" + go: $TRAVIS_GO_VERSION + stage: test + - os: linux + before_install: .ci/scripts/travis_has_changes.sh x-pack/metricbeat metricbeat libbeat || travis_terminate 0 + env: TARGETS="-C x-pack/metricbeat system-tests" + go: $TRAVIS_GO_VERSION + stage: test + - os: osx + before_install: .ci/scripts/travis_has_changes.sh metricbeat libbeat || travis_terminate 0 env: TARGETS="-C x-pack/metricbeat testsuite" go: $TRAVIS_GO_VERSION stage: test diff --git a/CHANGELOG-developer.next.asciidoc b/CHANGELOG-developer.next.asciidoc index 42d45bb80b6..9fb3f6bf1ea 100644 --- a/CHANGELOG-developer.next.asciidoc +++ b/CHANGELOG-developer.next.asciidoc @@ -40,6 +40,12 @@ The list below covers the major changes between 7.0.0-rc2 and master only. - Extract Elasticsearch client logic from `outputs/elasticsearch` package into new `esclientleg` package. {pull}16150[16150] - Rename `queue.BufferConfig.Events` to `queue.BufferConfig.MaxEvents`. {pull}17622[17622] - Remove `queue.Feature` and replace `queue.RegisterType` with `queue.RegisterQueueType`. {pull}17666[17666] +- Introduce APM libbeat instrumentation. `Publish` method on `Client` interface now takes a Context as first argument. {pull}17938[17938] +- The way configuration files are generated has changed to make it easier to customize parts + of the config without requiring changes to libbeat config templates. Generation is now + fully based on Go text/template and no longer uses file concatenation to generate the config. + Your magefile.go will require a change to adapt the devtool API. See the pull request for + more details. {pull}18148[18148] ==== Bugfixes diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index b3fa34b8b9b..3da2b9c4fcc 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,6 +30,7 @@ https://github.com/elastic/beats/compare/v7.5.0...v7.5.1[View commits] - Fix docker network stats when multiple interfaces are configured. {issue}14586[14586] {pull}14825[14825] - Fix ListMetrics pagination in aws module. {issue}14926[14926] {pull}14942[14942] - Fix CPU count in docker/cpu in cases where no `online_cpus` are reported {pull}15070[15070] +- Add domain state to kvm module {pull}17673[17673] [[release-notes-7.5.0]] === Beats version 7.5.0 diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index e01125fcd7b..0e31471030c 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -14,13 +14,16 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Variable substitution from environment variables is not longer supported. {pull}15937{15937} - Change aws_elb autodiscover provider field name from elb_listener.* to aws.elb.*. {issue}16219[16219] {pull}16402{16402} - Remove `AddDockerMetadata` and `AddKubernetesMetadata` processors from the `script` processor. They can still be used as normal processors in the configuration. {issue}16349[16349] {pull}16514[16514] +- Introduce APM libbeat instrumentation, active when running the beat with ELASTIC_APM_ACTIVE=true. {pull}17938[17938] *Auditbeat* - File integrity dataset (macOS): Replace unnecessary `file.origin.raw` (type keyword) with `file.origin.text` (type `text`). {issue}12423[12423] {pull}15630[15630] *Filebeat* - +- Improve ECS field mappings in panw module. event.outcome now only contains success/failure per ECS specification. {issue}16025[16025] {pull}17910[17910] +- Improve ECS categorization field mappings for nginx module. http.request.referrer is now lowercase & http.request.referrer only populated when nginx sets a value {issue}16174[16174] {pull}17844[17844] +- Improve ECS field mappings in santa module. move hash.sha256 to process.hash.sha256 & move certificate fields to santa.certificate . {issue}16180[16180] {pull}17982[17982] *Heartbeat* @@ -76,6 +79,12 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix building on FreeBSD by removing build flags from `add_cloudfoundry_metadata` processor. {pull}17486[17486] - Do not rotate log files on startup when interval is configured and rotateonstartup is disabled. {pull}17613[17613] - Fix goroutine leak and Elasticsearch output file descriptor leak when output reloading is in use. {issue}10491[10491] {pull}17381[17381] +- Fix `setup.dashboards.index` setting not working. {pull}17749[17749] +- Fix Elasticsearch license endpoint URL referenced in error message. {issue}17880[17880] {pull}18030[18030] +- Fix panic when assigning a key to a `nil` value in an event. {pull}18143[18143] +- Gives monitoring reporter hosts, if configured, total precedence over corresponding output hosts. {issue}17937[17937] {pull}17991[17991] +- Arbitrary fields and metadata maps are now deep merged into event. {pull}17958[17958] +- Change `decode_json_fields` processor, to merge parsed json objects with existing objects in the event instead of fully replacing them. {pull}17958[17958] *Auditbeat* @@ -113,6 +122,9 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Remove migrationVersion map 7.7.0 reference from Kibana dashboard file to fix backward compatibility issues. {pull}17425[17425] - Fix issue 17734 to retry on rate-limit error in the Filebeat httpjson input. {issue}17734[17734] {pull}17735[17735] - Fixed `cloudfoundry.access` to have the correct `cloudfoundry.app.id` contents. {pull}17847[17847] +- Fixing `ingress_controller.` fields to be of type keyword instead of text. {issue}17834[17834] +- Fixed typo in log message. {pull}17897[17897] +- Fix Cisco ASA ASA 3020** and 106023 messages {pull}17964[17964] *Heartbeat* @@ -196,9 +208,16 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Update RPM packages contained in Beat Docker images. {issue}17035[17035] - Update supported versions of `redis` output. {pull}17198[17198] - Update documentation for system.process.memory fields to include clarification on Windows os's. {pull}17268[17268] +- Add `replace` processor for replacing string values of fields. {pull}17342[17342] - Add optional regex based cid extractor to `add_kubernetes_metadata` processor. {pull}17360[17360] - Add `urldecode` processor to for decoding URL-encoded fields. {pull}17505[17505] - Add support for AWS IAM `role_arn` in credentials config. {pull}17658[17658] {issue}12464[12464] +- Add keystore support for autodiscover static configurations. {pull]16306[16306] +- Add Kerberos support to Elasticsearch output. {pull}17927[17927] +- Add support for fixed length extraction in `dissect` processor. {pull}17191[17191] +- Set `agent.name` to the hostname by default. {issue}16377[16377] {pull}18000[18000] +- Add config example of how to skip the `add_host_metadata` processor when forwarding logs. {issue}13920[13920] {pull}18153[18153] +- When using the `decode_json_fields` processor, decoded fields are now deep-merged into existing event. {pull}17958[17958] *Auditbeat* @@ -207,6 +226,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Log to stderr when running using reference kubernetes manifests. {pull}17443[174443] - Fix syscall kprobe arguments for 32-bit systems in socket module. {pull}17500[17500] - Fix memory leak on when we miss socket close kprobe events. {pull}17500[17500] +- Add system module process dataset ECS categorization fields. {pull}18032[18032] - Add file integrity module ECS categorization fields. {pull}18012[18012] - Add `file.mime_type`, `file.extension`, and `file.drive_letter` for file integrity module. {pull}18012[18012] @@ -270,11 +290,22 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Enhance `elasticsearch/slowlog` fileset to handle ECS-compatible logs emitted by Elasticsearch. {issue}17715[17715] {pull}17729[17729] - Improve ECS categorization field mappings in misp module. {issue}16026[16026] {pull}17344[17344] - Added Unix stream socket support as an input source and a syslog input source. {pull}17492[17492] +- Improve ECS categorization field mappings in postgresql module. {issue}16177[16177] {pull}17914[17914] +- Improve ECS categorization field mappings in rabbitmq module. {issue}16178[16178] {pull}17916[17916] +- Make `decode_cef` processor GA. {pull}17944[17944] +- Improve ECS categorization field mappings in redis module. {issue}16179[16179] {pull}17918[17918] +- Improve ECS categorization field mappings for zeek module. {issue}16029[16029] {pull}17738[17738] +- Improve ECS categorization field mappings for netflow module. {issue}16135[16135] {pull}18108[18108] +- Added an input option `publisher_pipeline.disable_host` to disable `host.name` + from being added to events by default. {pull}18159[18159] +- Improve ECS categorization field mappings in system module. {issue}16031[16031] {pull}18065[18065] +- When using the `json.*` setting available on some inputs, decoded fields are now deep-merged into existing event. {pull}17958[17958] +- Change the `json.*` input settings implementation to merge parsed json objects with existing objects in the event instead of fully replacing them. {pull}17958[17958] *Heartbeat* - Allow a list of status codes for HTTP checks. {pull}15587[15587] - +- Add additional ECS compatible fields for TLS information. {pull}17687[17687] *Journalbeat* @@ -343,6 +374,15 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Reference kubernetes manifests mount data directory from the host when running metricbeat as daemonset, so data persist between executions in the same node. {pull}17429[17429] - Add more detailed error messages, system tests and small refactoring to the service metricset in windows. {pull}17725[17725] - Stack Monitoring modules now auto-configure required metricsets when `xpack.enabled: true` is set. {issue}16471[[16471] {pull}17609[17609] +- Add Metricbeat IIS module dashboards. {pull}17966[17966] +- Add dashboard for the azure database account metricset. {pull}17901[17901] +- Allow partial region and zone name in googlecloud module config. {pull}17913[17913] +- Add aggregation aligner as a config parameter for googlecloud stackdriver metricset. {issue}17141[[17141] {pull}17719[17719] +- Move the perfmon metricset to GA. {issue}16608[16608] {pull}17879[17879] +- Add static mapping for metricsets under aws module. {pull}17614[17614] {pull}17650[17650] +- Collect new `bulk` indexing metrics from Elasticsearch when `xpack.enabled:true` is set. {issue} {pull}17992[17992] +- Remove requirement to connect as sysdba in Oracle module {issue}15846[15846] {pull}18182[18182] +- Update MSSQL module to fix some SSPI authentication and add brackets to USE statements {pull}17862[17862]] *Packetbeat* @@ -353,6 +393,8 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add more DNS error codes to the Sysmon module. {issue}15685[15685] - Add experimental event log reader implementation that should be faster in most cases. {issue}6585[6585] {pull}16849[16849] +- Set process.command_line and process.parent.command_line from Sysmon Event ID 1. {pull}17327[17327] +- Add support for event IDs 4673,4674,4697,4698,4699,4700,4701,4702,4768,4769,4770,4771,4776,4778,4779,4964 to the Security module {pull}17517[17517] ==== Deprecated diff --git a/Jenkinsfile b/Jenkinsfile index 4480a2af642..d38373d4f84 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,15 +2,25 @@ @Library('apm@current') _ +import groovy.transform.Field + +/** + This is required to store the stashed id with the test results to be digested with runbld +*/ +@Field def stashedTestReports = [:] + pipeline { agent { label 'ubuntu && immutable' } environment { BASE_DIR = 'src/github.com/elastic/beats' GOX_FLAGS = "-arch amd64" DOCKER_COMPOSE_VERSION = "1.21.0" + TERRAFORM_VERSION = "0.12.24" PIPELINE_LOG_LEVEL = "INFO" DOCKERELASTIC_SECRET = 'secret/observability-team/ci/docker-registry/prod' DOCKER_REGISTRY = 'docker.elastic.co' + AWS_ACCOUNT_SECRET = 'secret/observability-team/ci/elastic-observability-aws-account-auth' + RUNBLD_DISABLE_NOTIFICATIONS = 'true' } options { timeout(time: 2, unit: 'HOURS') @@ -28,6 +38,11 @@ pipeline { booleanParam(name: 'runAllStages', defaultValue: false, description: 'Allow to run all stages.') booleanParam(name: 'windowsTest', defaultValue: true, description: 'Allow Windows stages.') booleanParam(name: 'macosTest', defaultValue: true, description: 'Allow macOS stages.') + + booleanParam(name: 'allCloudTests', defaultValue: false, description: 'Run all cloud integration tests.') + booleanParam(name: 'awsCloudTests', defaultValue: false, description: 'Run AWS cloud integration tests.') + string(name: 'awsRegion', defaultValue: 'eu-central-1', description: 'Default AWS region to use for testing.') + booleanParam(name: 'debug', defaultValue: false, description: 'Allow debug logging for Jenkins steps') booleanParam(name: 'dry_run', defaultValue: false, description: 'Skip build steps, it is for testing pipeline flow') } @@ -39,14 +54,14 @@ pipeline { options { skipDefaultCheckout() } steps { deleteDir() - gitCheckout(basedir: "${BASE_DIR}") + gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: true) + stash allowEmpty: true, name: 'source', useDefaultExcludes: false dir("${BASE_DIR}"){ loadConfigEnvVars() } whenTrue(params.debug){ dumpFilteredEnvironment() } - stash allowEmpty: true, name: 'source', useDefaultExcludes: false } } stage('Lint'){ @@ -319,7 +334,20 @@ pipeline { } } steps { - mageTarget("Metricbeat OSS linux/amd64 (integTest)", "metricbeat", "integTest") + mageTarget("Metricbeat OSS linux/amd64 (goIntegTest)", "metricbeat", "goIntegTest") + } + } + stage('Metricbeat Python integration tests'){ + agent { label 'ubuntu && immutable' } + options { skipDefaultCheckout() } + when { + beforeAgent true + expression { + return env.BUILD_METRICBEAT != "false" + } + } + steps { + mageTarget("Metricbeat OSS linux/amd64 (pythonIntegTest)", "metricbeat", "pythonIntegTest") } } stage('Metricbeat x-pack'){ @@ -331,8 +359,30 @@ pipeline { return env.BUILD_METRICBEAT_XPACK != "false" } } - steps { - mageTarget("Metricbeat x-pack Linux", "x-pack/metricbeat", "build test") + stages { + stage('Prepare cloud integration tests environments'){ + agent { label 'ubuntu && immutable' } + options { skipDefaultCheckout() } + steps { + startCloudTestEnv('x-pack-metricbeat', [ + [cond: params.awsCloudTests, dir: 'x-pack/metricbeat/module/aws'], + ]) + } + } + stage('Metricbeat x-pack'){ + agent { label 'ubuntu && immutable' } + options { skipDefaultCheckout() } + steps { + withCloudTestEnv() { + mageTarget("Metricbeat x-pack Linux", "x-pack/metricbeat", "build test") + } + } + } + } + post { + cleanup { + terraformCleanup('x-pack-metricbeat', 'x-pack/metricbeat') + } } } stage('Metricbeat crosscompile'){ @@ -526,14 +576,20 @@ pipeline { stages { stage('Generators Metricbeat Linux'){ steps { - makeTarget("Generators Metricbeat Linux", "-C generator/_templates/metricbeat test") - makeTarget("Generators Metricbeat Linux", "-C generator/_templates/metricbeat test-package") + // FIXME see https://github.com/elastic/beats/issues/18132 + catchError(buildResult: 'SUCCESS', message: 'Ignore error temporally', stageResult: 'UNSTABLE') { + makeTarget("Generators Metricbeat Linux", "-C generator/_templates/metricbeat test") + makeTarget("Generators Metricbeat Linux", "-C generator/_templates/metricbeat test-package") + } } } stage('Generators Beat Linux'){ steps { - makeTarget("Generators Beat Linux", "-C generator/_templates/beat test") - makeTarget("Generators Beat Linux", "-C generator/_templates/beat test-package") + // FIXME see https://github.com/elastic/beats/issues/18132 + catchError(buildResult: 'SUCCESS', message: 'Ignore error temporally', stageResult: 'UNSTABLE') { + makeTarget("Generators Beat Linux", "-C generator/_templates/beat test") + makeTarget("Generators Beat Linux", "-C generator/_templates/beat test-package") + } } } stage('Generators Metricbeat Mac OS X'){ @@ -546,7 +602,10 @@ pipeline { } } steps { - makeTarget("Generators Metricbeat Mac OS X", "-C generator/_templates/metricbeat test") + // FIXME see https://github.com/elastic/beats/issues/18132 + catchError(buildResult: 'SUCCESS', message: 'Ignore error temporally', stageResult: 'UNSTABLE') { + makeTarget("Generators Metricbeat Mac OS X", "-C generator/_templates/metricbeat test") + } } } stage('Generators Beat Mac OS X'){ @@ -559,7 +618,10 @@ pipeline { } } steps { - makeTarget("Generators Beat Mac OS X", "-C generator/_templates/beat test") + // FIXME see https://github.com/elastic/beats/issues/18132 + catchError(buildResult: 'SUCCESS', message: 'Ignore error temporally', stageResult: 'UNSTABLE') { + makeTarget("Generators Beat Mac OS X", "-C generator/_templates/beat test") + } } } } @@ -574,12 +636,20 @@ pipeline { } } steps { - k8sTest(["v1.16.2","v1.15.3","v1.14.6","v1.13.10","v1.12.10","v1.11.10"]) + k8sTest(["v1.18.2","v1.17.2","v1.16.4","v1.15.7","v1.14.10"]) } } } } } + post { + always { + runbld() + } + cleanup { + notifyBuildResult(prComment: true) + } + } } def makeTarget(String context, String target, boolean clean = true) { @@ -642,24 +712,16 @@ def withBeatsEnv(boolean archive, Closure body) { "TEST_COVERAGE=true", "RACE_DETECTOR=true", "PYTHON_ENV=${WORKSPACE}/python-env", - "TEST_TAGS=oracle", + "TEST_TAGS=${env.TEST_TAGS},oracle", "DOCKER_PULL=0", ]) { deleteDir() unstash 'source' - if(os == 'linux'){ + if(isDockerInstalled()){ dockerLogin(secret: "${DOCKERELASTIC_SECRET}", registry: "${DOCKER_REGISTRY}") - // FIXME workaround until we fix the packer cache - // Retry to avoid DDoS detection from the server - retry(3) { - sleep randomNumber(min: 2, max: 5) - sh 'docker pull docker.elastic.co/observability-ci/database-enterprise:12.2.0.1' - } } dir("${env.BASE_DIR}") { - sh(label: "Install Go ${GO_VERSION}", script: ".ci/scripts/install-go.sh") - sh(label: "Install docker-compose ${DOCKER_COMPOSE_VERSION}", script: ".ci/scripts/install-docker-compose.sh") - sh(label: "Install Mage", script: "make mage") + installTools() // TODO (2020-04-07): This is a work-around to fix the Beat generator tests. // See https://github.com/elastic/beats/issues/17787. setGitConfig() @@ -670,7 +732,7 @@ def withBeatsEnv(boolean archive, Closure body) { } finally { if (archive) { catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - junit(allowEmptyResults: true, keepLongStdio: true, testResults: "**/build/TEST*.xml") + junitAndStore(allowEmptyResults: true, keepLongStdio: true, testResults: "**/build/TEST*.xml") archiveArtifacts(allowEmptyArchive: true, artifacts: '**/build/TEST*.out') } } @@ -697,14 +759,14 @@ def withBeatsEnvWin(Closure body) { deleteDir() unstash 'source' dir("${env.BASE_DIR}"){ - bat(label: "Install Go/Mage/Python ${GO_VERSION}", script: ".ci/scripts/install-tools.bat") + installTools() try { if(!params.dry_run){ body() } } finally { catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - junit(allowEmptyResults: true, keepLongStdio: true, testResults: "**\\build\\TEST*.xml") + junitAndStore(allowEmptyResults: true, keepLongStdio: true, testResults: "**\\build\\TEST*.xml") archiveArtifacts(allowEmptyArchive: true, artifacts: '**\\build\\TEST*.out') } } @@ -712,6 +774,18 @@ def withBeatsEnvWin(Closure body) { } } +def installTools() { + def i = 2 // Number of retries + if(isUnix()) { + retry(i) { sh(label: "Install Go ${GO_VERSION}", script: ".ci/scripts/install-go.sh") } + retry(i) { sh(label: "Install docker-compose ${DOCKER_COMPOSE_VERSION}", script: ".ci/scripts/install-docker-compose.sh") } + retry(i) { sh(label: "Install Terraform ${TERRAFORM_VERSION}", script: ".ci/scripts/install-terraform.sh") } + retry(i) { sh(label: "Install Mage", script: "make mage") } + } else { + retry(i) { bat(label: "Install Go/Mage/Python ${GO_VERSION}", script: ".ci/scripts/install-tools.bat") } + } +} + def goos(){ def labels = env.NODE_LABELS @@ -777,6 +851,7 @@ def dumpFilteredEnvironment(){ echo "SYSTEM_TESTS: ${env.SYSTEM_TESTS}" echo "STRESS_TESTS: ${env.STRESS_TESTS}" echo "STRESS_TEST_OPTIONS: ${env.STRESS_TEST_OPTIONS}" + echo "TEST_TAGS: ${env.TEST_TAGS}" echo "GOX_OS: ${env.GOX_OS}" echo "GOX_OSARCH: ${env.GOX_OSARCH}" echo "GOX_FLAGS: ${env.GOX_FLAGS}" @@ -794,14 +869,14 @@ def dumpFilteredEnvironment(){ def k8sTest(versions){ versions.each{ v -> stage("k8s ${v}"){ - withEnv(["K8S_VERSION=${v}"]){ + withEnv(["K8S_VERSION=${v}", "KIND_VERSION=v0.7.0", "KUBECONFIG=${env.WORKSPACE}/kubecfg"]){ withGithubNotify(context: "K8s ${v}") { withBeatsEnv(false) { - sh(label: "Install k8s", script: """ - eval "\$(gvm use ${GO_VERSION} --format=bash)" - .ci/scripts/kind-setup.sh - """) - sh(label: "Kubernetes Kind",script: "make KUBECONFIG=\"\$(kind get kubeconfig-path)\" -C deploy/kubernetes test") + sh(label: "Install kind", script: ".ci/scripts/install-kind.sh") + sh(label: "Install kubectl", script: ".ci/scripts/install-kubectl.sh") + sh(label: "Integration tests", script: "MODULE=kubernetes make -C metricbeat integration-tests") + sh(label: "Setup kind", script: ".ci/scripts/kind-setup.sh") + sh(label: "Deploy to kubernetes",script: "make -C deploy/kubernetes test") sh(label: 'Delete cluster', script: 'kind delete cluster') } } @@ -843,10 +918,9 @@ def isChangedOSSCode(patterns) { "^libbeat/.*", "^testing/.*", "^dev-tools/.*", - "^\\.ci/.*", + "^\\.ci/scripts/.*", ] allPatterns.addAll(patterns) - allPatterns.addAll(getVendorPatterns('libbeat')) return isChanged(allPatterns) } @@ -858,17 +932,112 @@ def isChangedXPackCode(patterns) { "^dev-tools/.*", "^testing/.*", "^x-pack/libbeat/.*", - "^\\.ci/.*", + "^\\.ci/scripts/.*", ] allPatterns.addAll(patterns) - allPatterns.addAll(getVendorPatterns('x-pack/libbeat')) return isChanged(allPatterns) } +// withCloudTestEnv executes a closure with credentials for cloud test +// environments. +def withCloudTestEnv(Closure body) { + def maskedVars = [] + def testTags = "${env.TEST_TAGS}" + + // AWS + if (params.allCloudTests || params.awsCloudTests) { + testTags = "${testTags},aws" + def aws = getVaultSecret(secret: "${AWS_ACCOUNT_SECRET}").data + if (!aws.containsKey('access_key')) { + error("${AWS_ACCOUNT_SECRET} doesn't contain 'access_key'") + } + if (!aws.containsKey('secret_key')) { + error("${AWS_ACCOUNT_SECRET} doesn't contain 'secret_key'") + } + maskedVars.addAll([ + [var: "AWS_REGION", password: params.awsRegion], + [var: "AWS_ACCESS_KEY_ID", password: aws.access_key], + [var: "AWS_SECRET_ACCESS_KEY", password: aws.secret_key], + ]) + } + + withEnv([ + "TEST_TAGS=${testTags}", + ]) { + withEnvMask(vars: maskedVars) { + body() + } + } +} + +def terraformInit(String directory) { + dir(directory) { + sh(label: "Terraform Init on ${directory}", script: "terraform init") + } +} + +def terraformApply(String directory) { + terraformInit(directory) + dir(directory) { + sh(label: "Terraform Apply on ${directory}", script: "terraform apply -auto-approve") + } +} + +// Start testing environment on cloud using terraform. Terraform files are +// stashed so they can be used by other stages. They are also archived in +// case manual cleanup is needed. +// +// Example: +// startCloudTestEnv('x-pack-metricbeat', [ +// [cond: params.awsCloudTests, dir: 'x-pack/metricbeat/module/aws'], +// ]) +// ... +// terraformCleanup('x-pack-metricbeat', 'x-pack/metricbeat') +def startCloudTestEnv(String name, environments = []) { + withCloudTestEnv() { + withBeatsEnv(false) { + def runAll = params.runAllCloudTests + try { + for (environment in environments) { + if (environment.cond || runAll) { + retry(2) { + terraformApply(environment.dir) + } + } + } + } finally { + // Archive terraform states in case manual cleanup is needed. + archiveArtifacts(allowEmptyArchive: true, artifacts: '**/terraform.tfstate') + } + stash(name: "terraform-${name}", allowEmpty: true, includes: '**/terraform.tfstate,**/.terraform/**') + } + } +} + + +// Looks for all terraform states in directory and runs terraform destroy for them, +// it uses terraform states previously stashed by startCloudTestEnv. +def terraformCleanup(String stashName, String directory) { + stage("Remove cloud scenarios in ${directory}"){ + withCloudTestEnv() { + withBeatsEnv(false) { + unstash "terraform-${stashName}" + retry(2) { + sh(label: "Terraform Cleanup", script: ".ci/scripts/terraform-cleanup.sh ${directory}") + } + } + } + } +} + def loadConfigEnvVars(){ def empty = [] env.GO_VERSION = readFile(".go-version").trim() + withEnv(["HOME=${env.WORKSPACE}"]) { + retry(2) { sh(label: "Install Go ${env.GO_VERSION}", script: ".ci/scripts/install-go.sh") } + } + // Libbeat is the core framework of Beats. It has no additional dependencies // on other projects in the Beats repository. env.BUILD_LIBBEAT = isChangedOSSCode(empty) @@ -927,17 +1096,25 @@ def loadConfigEnvVars(){ // involved. env.BUILD_KUBERNETES = isChanged(["^deploy/kubernetes/.*"]) - env.BUILD_GENERATOR = isChangedOSSCode(getVendorPatterns('generator')) + def generatorPatterns = ['^generator/.*'] + generatorPatterns.addAll(getVendorPatterns('generator/common/beatgen')) + generatorPatterns.addAll(getVendorPatterns('metricbeat/beater')) + env.BUILD_GENERATOR = isChangedOSSCode(generatorPatterns) } /** This method grab the dependencies of a Go module and transform them on regexp */ def getVendorPatterns(beatName){ + def os = goos() + def goRoot = "${env.WORKSPACE}/.gvm/versions/go${GO_VERSION}.${os}.amd64" def output = "" - docker.image("golang:${GO_VERSION}").inside{ + + withEnv([ + "HOME=${env.WORKSPACE}/${env.BASE_DIR}", + "PATH=${env.WORKSPACE}/bin:${goRoot}/bin:${env.PATH}", + ]) { output = sh(label: 'Get vendor dependency patterns', returnStdout: true, script: """ - export HOME=${WORKSPACE}/${BASE_DIR} go list -mod=vendor -f '{{ .ImportPath }}{{ "\\n" }}{{ join .Deps "\\n" }}' ./${beatName}\ |awk '{print \$1"/.*"}'\ |sed -e "s#github.com/elastic/beats/v7/##g" @@ -954,3 +1131,39 @@ def setGitConfig(){ fi ''') } + +def isDockerInstalled(){ + return sh(label: 'check for Docker', script: 'command -v docker', returnStatus: true) +} + +def junitAndStore(Map params = [:]){ + junit(params) + // STAGE_NAME env variable could be null in some cases, so let's use the currentmilliseconds + def stageName = env.STAGE_NAME ? env.STAGE_NAME.replaceAll("[\\W]|_",'-') : "uncategorized-${new java.util.Date().getTime()}" + stash(includes: params.testResults, allowEmpty: true, name: stageName, useDefaultExcludes: true) + stashedTestReports[stageName] = stageName +} + +def runbld() { + catchError(buildResult: 'SUCCESS', message: 'runbld post build action failed.') { + if (stashedTestReports) { + dir("${env.BASE_DIR}") { + sh(label: 'Prepare workspace context', + script: 'find . -type f -name "TEST*.xml" -path "*/build/*" -delete') + // Unstash the test reports + stashedTestReports.each { k, v -> + dir(k) { + unstash v + } + } + sh(label: 'Process JUnit reports with runbld', + script: '''\ + cat >./runbld-script <:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -526,7 +527,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -640,7 +662,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -794,6 +816,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -816,7 +841,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -934,7 +959,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -968,7 +993,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -981,7 +1006,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Auditbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -1007,11 +1032,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -1055,8 +1082,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1110,7 +1136,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1124,13 +1150,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'auditbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "auditbeat" +#setup.ilm.rollover_alias: 'auditbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'auditbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1145,7 +1171,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1200,9 +1226,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1269,8 +1294,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Auditbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1380,6 +1404,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1391,7 +1436,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1415,12 +1461,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/auditbeat/auditbeat.yml b/auditbeat/auditbeat.yml index 0aa50de30ce..79cca537a8a 100644 --- a/auditbeat/auditbeat.yml +++ b/auditbeat/auditbeat.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/auditbeat/index.html -#========================== Modules configuration ============================= +# =========================== Modules configuration ============================ auditbeat.modules: - module: auditd @@ -48,13 +48,14 @@ auditbeat.modules: - /etc -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false -#================================ General ===================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -69,8 +70,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -82,7 +82,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -99,7 +99,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Auditbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -112,11 +112,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -129,7 +129,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -144,7 +144,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -153,7 +153,8 @@ processors: - add_cloud_metadata: ~ - add_docker_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -164,8 +165,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# auditbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Auditbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -186,7 +187,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/auditbeat/docs/fields.asciidoc b/auditbeat/docs/fields.asciidoc index e5144ce4b0c..3d9af508052 100644 --- a/auditbeat/docs/fields.asciidoc +++ b/auditbeat/docs/fields.asciidoc @@ -2458,7 +2458,8 @@ Contains common beat fields available in all event types. *`agent.hostname`*:: + -- -Hostname of the agent. +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. + type: keyword diff --git a/auditbeat/include/fields.go b/auditbeat/include/fields.go index 67239ca6469..841ebeae62b 100644 --- a/auditbeat/include/fields.go +++ b/auditbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/auditbeat/scripts/mage/config.go b/auditbeat/scripts/mage/config.go index 91f70446123..d6a1f6b1424 100644 --- a/auditbeat/scripts/mage/config.go +++ b/auditbeat/scripts/mage/config.go @@ -61,27 +61,15 @@ func configFileParams(dirs ...string) (devtools.ConfigFileParams, error) { if len(configFiles) == 0 { return devtools.ConfigFileParams{}, errors.Errorf("no config files found in %v", globs) } + devtools.MustFileConcat("build/config.modules.yml.tmpl", 0644, configFiles...) - return devtools.ConfigFileParams{ - ShortParts: join( - devtools.OSSBeatDir("_meta/common.p1.yml"), - configFiles, - devtools.OSSBeatDir("_meta/common.p2.yml"), - devtools.LibbeatDir("_meta/config.yml.tmpl"), - ), - ReferenceParts: join( - devtools.OSSBeatDir("_meta/common.reference.yml"), - configFiles, - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - ), - DockerParts: []string{ - devtools.OSSBeatDir("_meta/beat.docker.yml"), - devtools.LibbeatDir("_meta/config.docker.yml"), - }, - ExtraVars: map[string]interface{}{ - "ArchBits": archBits, - }, - }, nil + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, devtools.OSSBeatDir("_meta/config/*.tmpl")) + p.Templates = append(p.Templates, "build/config.modules.yml.tmpl") + p.ExtraVars = map[string]interface{}{ + "ArchBits": archBits, + } + return p, nil } // archBits returns the number of bit width of the GOARCH architecture value. @@ -95,16 +83,3 @@ func archBits(goarch string) int { return 64 } } - -func join(items ...interface{}) []string { - var out []string - for _, item := range items { - switch v := item.(type) { - case string: - out = append(out, v) - case []string: - out = append(out, v...) - } - } - return out -} diff --git a/deploy/kubernetes/metricbeat-kubernetes.yaml b/deploy/kubernetes/metricbeat-kubernetes.yaml index f5550f6ecbe..179f33089e1 100644 --- a/deploy/kubernetes/metricbeat-kubernetes.yaml +++ b/deploy/kubernetes/metricbeat-kubernetes.yaml @@ -79,10 +79,11 @@ data: hosts: ["https://${NODE_NAME}:10250"] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token ssl.verification_mode: "none" - # If using Red Hat OpenShift remove ssl.verification_mode entry and - # uncomment these settings: + # If there is a CA bundle that contains the issuer of the certificate used in the Kubelet API, + # remove ssl.verification_mode entry and use the CA, for instance: #ssl.certificate_authorities: #- /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt + # Currently `proxy` metricset is not supported on Openshift, comment out section - module: kubernetes metricsets: - proxy diff --git a/deploy/kubernetes/metricbeat/metricbeat-daemonset-configmap.yaml b/deploy/kubernetes/metricbeat/metricbeat-daemonset-configmap.yaml index 8760c3eaa0a..a244dda551a 100644 --- a/deploy/kubernetes/metricbeat/metricbeat-daemonset-configmap.yaml +++ b/deploy/kubernetes/metricbeat/metricbeat-daemonset-configmap.yaml @@ -79,10 +79,11 @@ data: hosts: ["https://${NODE_NAME}:10250"] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token ssl.verification_mode: "none" - # If using Red Hat OpenShift remove ssl.verification_mode entry and - # uncomment these settings: + # If there is a CA bundle that contains the issuer of the certificate used in the Kubelet API, + # remove ssl.verification_mode entry and use the CA, for instance: #ssl.certificate_authorities: #- /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt + # Currently `proxy` metricset is not supported on Openshift, comment out section - module: kubernetes metricsets: - proxy diff --git a/dev-tools/mage/common.go b/dev-tools/mage/common.go index 7d31038d06d..91d291f5387 100644 --- a/dev-tools/mage/common.go +++ b/dev-tools/mage/common.go @@ -115,7 +115,7 @@ func joinMaps(args ...map[string]interface{}) map[string]interface{} { return args[0] } - var out map[string]interface{} + out := map[string]interface{}{} for _, m := range args { for k, v := range m { out[k] = v @@ -220,7 +220,19 @@ func dockerInfo() (*DockerInfo, error) { // PATH. func HaveDockerCompose() error { _, err := exec.LookPath("docker-compose") - return errors.Wrap(err, "docker-compose was not found on the PATH") + if err != nil { + return fmt.Errorf("docker-compose is not available") + } + return nil +} + +// HaveKubectl returns an error if kind is not found on the PATH. +func HaveKubectl() error { + _, err := exec.LookPath("kubectl") + if err != nil { + return fmt.Errorf("kubectl is not available") + } + return nil } // FindReplace reads a file, performs a find/replace operation, then writes the @@ -821,3 +833,28 @@ func ListMatchingEnvVars(prefixes ...string) []string { } return vars } + +// IntegrationTestEnvVars returns the names of environment variables needed to configure +// connections to integration test environments. +func IntegrationTestEnvVars() []string { + // Environment variables that can be configured with paths to files + // with authentication information. + vars := []string{ + "AWS_SHARED_CREDENTIAL_FILE", + "AZURE_AUTH_LOCATION", + "GOOGLE_APPLICATION_CREDENTIALS", + } + // Environment variables with authentication information. + prefixes := []string{ + "AWS_", + "AZURE_", + + // Accepted by terraform, but not by many clients, including Beats + "GOOGLE_", + "GCLOUD_", + } + for _, prefix := range prefixes { + vars = append(vars, ListMatchingEnvVars(prefix)...) + } + return vars +} diff --git a/dev-tools/mage/config.go b/dev-tools/mage/config.go index 668c71e1074..677b307926f 100644 --- a/dev-tools/mage/config.go +++ b/dev-tools/mage/config.go @@ -20,12 +20,11 @@ package mage import ( "fmt" "io/ioutil" - "log" "os" "path/filepath" - "regexp" "sort" "strings" + "text/template" "github.com/magefile/mage/mg" @@ -64,44 +63,87 @@ func (t ConfigFileType) IsDocker() bool { return t&DockerConfigType > 0 } // ConfigFileParams defines the files that make up each config file. type ConfigFileParams struct { - ShortParts []string // List of files or globs. - ShortDeps []interface{} - ReferenceParts []string // List of files or globs. - ReferenceDeps []interface{} - DockerParts []string // List of files or globs. - DockerDeps []interface{} - ExtraVars map[string]interface{} + Templates []string // List of files or globs to load. + ExtraVars map[string]interface{} + Short, Reference, Docker ConfigParams } -// Empty checks if configuration files are set. -func (c ConfigFileParams) Empty() bool { - return len(c.ShortParts) == len(c.ReferenceDeps) && len(c.ReferenceParts) == len(c.DockerParts) && len(c.DockerParts) == 0 +type ConfigParams struct { + Template string + Deps []interface{} +} + +func DefaultConfigFileParams() ConfigFileParams { + return ConfigFileParams{ + Templates: []string{LibbeatDir("_meta/config/*.tmpl")}, + ExtraVars: map[string]interface{}{}, + Short: ConfigParams{ + Template: LibbeatDir("_meta/config/default.short.yml.tmpl"), + }, + Reference: ConfigParams{ + Template: LibbeatDir("_meta/config/default.reference.yml.tmpl"), + }, + Docker: ConfigParams{ + Template: LibbeatDir("_meta/config/default.docker.yml.tmpl"), + }, + } } // Config generates config files. Set DEV_OS and DEV_ARCH to change the target // host for the generated configs. Defaults to linux/amd64. func Config(types ConfigFileType, args ConfigFileParams, targetDir string) error { - if args.Empty() { - args = ConfigFileParams{ - ShortParts: []string{ - OSSBeatDir("_meta/beat.yml"), - LibbeatDir("_meta/config.yml.tmpl"), - }, - ReferenceParts: []string{ - OSSBeatDir("_meta/beat.reference.yml"), - LibbeatDir("_meta/config.reference.yml.tmpl"), - }, - DockerParts: []string{ - OSSBeatDir("_meta/beat.docker.yml"), - LibbeatDir("_meta/config.docker.yml"), - }, + // Short + if types.IsShort() { + file := filepath.Join(targetDir, BeatName+".yml") + if err := makeConfigTemplate(file, 0600, args, ShortConfigType); err != nil { + return errors.Wrap(err, "failed making short config") } } - if err := makeConfigTemplates(types, args); err != nil { - return errors.Wrap(err, "failed making config templates") + // Reference + if types.IsReference() { + file := filepath.Join(targetDir, BeatName+".reference.yml") + if err := makeConfigTemplate(file, 0644, args, ReferenceConfigType); err != nil { + return errors.Wrap(err, "failed making reference config") + } + } + + // Docker + if types.IsDocker() { + file := filepath.Join(targetDir, BeatName+".docker.yml") + if err := makeConfigTemplate(file, 0600, args, DockerConfigType); err != nil { + return errors.Wrap(err, "failed making docker config") + } } + return nil +} + +func makeConfigTemplate(destination string, mode os.FileMode, confParams ConfigFileParams, typ ConfigFileType) error { + // Determine what type to build and set some parameters. + var confFile ConfigParams + var tmplParams map[string]interface{} + switch typ { + case ShortConfigType: + confFile = confParams.Short + tmplParams = map[string]interface{}{} + case ReferenceConfigType: + confFile = confParams.Reference + tmplParams = map[string]interface{}{"Reference": true} + case DockerConfigType: + confFile = confParams.Docker + tmplParams = map[string]interface{}{"Docker": true} + default: + panic(errors.Errorf("Invalid config file type: %v", typ)) + } + + // Build the dependencies. + mg.SerialDeps(confFile.Deps...) + + // Set variables that are available in templates. + // Rather than adding more "ExcludeX"/"UseX" options consider overwriting + // one of the libbeat templates in your project by adding a file with the + // same name to your _meta/config directory. params := map[string]interface{}{ "GOOS": EnvOr("DEV_OS", "linux"), "GOARCH": EnvOr("DEV_ARCH", "amd64"), @@ -117,88 +159,57 @@ func Config(types ConfigFileType, args ConfigFileParams, targetDir string) error "UseKubernetesMetadataProcessor": false, "ExcludeDashboards": false, } - for k, v := range args.ExtraVars { - params[k] = v - } - - // Short - if types.IsShort() { - file := filepath.Join(targetDir, BeatName+".yml") - fmt.Printf(">> Building %v for %v/%v\n", file, params["GOOS"], params["GOARCH"]) - if err := ExpandFile(shortTemplate, file, params); err != nil { - return errors.Wrapf(err, "failed building %v", file) - } - } + params = joinMaps(params, confParams.ExtraVars, tmplParams) + funcs := joinMaps(FuncMap, template.FuncMap{ + "header": header, + "subheader": subheader, + }) - // Reference - if types.IsReference() { - file := filepath.Join(targetDir, BeatName+".reference.yml") - params["Reference"] = true - fmt.Printf(">> Building %v for %v/%v\n", file, params["GOOS"], params["GOARCH"]) - if err := ExpandFile(referenceTemplate, file, params); err != nil { - return errors.Wrapf(err, "failed building %v", file) + fmt.Printf(">> Building %v for %v/%v\n", destination, params["GOOS"], params["GOARCH"]) + var err error + tmpl := template.New("config").Option("missingkey=error").Funcs(funcs) + for _, templateGlob := range confParams.Templates { + if tmpl, err = tmpl.ParseGlob(templateGlob); err != nil { + return errors.Wrapf(err, "failed to parse config templates in %q", templateGlob) } } - // Docker - if types.IsDocker() { - file := filepath.Join(targetDir, BeatName+".docker.yml") - params["Reference"] = false - params["Docker"] = true - fmt.Printf(">> Building %v for %v/%v\n", file, params["GOOS"], params["GOARCH"]) - if err := ExpandFile(dockerTemplate, file, params); err != nil { - return errors.Wrapf(err, "failed building %v", file) - } + data, err := ioutil.ReadFile(confFile.Template) + if err != nil { + return errors.Wrapf(err, "failed to read config template %q", confFile.Template) } - return nil -} - -func makeConfigTemplates(types ConfigFileType, args ConfigFileParams) error { - var err error - - if types.IsShort() { - mg.SerialDeps(args.ShortDeps...) - if err = makeConfigTemplate(shortTemplate, 0600, args.ShortParts...); err != nil { - return err - } + tmpl, err = tmpl.Parse(string(data)) + if err != nil { + return errors.Wrap(err, "failed to parse template") } - if types.IsReference() { - mg.SerialDeps(args.ReferenceDeps...) - if err = makeConfigTemplate(referenceTemplate, 0644, args.ReferenceParts...); err != nil { - return err - } + out, err := os.OpenFile(CreateDir(destination), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mode) + if err != nil { + return err } + defer out.Close() - if types.IsDocker() { - mg.SerialDeps(args.DockerDeps...) - if err = makeConfigTemplate(dockerTemplate, 0600, args.DockerParts...); err != nil { - return err - } + if err = tmpl.Execute(out, EnvMap(params)); err != nil { + return errors.Wrapf(err, "failed building %v", destination) } return nil } -func makeConfigTemplate(destination string, mode os.FileMode, parts ...string) error { - configFiles, err := FindFiles(parts...) - if err != nil { - return errors.Wrap(err, "failed to find config templates") - } +func header(title string) string { + return makeHeading(title, "=") +} - if IsUpToDate(destination, configFiles...) { - return nil - } +func subheader(title string) string { + return makeHeading(title, "-") +} - log.Println(">> Building", destination) - if err = FileConcat(destination, mode, configFiles...); err != nil { - return err - } - if err = FindReplace(destination, regexp.MustCompile("beatname"), "{{.BeatName}}"); err != nil { - return err - } - return FindReplace(destination, regexp.MustCompile("beat-index-prefix"), "{{.BeatIndexPrefix}}") +func makeHeading(title, separator string) string { + const line = 80 + leftEquals := (line - len("# ") - len(title) - 2*len(" ")) / 2 + rightEquals := leftEquals + len(title)%2 + return "# " + strings.Repeat(separator, leftEquals) + " " + title + " " + strings.Repeat(separator, rightEquals) } const moduleConfigTemplate = ` diff --git a/dev-tools/mage/crossbuild.go b/dev-tools/mage/crossbuild.go index 539e4fba03c..d80276a7974 100644 --- a/dev-tools/mage/crossbuild.go +++ b/dev-tools/mage/crossbuild.go @@ -297,7 +297,9 @@ func DockerChown(path string) { func chownPaths(uid, gid int, path string) error { start := time.Now() numFixed := 0 - defer log.Printf("chown took: %v, changed %d files", time.Now().Sub(start), numFixed) + defer func() { + log.Printf("chown took: %v, changed %d files", time.Now().Sub(start), numFixed) + }() return filepath.Walk(path, func(name string, info os.FileInfo, err error) error { if err != nil { diff --git a/dev-tools/mage/gotest.go b/dev-tools/mage/gotest.go index 9f0050025f9..c813693971e 100644 --- a/dev-tools/mage/gotest.go +++ b/dev-tools/mage/gotest.go @@ -26,6 +26,7 @@ import ( "log" "os" "os/exec" + "path" "path/filepath" "runtime" "sort" @@ -137,33 +138,49 @@ func DefaultTestBinaryArgs() TestBinaryArgs { // Use RACE_DETECTOR=true to enable the race detector. // Use MODULE=module to run only tests for `module`. func GoTestIntegrationForModule(ctx context.Context) error { - return RunIntegTest("goIntegTest", func() error { - module := EnvOr("MODULE", "") - if module != "" { - err := GoTest(ctx, GoTestIntegrationArgsForModule(module)) - return errors.Wrapf(err, "integration tests failed for module %s", module) + module := EnvOr("MODULE", "") + modulesFileInfo, err := ioutil.ReadDir("./module") + if err != nil { + return err + } + + foundModule := false + failedModules := []string{} + for _, fi := range modulesFileInfo { + if !fi.IsDir() { + continue } + if module != "" && module != fi.Name() { + continue + } + foundModule = true - modulesFileInfo, err := ioutil.ReadDir("./module") + // Set MODULE because only want that modules tests to run inside the testing environment. + env := map[string]string{"MODULE": fi.Name()} + passThroughEnvs(env, IntegrationTestEnvVars()...) + runners, err := NewIntegrationRunners(path.Join("./module", fi.Name()), env) if err != nil { - return err + return errors.Wrapf(err, "test setup failed for module %s", fi.Name()) } - - var failed bool - for _, fi := range modulesFileInfo { - if !fi.IsDir() { - continue - } + err = runners.Test("goIntegTest", func() error { err := GoTest(ctx, GoTestIntegrationArgsForModule(fi.Name())) if err != nil { - failed = true + return err } + return nil + }) + if err != nil { + // err will already be report to stdout, collect failed module to report at end + failedModules = append(failedModules, fi.Name()) } - if failed { - return errors.New("integration tests failed") - } - return nil - }) + } + if module != "" && !foundModule { + return fmt.Errorf("no module %s", module) + } + if len(failedModules) > 0 { + return fmt.Errorf("failed modules: %s", strings.Join(failedModules, ", ")) + } + return nil } // GoTest invokes "go test" and reports the results to stdout. It returns an @@ -439,5 +456,10 @@ func BuildSystemTestGoBinary(binArgs TestBinaryArgs) error { if len(binArgs.InputFiles) > 0 { args = append(args, binArgs.InputFiles...) } + + start := time.Now() + defer func() { + log.Printf("BuildSystemTestGoBinary (go %v) took %v.", strings.Join(args, " "), time.Since(start)) + }() return sh.RunV("go", args...) } diff --git a/dev-tools/mage/integtest.go b/dev-tools/mage/integtest.go index 6fc26915415..064dc7d8102 100644 --- a/dev-tools/mage/integtest.go +++ b/dev-tools/mage/integtest.go @@ -19,334 +19,334 @@ package mage import ( "fmt" - "io/ioutil" - "log" "os" "path/filepath" - "runtime" "strconv" - "strings" - "sync" - - "github.com/pkg/errors" + "github.com/joeshaw/multierror" "github.com/magefile/mage/mg" - "github.com/magefile/mage/sh" + "github.com/pkg/errors" ) const ( - // BEATS_DOCKER_INTEGRATION_TEST_ENV is used to indicate that we are inside - // of the Docker integration test environment (e.g. in a container). - beatsDockerIntegrationTestEnvVar = "BEATS_DOCKER_INTEGRATION_TEST_ENV" + // BEATS_INSIDE_INTEGRATION_TEST_ENV is used to indicate that we are inside + // of the integration test environment. + insideIntegrationTestEnvVar = "BEATS_INSIDE_INTEGRATION_TEST_ENV" ) var ( - integTestUseCount int32 // Reference count for the integ test env. - integTestUseCountLock sync.Mutex // Lock to guard integTestUseCount. - - integTestLock sync.Mutex // Only allow one integration test at a time. + globalIntegrationTesters map[string]IntegrationTester + globalIntegrationTestSetupSteps IntegrationTestSteps - integTestBuildImagesOnce sync.Once // Build images one time for all integ testing. -) - -// Integration Test Configuration -var ( - // StackEnvironment specifies what testing environment - // to use (like snapshot (default), latest, 5x). Formerly known as - // TESTING_ENVIRONMENT. - StackEnvironment = EnvOr("STACK_ENVIRONMENT", "snapshot") + defaultPassthroughEnvVars = []string{ + "TEST_COVERAGE", + "RACE_DETECTOR", + "TEST_TAGS", + "PYTHON_EXE", + "MODULE", + "KUBECONFIG", + "KUBE_CONFIG", + } ) -// AddIntegTestUsage increments the use count for the integration test -// environment and prevents it from being stopped until the last call to -// StopIntegTestEnv(). You should also pair this with -// 'defer StopIntegTestEnv()'. -// -// This allows for the same environment to be reused by multiple tests (like -// both Go and Python) without tearing it down in between runs. -func AddIntegTestUsage() { - if IsInIntegTestEnv() { - return +// RegisterIntegrationTester registers a integration tester. +func RegisterIntegrationTester(tester IntegrationTester) { + if globalIntegrationTesters == nil { + globalIntegrationTesters = make(map[string]IntegrationTester) } - - integTestUseCountLock.Lock() - defer integTestUseCountLock.Unlock() - integTestUseCount++ + globalIntegrationTesters[tester.Name()] = tester } -// StopIntegTestEnv will stop and removing the integration test environment -// (e.g. docker-compose rm --stop --force) when there are no more users -// of the environment. -func StopIntegTestEnv() error { - if IsInIntegTestEnv() { - return nil - } - - integTestUseCountLock.Lock() - defer integTestUseCountLock.Unlock() - if integTestUseCount == 0 { - panic("integTestUseCount is 0. Did you call AddIntegTestUsage()?") - } +// RegisterIntegrationTestSetupStep registers a integration step. +func RegisterIntegrationTestSetupStep(step IntegrationTestSetupStep) { + globalIntegrationTestSetupSteps = append(globalIntegrationTestSetupSteps, step) +} - integTestUseCount-- - if integTestUseCount > 0 { - return nil - } +// IntegrationTestSetupStep is interface used by a step in the integration setup +// chain. Example could be: Terraform -> Kind -> Kubernetes (IntegrationTester). +type IntegrationTestSetupStep interface { + // Name is the name of the step. + Name() string + // Use returns true in the case that the step should be used. Not called + // when a step is defined as a dependency of a tester. + Use(dir string) (bool, error) + // Setup sets up the environment for the integration test. + Setup(env map[string]string) error + // Teardown brings down the environment for the integration test. + Teardown(env map[string]string) error +} - if err := haveIntegTestEnvRequirements(); err != nil { - // Ignore error because it will be logged by RunIntegTest. - return nil - } +// IntegrationTestSteps wraps all the steps and completes the in the order added. +type IntegrationTestSteps []IntegrationTestSetupStep - if _, skip := skipIntegTest(); skip { - return nil - } +// Name is the name of the step. +func (steps IntegrationTestSteps) Name() string { + return "IntegrationTestSteps" +} - composeEnv, err := integTestDockerComposeEnvVars() - if err != nil { - return err +// Setup calls Setup on each step in the order defined. +// +// In the case that Setup fails on a step, Teardown will be called on the previous +// successful steps. +func (steps IntegrationTestSteps) Setup(env map[string]string) error { + for i, step := range steps { + if mg.Verbose() { + fmt.Printf("Setup %s...\n", step.Name()) + } + if err := step.Setup(env); err != nil { + prev := i - 1 + if prev >= 0 { + // errors ignored + _ = steps.teardownFrom(prev, env) + } + return errors.Wrapf(err, "%s setup failed", step.Name()) + } } + return nil +} - // Stop docker-compose when reference count hits 0. - fmt.Println(">> Stopping Docker test environment...") +// Teardown calls Teardown in the reverse order defined. +// +// In the case a teardown step fails the error is recorded but the +// previous steps teardown is still called. This guarantees that teardown +// will always be called for each step. +func (steps IntegrationTestSteps) Teardown(env map[string]string) error { + return steps.teardownFrom(len(steps)-1, env) +} - // Docker-compose rm is noisy. So only pass through stderr when in verbose. - out := ioutil.Discard - if mg.Verbose() { - out = os.Stderr +func (steps IntegrationTestSteps) teardownFrom(start int, env map[string]string) error { + var errs multierror.Errors + for i := start; i >= 0; i-- { + if mg.Verbose() { + fmt.Printf("Teardown %s...\n", steps[i].Name()) + } + if err := steps[i].Teardown(env); err != nil { + errs = append(errs, errors.Wrapf(err, "%s teardown failed", steps[i].Name())) + } } + return errs.Err() +} - _, err = sh.Exec( - composeEnv, - ioutil.Discard, - out, - "docker-compose", - "-p", dockerComposeProjectName(), - "rm", "--stop", "--force", - ) - return err +// IntegrationTester is interface used by the actual test runner. +type IntegrationTester interface { + // Name returns the name of the tester. + Name() string + // Use returns true in the case that the tester should be used. + Use(dir string) (bool, error) + // HasRequirements returns an error if requirements are missing. + HasRequirements() error + // Test performs excecuting the test inside the environment. + Test(dir string, mageTarget string, env map[string]string) error + // InsideTest performs the actual test on the inside of environment. + InsideTest(test func() error) error + // StepRequirements returns the steps this tester requires. These + // are always placed before other autodiscover steps. + StepRequirements() IntegrationTestSteps } -// RunIntegTest executes the given target inside the integration testing -// environment (Docker). -// Use TEST_COVERAGE=true to enable code coverage profiling. -// Use RACE_DETECTOR=true to enable the race detector. -// Use STACK_ENVIRONMENT=env to specify what testing environment -// to use (like snapshot (default), latest, 5x). -// -// Always use this with AddIntegTestUsage() and defer StopIntegTestEnv(). -func RunIntegTest(mageTarget string, test func() error, passThroughEnvVars ...string) error { - if reason, skip := skipIntegTest(); skip { - fmt.Printf(">> %v: Skipping because %v\n", mageTarget, reason) - return nil - } +// IntegrationRunner performs the running of the integration tests. +type IntegrationRunner struct { + steps IntegrationTestSteps + tester IntegrationTester + dir string + env map[string]string +} - AddIntegTestUsage() - defer StopIntegTestEnv() +// IntegrationRunners is an array of multiple runners. +type IntegrationRunners []*IntegrationRunner - env := []string{ - "TEST_COVERAGE", - "RACE_DETECTOR", - "TEST_TAGS", - "PYTHON_EXE", - "MODULE", +// NewIntegrationRunners returns the integration test runners discovered from the provided path. +func NewIntegrationRunners(path string, passInEnv map[string]string) (IntegrationRunners, error) { + cwd, err := os.Getwd() + if err != nil { + return nil, err } - env = append(env, passThroughEnvVars...) - return runInIntegTestEnv(mageTarget, test, env...) -} - -func runInIntegTestEnv(mageTarget string, test func() error, passThroughEnvVars ...string) error { - if IsInIntegTestEnv() { - // Fix file permissions after test is done writing files as root. - if runtime.GOOS != "windows" { - defer DockerChown(".") + dir := filepath.Join(cwd, path) + + // Load the overall steps to use (skipped inside of test environment, as they are never ran on the inside). + // These steps are duplicated per scenario. + var steps IntegrationTestSteps + if !IsInIntegTestEnv() { + for _, step := range globalIntegrationTestSetupSteps { + use, err := step.Use(dir) + if err != nil { + return nil, errors.Wrapf(err, "%s step failed on Use", step.Name()) + } + if use { + steps = append(steps, step) + } } - return test() } - var err error - integTestBuildImagesOnce.Do(func() { err = dockerComposeBuildImages() }) - if err != nil { - return err + // Create the runners (can only be multiple). + var runners IntegrationRunners + for _, t := range globalIntegrationTesters { + use, err := t.Use(dir) + if err != nil { + return nil, errors.Wrapf(err, "%s tester failed on Use", t.Name()) + } + if !use { + continue + } + runner, err := initRunner(t, dir, passInEnv) + if err != nil { + return nil, errors.Wrapf(err, "initializing %s runner", t.Name()) + } + runners = append(runners, runner) } - - // Test that we actually have Docker and docker-compose. - if err := haveIntegTestEnvRequirements(); err != nil { - return errors.Wrapf(err, "failed to run %v target in integration environment", mageTarget) + // Keep support for modules that don't have a local environment defined at the module + // level (system, stack and cloud modules by now) + if len(runners) == 0 { + if mg.Verbose() { + fmt.Printf(">> No runner found in %s, using docker\n", path) + } + tester, ok := globalIntegrationTesters["docker"] + if !ok { + return nil, fmt.Errorf("docker integration test runner not registered") + } + runner, err := initRunner(tester, dir, passInEnv) + if err != nil { + return nil, errors.Wrapf(err, "initializing docker runner") + } + runners = append(runners, runner) } + return runners, nil +} - // Pre-build a mage binary to execute inside docker so that we don't need to - // have mage installed inside the container. - mg.Deps(buildMage) - - // Determine the path to use inside the container. - repo, err := GetProjectRepoInfo() +// NewDockerIntegrationRunner returns an intergration runner configured only for docker. +func NewDockerIntegrationRunner(passThroughEnvVars ...string) (*IntegrationRunner, error) { + cwd, err := os.Getwd() if err != nil { - return err + return nil, err } - magePath := filepath.Join("/go/src", repo.CanonicalRootImportPath, repo.SubDir, "build/mage-linux-amd64") - - // Build docker-compose args. - args := []string{"-p", dockerComposeProjectName(), "run", - "-e", "DOCKER_COMPOSE_PROJECT_NAME=" + dockerComposeProjectName(), - // Disable strict.perms because we moust host dirs inside containers - // and the UID/GID won't meet the strict requirements. - "-e", "BEAT_STRICT_PERMS=false", - // compose.EnsureUp needs to know the environment type. - "-e", "STACK_ENVIRONMENT=" + StackEnvironment, - "-e", "TESTING_ENVIRONMENT=" + StackEnvironment, + tester, ok := globalIntegrationTesters["docker"] + if !ok { + return nil, fmt.Errorf("docker integration test runner not registered") } - if UseVendor { - args = append(args, "-e", "GOFLAGS=-mod=vendor") + return initRunner(tester, cwd, nil, passThroughEnvVars...) +} + +func initRunner(tester IntegrationTester, dir string, passInEnv map[string]string, passThroughEnvVars ...string) (*IntegrationRunner, error) { + var runnerSteps IntegrationTestSteps + requirements := tester.StepRequirements() + if requirements != nil { + runnerSteps = append(runnerSteps, requirements...) } - args, err = addUidGidEnvArgs(args) - if err != nil { - return err + + // Create the custom env for the runner. + env := map[string]string{ + insideIntegrationTestEnvVar: "true", } - for _, envVar := range passThroughEnvVars { - args = append(args, "-e", envVar+"="+os.Getenv(envVar)) + for name, value := range passInEnv { + env[name] = value } + passThroughEnvs(env, passThroughEnvVars...) + passThroughEnvs(env, defaultPassthroughEnvVars...) if mg.Verbose() { - args = append(args, "-e", "MAGEFILE_VERBOSE=1") + env["MAGEFILE_VERBOSE"] = "1" } - args = append(args, - "-e", beatsDockerIntegrationTestEnvVar+"=true", - "beat", // Docker compose container name. - magePath, - mageTarget, - ) - - composeEnv, err := integTestDockerComposeEnvVars() - if err != nil { - return err + if UseVendor { + env["GOFLAGS"] = "-mod=vendor" } - // Only allow one usage at a time. - integTestLock.Lock() - defer integTestLock.Unlock() - - _, err = sh.Exec( - composeEnv, - os.Stdout, - os.Stderr, - "docker-compose", - args..., - ) - return err -} - -// IsInIntegTestEnv return true if executing inside the integration test -// environment. -func IsInIntegTestEnv() bool { - _, found := os.LookupEnv(beatsDockerIntegrationTestEnvVar) - return found -} - -func haveIntegTestEnvRequirements() error { - if err := HaveDockerCompose(); err != nil { - return err + runner := &IntegrationRunner{ + steps: runnerSteps, + tester: tester, + dir: dir, + env: env, } - if err := HaveDocker(); err != nil { - return err - } - return nil + return runner, nil } -// skipIntegTest returns true if integ tests should be skipped. -func skipIntegTest() (reason string, skip bool) { +// Test actually performs the test. +func (r *IntegrationRunner) Test(mageTarget string, test func() error) (err error) { + // Inside the testing environment just run the test. if IsInIntegTestEnv() { - return "", false + err = r.tester.InsideTest(test) + return } // Honor the TEST_ENVIRONMENT value if set. if testEnvVar, isSet := os.LookupEnv("TEST_ENVIRONMENT"); isSet { - enabled, err := strconv.ParseBool(testEnvVar) + var enabled bool + enabled, err = strconv.ParseBool(testEnvVar) if err != nil { - panic(errors.Wrap(err, "failed to parse TEST_ENVIRONMENT value")) + err = errors.Wrap(err, "failed to parse TEST_ENVIRONMENT value") + return + } + if !enabled { + err = fmt.Errorf("TEST_ENVIRONMENT=%s", testEnvVar) + return } - return "TEST_ENVIRONMENT=" + testEnvVar, !enabled - } - - // Otherwise skip if we don't have all the right dependencies. - if err := haveIntegTestEnvRequirements(); err != nil { - // Skip if we don't meet the requirements. - log.Println("Skipping integ test because:", err) - return "docker is not available", true } - return "", false -} - -// integTestDockerComposeEnvVars returns the environment variables used for -// executing docker-compose (not the variables passed into the containers). -// docker-compose uses these when evaluating docker-compose.yml files. -func integTestDockerComposeEnvVars() (map[string]string, error) { - esBeatsDir, err := ElasticBeatsDir() + // log missing requirements and do nothing + err = r.tester.HasRequirements() if err != nil { - return nil, err + // log error; and return (otherwise on machines without requirements it will mark the tests as failed) + fmt.Printf("skipping test run with %s due to missing requirements: %s\n", r.tester.Name(), err) + err = nil + return } - return map[string]string{ - "ES_BEATS": esBeatsDir, - "STACK_ENVIRONMENT": StackEnvironment, - // Deprecated use STACK_ENVIRONMENT instead (it's more descriptive). - "TESTING_ENVIRONMENT": StackEnvironment, - }, nil -} - -// dockerComposeProjectName returns the project name to use with docker-compose. -// It is passed to docker-compose using the `-p` flag. And is passed to our -// Go and Python testing libraries through the DOCKER_COMPOSE_PROJECT_NAME -// environment variable. -func dockerComposeProjectName() string { - commit, err := CommitHash() - if err != nil { - panic(errors.Wrap(err, "failed to construct docker compose project name")) + if err = r.steps.Setup(r.env); err != nil { + return } - version, err := BeatQualifiedVersion() - if err != nil { - panic(errors.Wrap(err, "failed to construct docker compose project name")) + // catch any panics to run teardown + inTeardown := false + defer func() { + if recoverErr := recover(); recoverErr != nil { + err = recoverErr.(error) + if !inTeardown { + // ignore errors + _ = r.steps.Teardown(r.env) + } + } + }() + + if mg.Verbose() { + fmt.Printf(">> Running testing inside of %s...\n", r.tester.Name()) } - version = strings.NewReplacer(".", "_").Replace(version) - - projectName := "{{.BeatName}}_{{.Version}}_{{.ShortCommit}}-{{.StackEnvironment}}" - projectName = MustExpand(projectName, map[string]interface{}{ - "StackEnvironment": StackEnvironment, - "ShortCommit": commit[:10], - "Version": version, - }) - return projectName -} -// dockerComposeBuildImages builds all images in the docker-compose.yml file. -func dockerComposeBuildImages() error { - fmt.Println(">> Building docker images") + err = r.tester.Test(r.dir, mageTarget, r.env) - composeEnv, err := integTestDockerComposeEnvVars() - if err != nil { - return err + if mg.Verbose() { + fmt.Printf(">> Done running testing inside of %s...\n", r.tester.Name()) } - args := []string{"-p", dockerComposeProjectName(), "build", "--force-rm"} - if _, noCache := os.LookupEnv("DOCKER_NOCACHE"); noCache { - args = append(args, "--no-cache") + inTeardown = true + if teardownErr := r.steps.Teardown(r.env); teardownErr != nil { + if err == nil { + // test didn't error, but teardown did + err = teardownErr + } } + return +} - if _, forcePull := os.LookupEnv("DOCKER_PULL"); forcePull { - args = append(args, "--pull") +// Test runs the test on each runner and collects the errors. +func (r IntegrationRunners) Test(mageTarget string, test func() error) error { + var errs multierror.Errors + for _, runner := range r { + if err := runner.Test(mageTarget, test); err != nil { + errs = append(errs, err) + } } + return errs.Err() +} - out := ioutil.Discard - if mg.Verbose() { - out = os.Stderr +func passThroughEnvs(env map[string]string, passthrough ...string) { + for _, envName := range passthrough { + val, set := os.LookupEnv(envName) + if set { + env[envName] = val + } } +} - _, err = sh.Exec( - composeEnv, - out, - os.Stderr, - "docker-compose", args..., - ) - return err +// IsInIntegTestEnv return true if executing inside the integration test environment. +func IsInIntegTestEnv() bool { + _, found := os.LookupEnv(insideIntegrationTestEnvVar) + return found } diff --git a/dev-tools/mage/integtest_docker.go b/dev-tools/mage/integtest_docker.go new file mode 100644 index 00000000000..afc05a16dc1 --- /dev/null +++ b/dev-tools/mage/integtest_docker.go @@ -0,0 +1,236 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package mage + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + + "github.com/pkg/errors" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +var ( + // StackEnvironment specifies what testing environment + // to use (like snapshot (default), latest, 5x). Formerly known as + // TESTING_ENVIRONMENT. + StackEnvironment = EnvOr("STACK_ENVIRONMENT", "snapshot") +) + +func init() { + RegisterIntegrationTester(&DockerIntegrationTester{}) +} + +type DockerIntegrationTester struct { + buildImagesOnce sync.Once +} + +// Name returns docker name. +func (d *DockerIntegrationTester) Name() string { + return "docker" +} + +// Use determines if this tester should be used. +func (d *DockerIntegrationTester) Use(dir string) (bool, error) { + dockerFile := filepath.Join(dir, "docker-compose.yml") + if _, err := os.Stat(dockerFile); !os.IsNotExist(err) { + return true, nil + } + return false, nil +} + +// HasRequirements ensures that the required docker and docker-compose are installed. +func (d *DockerIntegrationTester) HasRequirements() error { + if err := HaveDocker(); err != nil { + return err + } + if err := HaveDockerCompose(); err != nil { + return err + } + return nil +} + +// StepRequirements returns the steps required for this tester. +func (d *DockerIntegrationTester) StepRequirements() IntegrationTestSteps { + return IntegrationTestSteps{&MageIntegrationTestStep{}} +} + +// Test performs the tests with docker-compose. +func (d *DockerIntegrationTester) Test(_ string, mageTarget string, env map[string]string) error { + var err error + d.buildImagesOnce.Do(func() { err = dockerComposeBuildImages() }) + if err != nil { + return err + } + + // Determine the path to use inside the container. + repo, err := GetProjectRepoInfo() + if err != nil { + return err + } + dockerRepoRoot := filepath.Join("/go/src", repo.CanonicalRootImportPath) + dockerGoCache := filepath.Join(dockerRepoRoot, "build/docker-gocache") + magePath := filepath.Join("/go/src", repo.CanonicalRootImportPath, repo.SubDir, "build/mage-linux-amd64") + + // Execute the inside of docker-compose. + args := []string{"-p", dockerComposeProjectName(), "run", + "-e", "DOCKER_COMPOSE_PROJECT_NAME=" + dockerComposeProjectName(), + // Disable strict.perms because we mount host dirs inside containers + // and the UID/GID won't meet the strict requirements. + "-e", "BEAT_STRICT_PERMS=false", + // compose.EnsureUp needs to know the environment type. + "-e", "STACK_ENVIRONMENT=" + StackEnvironment, + "-e", "TESTING_ENVIRONMENT=" + StackEnvironment, + "-e", "GOCACHE=" + dockerGoCache, + } + args, err = addUidGidEnvArgs(args) + if err != nil { + return err + } + for envVame, envVal := range env { + args = append(args, "-e", fmt.Sprintf("%s=%s", envVame, envVal)) + } + args = append(args, + "beat", // Docker compose container name. + magePath, + mageTarget, + ) + + composeEnv, err := integTestDockerComposeEnvVars() + if err != nil { + return err + } + + _, testErr := sh.Exec( + composeEnv, + os.Stdout, + os.Stderr, + "docker-compose", + args..., + ) + + // Docker-compose rm is noisy. So only pass through stderr when in verbose. + out := ioutil.Discard + if mg.Verbose() { + out = os.Stderr + } + + _, err = sh.Exec( + composeEnv, + ioutil.Discard, + out, + "docker-compose", + "-p", dockerComposeProjectName(), + "rm", "--stop", "--force", + ) + if err != nil && testErr == nil { + // docker-compose rm failed but the test didn't + return err + } + return testErr +} + +// InsideTest performs the tests inside of environment. +func (d *DockerIntegrationTester) InsideTest(test func() error) error { + // Fix file permissions after test is done writing files as root. + if runtime.GOOS != "windows" { + defer DockerChown(".") + } + return test() +} + +// integTestDockerComposeEnvVars returns the environment variables used for +// executing docker-compose (not the variables passed into the containers). +// docker-compose uses these when evaluating docker-compose.yml files. +func integTestDockerComposeEnvVars() (map[string]string, error) { + esBeatsDir, err := ElasticBeatsDir() + if err != nil { + return nil, err + } + + return map[string]string{ + "ES_BEATS": esBeatsDir, + "STACK_ENVIRONMENT": StackEnvironment, + // Deprecated use STACK_ENVIRONMENT instead (it's more descriptive). + "TESTING_ENVIRONMENT": StackEnvironment, + }, nil +} + +// dockerComposeProjectName returns the project name to use with docker-compose. +// It is passed to docker-compose using the `-p` flag. And is passed to our +// Go and Python testing libraries through the DOCKER_COMPOSE_PROJECT_NAME +// environment variable. +func dockerComposeProjectName() string { + commit, err := CommitHash() + if err != nil { + panic(errors.Wrap(err, "failed to construct docker compose project name")) + } + + version, err := BeatQualifiedVersion() + if err != nil { + panic(errors.Wrap(err, "failed to construct docker compose project name")) + } + version = strings.NewReplacer(".", "_").Replace(version) + + projectName := "{{.BeatName}}_{{.Version}}_{{.ShortCommit}}-{{.StackEnvironment}}" + projectName = MustExpand(projectName, map[string]interface{}{ + "StackEnvironment": StackEnvironment, + "ShortCommit": commit[:10], + "Version": version, + }) + return projectName +} + +// dockerComposeBuildImages builds all images in the docker-compose.yml file. +func dockerComposeBuildImages() error { + fmt.Println(">> Building docker images") + + composeEnv, err := integTestDockerComposeEnvVars() + if err != nil { + return err + } + + args := []string{"-p", dockerComposeProjectName(), "build", "--force-rm"} + if _, noCache := os.LookupEnv("DOCKER_NOCACHE"); noCache { + args = append(args, "--no-cache") + } + + if _, forcePull := os.LookupEnv("DOCKER_PULL"); forcePull { + args = append(args, "--pull") + } + + out := ioutil.Discard + if mg.Verbose() { + out = os.Stderr + } + + _, err = sh.Exec( + composeEnv, + out, + os.Stderr, + "docker-compose", args..., + ) + return err +} diff --git a/dev-tools/mage/integtest_mage.go b/dev-tools/mage/integtest_mage.go new file mode 100644 index 00000000000..82dcb90fefd --- /dev/null +++ b/dev-tools/mage/integtest_mage.go @@ -0,0 +1,58 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package mage + +import ( + "sync" + + "github.com/magefile/mage/mg" +) + +var ( + buildMageOnce sync.Once +) + +// MageIntegrationTestStep setups mage to be ran. +type MageIntegrationTestStep struct{} + +// Name returns the mage name. +func (m *MageIntegrationTestStep) Name() string { + return "mage" +} + +// Use always returns false. +// +// This step should be defined in `StepRequirements` for the tester, for it +// to be used. It cannot be autodiscovered for usage. +func (m *MageIntegrationTestStep) Use(dir string) (bool, error) { + return false, nil +} + +// Setup ensures the mage binary is built. +// +// Multiple uses of this step will only build the mage binary once. +func (m *MageIntegrationTestStep) Setup(_ map[string]string) error { + // Pre-build a mage binary to execute. + buildMageOnce.Do(func() { mg.Deps(buildMage) }) + return nil +} + +// Teardown does nothing. +func (m *MageIntegrationTestStep) Teardown(_ map[string]string) error { + return nil +} diff --git a/dev-tools/mage/kubernetes/kind.go b/dev-tools/mage/kubernetes/kind.go new file mode 100644 index 00000000000..c4a94649ca7 --- /dev/null +++ b/dev-tools/mage/kubernetes/kind.go @@ -0,0 +1,143 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package kubernetes + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +// KindIntegrationTestStep setups a kind environment. +type KindIntegrationTestStep struct{} + +// Name returns the kind name. +func (m *KindIntegrationTestStep) Name() string { + return "kind" +} + +// Use always returns false. +// +// This step should be defined in `StepRequirements` for the tester, for it +// to be used. It cannot be autodiscovered for usage. +func (m *KindIntegrationTestStep) Use(dir string) (bool, error) { + return false, nil +} + +// Setup ensures that a kubernetes cluster is up and running. +// +// If `KUBECONFIG` is already deinfed in the env then it will do nothing. +func (m *KindIntegrationTestStep) Setup(env map[string]string) error { + _, exists := env["KUBECONFIG"] + if exists { + // do nothing + return nil + } + _, exists = env["KUBE_CONFIG"] + if exists { + // do nothing + return nil + } + _, err := exec.LookPath("kind") + if err != nil { + if mg.Verbose() { + fmt.Println("Skipping kind setup; kind command missing") + } + return nil + } + + clusterName := kubernetesPodName() + stdOut := ioutil.Discard + stdErr := ioutil.Discard + if mg.Verbose() { + stdOut = os.Stdout + stdErr = os.Stderr + } + + kubeCfgDir := filepath.Join("build", "kind", clusterName) + kubeCfgDir, err = filepath.Abs(kubeCfgDir) + if err != nil { + return err + } + kubeConfig := filepath.Join(kubeCfgDir, "kubecfg") + if err := os.MkdirAll(kubeCfgDir, os.ModePerm); err != nil { + return err + } + + args := []string{ + "create", + "cluster", + "--name", clusterName, + "--kubeconfig", kubeConfig, + "--wait", + "300s", + } + kubeVersion := os.Getenv("K8S_VERSION") + if kubeVersion != "" { + args = append(args, "--image", fmt.Sprintf("kindest/node:%s", kubeVersion)) + } + + _, err = sh.Exec( + map[string]string{}, + stdOut, + stdErr, + "kind", + args..., + ) + if err != nil { + return err + } + env["KUBECONFIG"] = kubeConfig + env["KIND_CLUSTER"] = clusterName + return nil +} + +// Teardown destroys the kubernetes cluster. +func (m *KindIntegrationTestStep) Teardown(env map[string]string) error { + stdOut := ioutil.Discard + stdErr := ioutil.Discard + if mg.Verbose() { + stdOut = os.Stdout + stdErr = os.Stderr + } + + name, created := env["KIND_CLUSTER"] + _, keepUp := os.LookupEnv("KIND_SKIP_DELETE") + if created && !keepUp { + _, err := sh.Exec( + env, + stdOut, + stdErr, + "kind", + "delete", + "cluster", + "--name", + name, + ) + if err != nil { + return err + } + delete(env, "KIND_CLUSTER") + } + return nil +} diff --git a/dev-tools/mage/kubernetes/kubectl.go b/dev-tools/mage/kubernetes/kubectl.go new file mode 100644 index 00000000000..d2899ef6a8f --- /dev/null +++ b/dev-tools/mage/kubernetes/kubectl.go @@ -0,0 +1,129 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package kubernetes + +import ( + "fmt" + "io" + "os" + "os/exec" + "strings" + + "github.com/magefile/mage/mg" + "github.com/magefile/mage/sh" +) + +// KubectlApply applys the manifest file to the kubernetes cluster. +// +// KUBECONFIG must be in `env` to target a specific cluster. +func KubectlApply(env map[string]string, stdout, stderr io.Writer, filepath string) error { + _, err := sh.Exec( + env, + stdout, + stderr, + "kubectl", + "apply", + "-f", + filepath, + ) + return err +} + +// KubectlDelete deletes the resources from the manifest file from the kubernetes cluster. +// +// KUBECONFIG must be in `env` to target a specific cluster. +func KubectlDelete(env map[string]string, stdout, stderr io.Writer, filepath string) error { + _, err := sh.Exec( + env, + stdout, + stderr, + "kubectl", + "delete", + "-f", + filepath, + ) + return err +} + +// KubectlApplyInput applys the manifest string to the kubernetes cluster. +// +// KUBECONFIG must be in `env` to target a specific cluster. +func KubectlApplyInput(env map[string]string, stdout, stderr io.Writer, manifest string) error { + return kubectlIn(env, stdout, stderr, manifest, "apply", "-f", "-") +} + +// KubectlDeleteInput deletes the resources from the manifest string from the kubernetes cluster. +// +// KUBECONFIG must be in `env` to target a specific cluster. +func KubectlDeleteInput(env map[string]string, stdout, stderr io.Writer, manifest string) error { + return kubectlIn(env, stdout, stderr, manifest, "delete", "-f", "-") +} + +// KubectlWait waits for a condition to occur for a resource in the kubernetes cluster. +// +// KUBECONFIG must be in `env` to target a specific cluster. +func KubectlWait(env map[string]string, stdout, stderr io.Writer, waitFor, resource string) error { + _, err := sh.Exec( + env, + stdout, + stderr, + "kubectl", + "wait", + "--timeout=300s", + fmt.Sprintf("--for=%s", waitFor), + resource, + ) + return err +} + +func kubectlIn(env map[string]string, stdout, stderr io.Writer, input string, args ...string) error { + c := exec.Command("kubectl", args...) + c.Env = os.Environ() + for k, v := range env { + c.Env = append(c.Env, k+"="+v) + } + c.Stdout = stdout + c.Stderr = stderr + c.Stdin = strings.NewReader(input) + + if mg.Verbose() { + fmt.Println("exec:", "kubectl", strings.Join(args, " ")) + } + + return c.Run() +} + +func kubectlStart(env map[string]string, stdout, stderr io.Writer, args ...string) (*exec.Cmd, error) { + c := exec.Command("kubectl", args...) + c.Env = os.Environ() + for k, v := range env { + c.Env = append(c.Env, k+"="+v) + } + c.Stdout = stdout + c.Stderr = stderr + c.Stdin = nil + + if mg.Verbose() { + fmt.Println("exec:", "kubectl", strings.Join(args, " ")) + } + + if err := c.Start(); err != nil { + return nil, err + } + return c, nil +} diff --git a/dev-tools/mage/kubernetes/kuberemote.go b/dev-tools/mage/kubernetes/kuberemote.go new file mode 100644 index 00000000000..67a28aacdd8 --- /dev/null +++ b/dev-tools/mage/kubernetes/kuberemote.go @@ -0,0 +1,621 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package kubernetes + +import ( + "bufio" + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "os/exec" + "strings" + "time" + + "github.com/pkg/errors" + "golang.org/x/crypto/ssh" + + apiv1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/portforward" + watchtools "k8s.io/client-go/tools/watch" + "k8s.io/client-go/transport/spdy" + + "github.com/elastic/beats/v7/dev-tools/mage" +) + +const sshBitSize = 4096 + +var mode = int32(256) + +// KubeRemote rsyncs the passed directory to a pod and runs the command inside of that pod. +type KubeRemote struct { + cfg *rest.Config + cs *kubernetes.Clientset + namespace string + name string + workDir string + destDir string + syncDir string + + svcAccName string + secretName string + privateKey []byte + publicKey []byte +} + +// NewKubeRemote creates a new kubernetes remote runner. +func NewKubeRemote(kubeconfig string, namespace string, name string, workDir string, destDir string, syncDir string) (*KubeRemote, error) { + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return nil, err + } + cs, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + name = strings.Replace(name, "_", "-", -1) + svcAccName := fmt.Sprintf("%s-sa", name) + secretName := fmt.Sprintf("%s-ssh-key", name) + privateKey, publicKey, err := generateSSHKeyPair() + if err != nil { + return nil, err + } + return &KubeRemote{config, cs, namespace, name, workDir, destDir, syncDir, svcAccName, secretName, privateKey, publicKey}, nil +} + +// Run runs the command remotely on the kubernetes cluster. +func (r *KubeRemote) Run(env map[string]string, stdout io.Writer, stderr io.Writer, args ...string) error { + if err := r.syncSSHKey(); err != nil { + return errors.Wrap(err, "failed to sync SSH secret") + } + defer r.deleteSSHKey() + if err := r.syncServiceAccount(); err != nil { + return err + } + defer r.deleteServiceAccount() + _, err := r.createPod(env, args...) + if err != nil { + return errors.Wrap(err, "failed to create execute pod") + } + defer r.deletePod() + + // wait for SSH to be up inside the init container. + _, err = r.waitForPod(5*time.Minute, podInitReady) + if err != nil { + return errors.Wrap(err, "execute pod init container never started") + } + time.Sleep(1 * time.Second) // SSH inside of container can take a moment + + // forward the SSH port so rsync can be ran. + randomPort, err := getFreePort() + if err != nil { + return errors.Wrap(err, "failed to find a free port") + } + stopChannel := make(chan struct{}, 1) + readyChannel := make(chan struct{}, 1) + f, err := r.portForward([]string{fmt.Sprintf("%d:%d", randomPort, 22)}, stopChannel, readyChannel, stderr, stderr) + if err != nil { + return err + } + go f.ForwardPorts() + <-readyChannel + + // perform the rsync + r.rsync(randomPort, stderr, stderr) + + // stop port forwarding + close(stopChannel) + + // wait for exec container to be running + _, err = r.waitForPod(5*time.Minute, containerRunning("exec")) + if err != nil { + return errors.Wrap(err, "execute pod container never started") + } + + // stream the logs of the container + err = r.streamLogs("exec", stdout) + if err != nil { + return errors.Wrap(err, "failed to stream the logs") + } + + // wait for exec container to be completely done + pod, err := r.waitForPod(30*time.Second, podDone) + if err != nil { + return errors.Wrap(err, "execute pod didn't terminate after 30 seconds of log stream") + } + + // return error on failure + if pod.Status.Phase == apiv1.PodFailed { + return fmt.Errorf("execute pod test failed") + } + return nil +} + +// deleteSSHKey deletes SSH key from the cluster. +func (r *KubeRemote) deleteSSHKey() { + _ = r.cs.CoreV1().Secrets(r.namespace).Delete(r.secretName, &metav1.DeleteOptions{}) +} + +// syncSSHKey syncs the SSH key to the cluster. +func (r *KubeRemote) syncSSHKey() error { + // delete before create + r.deleteSSHKey() + _, err := r.cs.CoreV1().Secrets(r.namespace).Create(createSecretManifest(r.secretName, r.publicKey)) + if err != nil { + return err + } + return nil +} + +// deleteServiceAccount syncs required service account. +func (r *KubeRemote) deleteServiceAccount() { + _ = r.cs.RbacV1().ClusterRoleBindings().Delete(r.name, &metav1.DeleteOptions{}) + _ = r.cs.RbacV1().ClusterRoles().Delete(r.name, &metav1.DeleteOptions{}) + _ = r.cs.CoreV1().ServiceAccounts(r.namespace).Delete(r.svcAccName, &metav1.DeleteOptions{}) +} + +// syncServiceAccount syncs required service account. +func (r *KubeRemote) syncServiceAccount() error { + // delete before create + r.deleteServiceAccount() + _, err := r.cs.CoreV1().ServiceAccounts(r.namespace).Create(createServiceAccountManifest(r.svcAccName)) + if err != nil { + return errors.Wrap(err, "failed to create service account") + } + _, err = r.cs.RbacV1().ClusterRoles().Create(createClusterRoleManifest(r.name)) + if err != nil { + return errors.Wrap(err, "failed to create cluster role") + } + _, err = r.cs.RbacV1().ClusterRoleBindings().Create(createClusterRoleBindingManifest(r.name, r.namespace, r.svcAccName)) + if err != nil { + return errors.Wrap(err, "failed to create cluster role binding") + } + return nil +} + +// createPod creates the pod. +func (r *KubeRemote) createPod(env map[string]string, cmd ...string) (*apiv1.Pod, error) { + version, err := mage.GoVersion() + if err != nil { + return nil, err + } + image := fmt.Sprintf("golang:%s", version) + r.deletePod() // ensure it doesn't already exist + return r.cs.CoreV1().Pods(r.namespace).Create(createPodManifest(r.name, image, env, cmd, r.workDir, r.destDir, r.secretName, r.svcAccName)) +} + +// deletePod deletes the pod. +func (r *KubeRemote) deletePod() { + _ = r.cs.CoreV1().Pods(r.namespace).Delete(r.name, &metav1.DeleteOptions{}) +} + +// waitForPod waits for the created pod to match the given condition. +func (r *KubeRemote) waitForPod(wait time.Duration, condition watchtools.ConditionFunc) (*apiv1.Pod, error) { + w, err := r.cs.CoreV1().Pods(r.namespace).Watch(metav1.SingleObject(metav1.ObjectMeta{Name: r.name})) + if err != nil { + return nil, err + } + + ctx, _ := watchtools.ContextWithOptionalTimeout(context.Background(), wait) + ev, err := watchtools.UntilWithoutRetry(ctx, w, func(ev watch.Event) (bool, error) { + return condition(ev) + }) + if ev != nil { + return ev.Object.(*apiv1.Pod), err + } + return nil, err +} + +// portFoward runs the port forwarding so SSH rsync can be ran into the pod. +func (r *KubeRemote) portForward(ports []string, stopChannel, readyChannel chan struct{}, stdout, stderr io.Writer) (*portforward.PortForwarder, error) { + roundTripper, upgrader, err := spdy.RoundTripperFor(r.cfg) + if err != nil { + return nil, err + } + + path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", r.namespace, r.name) + hostIP := strings.TrimLeft(r.cfg.Host, "https://") + serverURL := url.URL{Scheme: "https", Path: path, Host: hostIP} + dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, &serverURL) + return portforward.New(dialer, ports, stopChannel, readyChannel, stdout, stderr) +} + +// rsync performs the rsync of sync directory to destination directory inside of the pod. +func (r *KubeRemote) rsync(port uint16, stdout, stderr io.Writer) error { + privateKeyFile, err := createTempFile(r.privateKey) + if err != nil { + return err + } + + rsh := fmt.Sprintf("ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -p %d -i %s", port, privateKeyFile) + args := []string{ + "--rsh", rsh, + "-a", fmt.Sprintf("%s/", r.syncDir), + fmt.Sprintf("root@localhost:%s", r.destDir), + } + cmd := exec.Command("rsync", args...) + cmd.Stdout = stdout + cmd.Stderr = stderr + return cmd.Run() +} + +// streamLogs streams the logs from the execution pod until the pod is terminated. +func (r *KubeRemote) streamLogs(container string, stdout io.Writer) error { + req := r.cs.CoreV1().Pods(r.namespace).GetLogs(r.name, &apiv1.PodLogOptions{ + Container: container, + Follow: true, + }) + logs, err := req.Stream() + if err != nil { + return err + } + defer logs.Close() + + reader := bufio.NewReader(logs) + for { + bytes, err := reader.ReadBytes('\n') + if _, err := stdout.Write(bytes); err != nil { + return err + } + if err != nil { + if err != io.EOF { + return err + } + return nil + } + } +} + +// generateSSHKeyPair generates a new SSH key pair. +func generateSSHKeyPair() ([]byte, []byte, error) { + private, err := rsa.GenerateKey(rand.Reader, sshBitSize) + if err != nil { + return nil, nil, err + } + if err = private.Validate(); err != nil { + return nil, nil, err + } + public, err := ssh.NewPublicKey(&private.PublicKey) + if err != nil { + return nil, nil, err + } + return encodePrivateKeyToPEM(private), ssh.MarshalAuthorizedKey(public), nil +} + +// encodePrivateKeyToPEM encodes private key from RSA to PEM format. +func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte { + privDER := x509.MarshalPKCS1PrivateKey(privateKey) + privBlock := pem.Block{ + Type: "RSA PRIVATE KEY", + Headers: nil, + Bytes: privDER, + } + return pem.EncodeToMemory(&privBlock) +} + +// getFreePort finds a free port. +func getFreePort() (uint16, error) { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0, err + } + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0, err + } + defer l.Close() + return uint16(l.Addr().(*net.TCPAddr).Port), nil +} + +// createSecretManifest creates the secret object to create in the cluster. +// +// This is the public key that the sshd uses as the authorized key. +func createSecretManifest(name string, publicKey []byte) *apiv1.Secret { + return &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + StringData: map[string]string{ + "authorized_keys": string(publicKey), + }, + } +} + +// createServiceAccountManifest creates the service account the pod will used. +func createServiceAccountManifest(name string) *apiv1.ServiceAccount { + return &apiv1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} + +// createClusterRoleManifest creates the cluster role the pod will used. +// +// This gives the pod all permissions on everything! +func createClusterRoleManifest(name string) *rbacv1.ClusterRole { + return &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Rules: []rbacv1.PolicyRule{ + rbacv1.PolicyRule{ + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + }, + rbacv1.PolicyRule{ + Verbs: []string{"*"}, + NonResourceURLs: []string{"*"}, + }, + }, + } +} + +// createClusterRoleBindingManifest creates the cluster role binding the pod will used. +// +// This binds the service account to the cluster role. +func createClusterRoleBindingManifest(name string, namespace string, svcAccName string) *rbacv1.ClusterRoleBinding { + return &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Subjects: []rbacv1.Subject{ + rbacv1.Subject{ + Kind: "ServiceAccount", + Name: svcAccName, + Namespace: namespace, + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: name, + }, + } +} + +// createPodManifest creates the pod inside of the cluster that will be used for remote execution. +// +// Creates a pod with an init container that runs sshd-rsync, once the first connection closes the init container +// exits then the exec container starts using the rsync'd directory as its work directory. +func createPodManifest(name string, image string, env map[string]string, cmd []string, workDir string, destDir string, secretName string, svcAccName string) *apiv1.Pod { + execEnv := []apiv1.EnvVar{ + apiv1.EnvVar{ + Name: "NODE_NAME", + ValueFrom: &apiv1.EnvVarSource{ + FieldRef: &apiv1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + } + for k, v := range env { + execEnv = append(execEnv, apiv1.EnvVar{ + Name: k, + Value: v, + }) + } + return &apiv1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: apiv1.PodSpec{ + ServiceAccountName: svcAccName, + HostNetwork: true, + DNSPolicy: apiv1.DNSClusterFirstWithHostNet, + RestartPolicy: apiv1.RestartPolicyNever, + InitContainers: []apiv1.Container{ + { + Name: "sync-init", + Image: "ernoaapa/sshd-rsync", + Ports: []apiv1.ContainerPort{ + { + Name: "ssh", + Protocol: apiv1.ProtocolTCP, + ContainerPort: 22, + }, + }, + Env: []apiv1.EnvVar{ + { + Name: "ONE_TIME", + Value: "true", + }, + }, + VolumeMounts: []apiv1.VolumeMount{ + { + Name: "ssh-config", + MountPath: "/root/.ssh/authorized_keys", + SubPath: "authorized_keys", + }, + { + Name: "destdir", + MountPath: destDir, + }, + }, + }, + }, + Containers: []apiv1.Container{ + { + Name: "exec", + Image: image, + Command: cmd, + WorkingDir: workDir, + Env: execEnv, + VolumeMounts: []apiv1.VolumeMount{ + { + Name: "destdir", + MountPath: destDir, + }, + }, + }, + }, + Volumes: []apiv1.Volume{ + { + Name: "ssh-config", + VolumeSource: apiv1.VolumeSource{ + Secret: &apiv1.SecretVolumeSource{ + SecretName: secretName, + DefaultMode: &mode, + }, + }, + }, + { + Name: "destdir", + VolumeSource: apiv1.VolumeSource{ + EmptyDir: &apiv1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + } +} + +func podInitReady(event watch.Event) (bool, error) { + switch event.Type { + case watch.Deleted: + return false, k8serrors.NewNotFound(schema.GroupResource{Resource: "pods"}, "") + } + switch t := event.Object.(type) { + case *apiv1.Pod: + switch t.Status.Phase { + case apiv1.PodFailed, apiv1.PodSucceeded: + return false, nil + case apiv1.PodRunning: + return false, nil + case apiv1.PodPending: + return isInitContainersReady(t), nil + } + } + return false, nil +} + +func isInitContainersReady(pod *apiv1.Pod) bool { + if isScheduled(pod) && isInitContainersRunning(pod) { + return true + } + return false +} + +func isScheduled(pod *apiv1.Pod) bool { + if &pod.Status != nil && len(pod.Status.Conditions) > 0 { + for _, condition := range pod.Status.Conditions { + if condition.Type == apiv1.PodScheduled && + condition.Status == apiv1.ConditionTrue { + return true + } + } + } + return false +} + +func isInitContainersRunning(pod *apiv1.Pod) bool { + if &pod.Status != nil { + if len(pod.Spec.InitContainers) != len(pod.Status.InitContainerStatuses) { + return false + } + for _, status := range pod.Status.InitContainerStatuses { + if status.State.Running == nil { + return false + } + } + return true + } + return false +} + +func containerRunning(containerName string) func(watch.Event) (bool, error) { + return func(event watch.Event) (bool, error) { + switch event.Type { + case watch.Deleted: + return false, k8serrors.NewNotFound(schema.GroupResource{Resource: "pods"}, "") + } + switch t := event.Object.(type) { + case *apiv1.Pod: + switch t.Status.Phase { + case apiv1.PodFailed, apiv1.PodSucceeded: + return false, nil + case apiv1.PodRunning: + return isContainerRunning(t, containerName) + } + } + return false, nil + } +} + +func isContainerRunning(pod *apiv1.Pod, containerName string) (bool, error) { + for _, status := range pod.Status.ContainerStatuses { + if status.Name == containerName { + if status.State.Waiting != nil { + return false, nil + } else if status.State.Running != nil { + return true, nil + } else if status.State.Terminated != nil { + return false, nil + } else { + return false, fmt.Errorf("Unknown container state") + } + } + } + return false, nil +} + +func podDone(event watch.Event) (bool, error) { + switch event.Type { + case watch.Deleted: + return false, k8serrors.NewNotFound(schema.GroupResource{Resource: "pods"}, "") + } + switch t := event.Object.(type) { + case *apiv1.Pod: + switch t.Status.Phase { + case apiv1.PodFailed, apiv1.PodSucceeded: + return true, nil + } + } + return false, nil +} + +func createTempFile(content []byte) (string, error) { + randBytes := make([]byte, 16) + rand.Read(randBytes) + tmpfile, err := ioutil.TempFile("", hex.EncodeToString(randBytes)) + if err != nil { + return "", err + } + defer tmpfile.Close() + if _, err := tmpfile.Write(content); err != nil { + return "", err + } + return tmpfile.Name(), nil +} diff --git a/dev-tools/mage/kubernetes/kubernetes.go b/dev-tools/mage/kubernetes/kubernetes.go new file mode 100644 index 00000000000..ec2cf95ddd4 --- /dev/null +++ b/dev-tools/mage/kubernetes/kubernetes.go @@ -0,0 +1,165 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package kubernetes + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "github.com/magefile/mage/mg" + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/dev-tools/mage" +) + +func init() { + mage.RegisterIntegrationTester(&KubernetesIntegrationTester{}) +} + +type KubernetesIntegrationTester struct { +} + +// Name returns kubernetes name. +func (d *KubernetesIntegrationTester) Name() string { + return "kubernetes" +} + +// Use determines if this tester should be used. +func (d *KubernetesIntegrationTester) Use(dir string) (bool, error) { + kubernetesFile := filepath.Join(dir, "kubernetes.yml") + if _, err := os.Stat(kubernetesFile); !os.IsNotExist(err) { + return true, nil + } + return false, nil +} + +// HasRequirements ensures that the required kubectl are installed. +func (d *KubernetesIntegrationTester) HasRequirements() error { + if err := mage.HaveKubectl(); err != nil { + return err + } + return nil +} + +// StepRequirements returns the steps required for this tester. +func (d *KubernetesIntegrationTester) StepRequirements() mage.IntegrationTestSteps { + return mage.IntegrationTestSteps{&mage.MageIntegrationTestStep{}, &KindIntegrationTestStep{}} +} + +// Test performs the tests with kubernetes. +func (d *KubernetesIntegrationTester) Test(dir string, mageTarget string, env map[string]string) error { + stdOut := ioutil.Discard + stdErr := ioutil.Discard + if mg.Verbose() { + stdOut = os.Stdout + stdErr = os.Stderr + } + + manifestPath := filepath.Join(dir, "kubernetes.yml") + if _, err := os.Stat(manifestPath); os.IsNotExist(err) { + // defensive, as `Use` should cause this runner not to be used if no file. + return fmt.Errorf("no kubernetes.yml") + } + + kubeConfig := env["KUBECONFIG"] + if kubeConfig == "" { + kubeConfig = env["KUBE_CONFIG"] + } + if kubeConfig == "" { + fmt.Println("Skip running tests inside of kubernetes no KUBECONFIG defined.") + return nil + } + + if mg.Verbose() { + fmt.Println(">> Applying module manifest to cluster...") + } + + // Determine the path to use inside the pod. + repo, err := mage.GetProjectRepoInfo() + if err != nil { + return err + } + magePath := filepath.Join("/go/src", repo.CanonicalRootImportPath, repo.SubDir, "build/mage-linux-amd64") + + // Apply the manifest from the dir. This is the requirements for the tests that will + // run inside the cluster. + if err := KubectlApply(env, stdOut, stdErr, manifestPath); err != nil { + return errors.Wrapf(err, "failed to apply manifest %s", manifestPath) + } + defer func() { + if mg.Verbose() { + fmt.Println(">> Deleting module manifest from cluster...") + } + if err := KubectlDelete(env, stdOut, stdErr, manifestPath); err != nil { + log.Printf("%s", errors.Wrapf(err, "failed to apply manifest %s", manifestPath)) + } + }() + + // Pass all environment variables inside the pod, except for KUBECONFIG as the test + // should use the environment set by kubernetes on the pod. + insideEnv := map[string]string{} + for envKey, envVal := range env { + if envKey != "KUBECONFIG" && envKey != "KUBE_CONFIG" { + insideEnv[envKey] = envVal + } + } + + destDir := filepath.Join("/go/src", repo.CanonicalRootImportPath) + workDir := filepath.Join(destDir, repo.SubDir) + remote, err := NewKubeRemote(kubeConfig, "default", kubernetesPodName(), workDir, destDir, repo.RootDir) + if err != nil { + return err + } + // Uses `os.Stdout` directly as its output should always be shown. + err = remote.Run(insideEnv, os.Stdout, stdErr, magePath, mageTarget) + if err != nil { + return err + } + return nil +} + +// InsideTest performs the tests inside of environment. +func (d *KubernetesIntegrationTester) InsideTest(test func() error) error { + return test() +} + +// kubernetesPodName returns the pod name to use with kubernetes. +func kubernetesPodName() string { + commit, err := mage.CommitHash() + if err != nil { + panic(errors.Wrap(err, "failed to construct kind cluster name")) + } + + version, err := mage.BeatQualifiedVersion() + if err != nil { + panic(errors.Wrap(err, "failed to construct kind cluster name")) + } + version = strings.NewReplacer(".", "_").Replace(version) + + clusterName := "{{.BeatName}}_{{.Version}}_{{.ShortCommit}}-{{.StackEnvironment}}" + clusterName = mage.MustExpand(clusterName, map[string]interface{}{ + "StackEnvironment": mage.StackEnvironment, + "ShortCommit": commit[:10], + "Version": version, + }) + return clusterName +} diff --git a/dev-tools/mage/target/integtest/integtest.go b/dev-tools/mage/target/integtest/integtest.go index 324fa25e732..62d601cea6d 100644 --- a/dev-tools/mage/target/integtest/integtest.go +++ b/dev-tools/mage/target/integtest/integtest.go @@ -53,8 +53,6 @@ func WhitelistEnvVar(key ...string) { // IntegTest executes integration tests (it uses Docker to run the tests). func IntegTest() { - devtools.AddIntegTestUsage() - defer devtools.StopIntegTestEnv() mg.SerialDeps(GoIntegTest, PythonIntegTest) } @@ -65,9 +63,13 @@ func GoIntegTest(ctx context.Context) error { if !devtools.IsInIntegTestEnv() { mg.SerialDeps(goTestDeps...) } - return devtools.RunIntegTest("goIntegTest", func() error { + runner, err := devtools.NewDockerIntegrationRunner(whitelistedEnvVars...) + if err != nil { + return err + } + return runner.Test("goIntegTest", func() error { return devtools.GoTest(ctx, devtools.DefaultGoTestIntegrationArgs()) - }, whitelistedEnvVars...) + }) } // PythonIntegTest executes the python system tests in the integration @@ -79,8 +81,12 @@ func PythonIntegTest(ctx context.Context) error { if !devtools.IsInIntegTestEnv() { mg.SerialDeps(pythonTestDeps...) } - return devtools.RunIntegTest("pythonIntegTest", func() error { + runner, err := devtools.NewDockerIntegrationRunner(append(whitelistedEnvVars, devtools.ListMatchingEnvVars("NOSE_")...)...) + if err != nil { + return err + } + return runner.Test("pythonIntegTest", func() error { mg.Deps(devtools.BuildSystemTestBinary) return devtools.PythonNoseTest(devtools.DefaultPythonTestIntegrationArgs()) - }, append(whitelistedEnvVars, devtools.ListMatchingEnvVars("NOSE_")...)...) + }) } diff --git a/dev-tools/make/mage-install.mk b/dev-tools/make/mage-install.mk new file mode 100644 index 00000000000..8966ed6f474 --- /dev/null +++ b/dev-tools/make/mage-install.mk @@ -0,0 +1,13 @@ +MAGE_VERSION ?= v1.9.0 +MAGE_PRESENT := $(shell mage --version 2> /dev/null | grep $(MAGE_VERSION)) +MAGE_IMPORT_PATH ?= github.com/magefile/mage +export MAGE_IMPORT_PATH + +.PHONY: mage +mage: +ifndef MAGE_PRESENT + @echo Installing mage $(MAGE_VERSION) from vendor dir. + @go install -mod=vendor -ldflags="-X $(MAGE_IMPORT_PATH)/mage.gitTag=$(MAGE_VERSION)" ${MAGE_IMPORT_PATH}/... + @-mage -clean +endif + @true diff --git a/dev-tools/make/mage.mk b/dev-tools/make/mage.mk index 8966ed6f474..6b210832006 100644 --- a/dev-tools/make/mage.mk +++ b/dev-tools/make/mage.mk @@ -1,13 +1,77 @@ -MAGE_VERSION ?= v1.9.0 -MAGE_PRESENT := $(shell mage --version 2> /dev/null | grep $(MAGE_VERSION)) -MAGE_IMPORT_PATH ?= github.com/magefile/mage -export MAGE_IMPORT_PATH - -.PHONY: mage -mage: -ifndef MAGE_PRESENT - @echo Installing mage $(MAGE_VERSION) from vendor dir. - @go install -mod=vendor -ldflags="-X $(MAGE_IMPORT_PATH)/mage.gitTag=$(MAGE_VERSION)" ${MAGE_IMPORT_PATH}/... - @-mage -clean -endif - @true +# This is a minimal Makefile for Beats that are built with Mage. Its only +# responsibility is to provide compatibility with existing Jenkins and Travis +# setups. + +# +# Variables +# +.DEFAULT_GOAL := help +PWD := $(CURDIR) + +# +# Includes +# +include $(ES_BEATS)/dev-tools/make/mage-install.mk + +# +# Targets (alphabetically sorted). +# +.PHONY: check +check: mage + mage check + +.PHONY: clean +clean: mage + mage clean + +fix-permissions: + +.PHONY: fmt +fmt: mage + mage fmt + +# Default target. +.PHONY: help +help: + @echo Use mage rather than make. Here are the available mage targets: + @mage -l + +.PHONY: release +release: mage + mage package + +stop-environment: + +.PHONY: unit-tests +unit-tests: mage + mage unitTest + +.PHONY: integration-tests +integration-tests: mage + rm -f build/TEST-go-integration.out + mage goIntegTest || ( cat build/TEST-go-integration.out && false ) + +.PHONY: system-tests +system-tests: mage + mage pythonIntegTest + +.PHONY: testsuite +testsuite: mage + rm -f build/TEST-go-integration.out + mage update build unitTest integTest || ( cat build/TEST-go-integration.out && false ) + +.PHONY: update +update: mage + mage update + +.PHONY: crosscompile +crosscompile: mage + mage crossBuild + +.PHONY: docs +docs: + mage docs + +.PHONY: docs-preview +docs-preview: + PREVIEW=1 $(MAKE) docs diff --git a/dev-tools/make/xpack.mk b/dev-tools/make/xpack.mk deleted file mode 100644 index 54f60831108..00000000000 --- a/dev-tools/make/xpack.mk +++ /dev/null @@ -1,53 +0,0 @@ -# This is a minimal Makefile for Beats that are built with Mage. Its only -# responsibility is to provide compatibility with existing Jenkins and Travis -# setups. - -# -# Variables -# -.DEFAULT_GOAL := help -PWD := $(CURDIR) - -# -# Includes -# -include $(ES_BEATS)/dev-tools/make/mage.mk - -# -# Targets (alphabetically sorted). -# -.PHONY: check -check: mage - mage check - -.PHONY: clean -clean: mage - mage clean - -fix-permissions: - -.PHONY: fmt -fmt: mage - mage fmt - -# Default target. -.PHONY: help -help: - @echo Use mage rather than make. Here are the available mage targets: - @mage -l - -.PHONY: release -release: mage - mage package - -stop-environment: - -.PHONY: testsuite -testsuite: mage - rm -f build/TEST-go-integration.out - mage update build unitTest integTest || ( cat build/TEST-go-integration.out && false ) - -.PHONY: update -update: mage - mage update - diff --git a/docs/devguide/terraform.asciidoc b/docs/devguide/terraform.asciidoc new file mode 100644 index 00000000000..2c21c8f4314 --- /dev/null +++ b/docs/devguide/terraform.asciidoc @@ -0,0 +1,101 @@ +[[terraform-beats]] +== Terraform in Beats + +Terraform is used to provision scenarios for integration testing of some cloud +features. Features implementing integration tests that require the presence of +cloud resources should have their own Terraform configuration, this configuration +can be used when developing locally to create (and destroy) resources that allow +to test these features. + +Tests requiring access to cloud providers should be disabled by default with the +use of build tags. + +[[installing-terraform]] +=== Installing Terraform + +Terraform is available in https://www.terraform.io/downloads.html + +Download it and place it in some directory in your PATH. + +`terraform` is the main command for Terraform and the only one that is usually +needed to manage configurations. Terraform will also download other plugins that +implement the specific functionality for each provider. These plugins are +automatically managed and stored in the working copy, if you want to share the +plugins between multiple working copies you can manually install them in the +user the user plugins directory located at `~/.terraform.d/plugins`, +or `%APPDATA%\terraform.d\plugins on Windows`. + +Plugins are available in https://registry.terraform.io/ + +[[using-terraform]] +=== Using Terraform + +The most important commands when using Terraform are: +* `terraform init` to do some initial checks and install the required plugins. +* `terraform apply` to create the resources defined in the configuration. +* `terraform destroy` to destroy resources previously created. + +Cloud providers use to require credentials, they can be provided with the usual +methods supported by these providers, using environment variables and/or +credential files. + +Terraform stores the last known state of the resources managed by a +configuration in a `terraform.tfstate` file. It is important to keep this file +as it is used as input by `terraform destroy`. This file is created in the same +directory where `terraform apply` is executed. + +Please take a look to Terraform documentation for more details: https://www.terraform.io/intro/index.html + +[[terraform-configurations]] +=== Terraform configuration guidelines + +The main purpouse of Terraform in Beats is to create and destroy cloud resources +required by integration tests. For these configurations there are some things to +take into account: +* Apply should work without additional inputs or files. Only input will be the + required for specific providers, using environment variables or credential + files. +* You must be able to apply the same configuration multiple times in the same + account. This will allow to have multiple builds using the same configuration + but with different instances of the resources. Some resources are already + created with unique identifiers (as EC2 instances), some others have to be + explicitly created with unique names (e.g. S3 buckets). For these cases random + suffixes can be added to identifiers. +* Destroy must work without additional input, and should be able to destroy all + the resources created by the configuration. There are some resources that need + specific flags to be destroyed by `terraform destroy`. For example S3 buckets + need a flag to force to empty the bucket before deleting it, or RDS instances + need a flag to disable snapshots on deletion. + +[[terraform-in-ci]] +=== Terraform in CI + +Integration tests that need the presence of certain resources to work can be +executed in CI if they provide a Terraform configuration to start these +resources. These tests are disabled by default in CI. + +Terraform states are archived as artifacrs of builds, this allows to manually +destroy resources created by builds that were not able to do a proper cleanup. + +Here is a checklist to add support for a cloud feature in Jenkins: +* In the feature code: + * Tests have a build tag so they are disabled by default. When run from mage, + its execution can be selected using the `TEST_TAGS` environment variable, e.g: + `TEST_TAGS=aws` for AWS tests. + * There is some Terraform configuration that defines a cloud scenario where + tests pass. This configuration should be in the directory of the feature. +* In the Jenkinsfile: + * Add a boolean parameter to run the tests on this environment, e.g. + `awsCloudTests`. This parameter should be set to false by default. + * Add a conditional block in `withCloudTestEnv` that: + * Will be executed if the previously added boolean parameter, or `allCloudTests` + are set to true. + * Adds the tag to `TEST_TAGS` (as comma separated values), so tests are + selected. + * Defines how to obtain the credentials and provide them to the tests. + * In the stage of the specific beat: + * Add a stage that calls to `startCloudTestEnv`, if there isn't anyone. + * Add a post cleanup step that calls to `terraformCleanup`, if there isn't anyone. + * Add a environment to the list of environments started by `startCloudEnv`, + with the condition to start the scenario, and the path to the directory + with its definition, e.g. `[cond: params.awsCloudTests, dir: 'x-pack/metricbeat/module/aws']` diff --git a/filebeat/_meta/beat.docker.yml b/filebeat/_meta/config/beat.docker.yml.tmpl similarity index 98% rename from filebeat/_meta/beat.docker.yml rename to filebeat/_meta/config/beat.docker.yml.tmpl index 756c2df5217..3af3b45289c 100644 --- a/filebeat/_meta/beat.docker.yml +++ b/filebeat/_meta/config/beat.docker.yml.tmpl @@ -2,4 +2,3 @@ filebeat.config: modules: path: ${path.config}/modules.d/*.yml reload.enabled: false - diff --git a/filebeat/_meta/config/beat.reference.yml.tmpl b/filebeat/_meta/config/beat.reference.yml.tmpl new file mode 100644 index 00000000000..542892789b1 --- /dev/null +++ b/filebeat/_meta/config/beat.reference.yml.tmpl @@ -0,0 +1,5 @@ +{{template "header.reference.yml.tmpl" .}} +{{template "config.modules.yml.tmpl" .}} +{{template "filebeat.inputs.reference.yml.tmpl" .}} +{{template "filebeat.autodiscover.reference.yml.tmpl" .}} +{{template "filebeat.global.reference.yml.tmpl" .}} diff --git a/filebeat/_meta/config/beat.yml.tmpl b/filebeat/_meta/config/beat.yml.tmpl new file mode 100644 index 00000000000..95a6019ee70 --- /dev/null +++ b/filebeat/_meta/config/beat.yml.tmpl @@ -0,0 +1,4 @@ +{{template "header.yml.tmpl" .}} +{{template "filebeat.inputs.yml.tmpl" .}} +{{template "filebeat.config.modules.yml.tmpl" .}} +{{template "setup.template.yml.tmpl" .}} diff --git a/filebeat/_meta/config/filebeat.autodiscover.reference.yml.tmpl b/filebeat/_meta/config/filebeat.autodiscover.reference.yml.tmpl new file mode 100644 index 00000000000..003559be0dc --- /dev/null +++ b/filebeat/_meta/config/filebeat.autodiscover.reference.yml.tmpl @@ -0,0 +1,16 @@ +{{header "Filebeat autodiscover"}} + +# Autodiscover allows you to detect changes in the system and spawn new modules +# or inputs as they happen. + +#filebeat.autodiscover: + # List of enabled autodiscover providers +# providers: +# - type: docker +# templates: +# - condition: +# equals.docker.container.image: busybox +# config: +# - type: container +# paths: +# - /var/lib/docker/containers/${data.docker.container.id}/*.log diff --git a/filebeat/_meta/config/filebeat.config.modules.yml.tmpl b/filebeat/_meta/config/filebeat.config.modules.yml.tmpl new file mode 100644 index 00000000000..58214008d2b --- /dev/null +++ b/filebeat/_meta/config/filebeat.config.modules.yml.tmpl @@ -0,0 +1,11 @@ +{{header "Filebeat modules"}} + +filebeat.config.modules: + # Glob pattern for configuration loading + path: ${path.config}/modules.d/*.yml + + # Set to true to enable config reloading + reload.enabled: false + + # Period on which files under path should be checked for changes + #reload.period: 10s diff --git a/filebeat/_meta/common.reference.p2.yml b/filebeat/_meta/config/filebeat.global.reference.yml.tmpl similarity index 74% rename from filebeat/_meta/common.reference.p2.yml rename to filebeat/_meta/config/filebeat.global.reference.yml.tmpl index eb3c1c9cca4..dccfc790a7c 100644 --- a/filebeat/_meta/common.reference.p2.yml +++ b/filebeat/_meta/config/filebeat.global.reference.yml.tmpl @@ -1,21 +1,4 @@ -#========================== Filebeat autodiscover ============================== - -# Autodiscover allows you to detect changes in the system and spawn new modules -# or inputs as they happen. - -#filebeat.autodiscover: - # List of enabled autodiscover providers -# providers: -# - type: docker -# templates: -# - condition: -# equals.docker.container.image: busybox -# config: -# - type: container -# paths: -# - /var/lib/docker/containers/${data.docker.container.id}/*.log - -#========================= Filebeat global options ============================ +{{header "Filebeat global options"}} # Registry data path. If a relative path is used, it is considered relative to the # data path. diff --git a/filebeat/_meta/common.reference.inputs.yml b/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl similarity index 98% rename from filebeat/_meta/common.reference.inputs.yml rename to filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl index b7de598be0b..db105f17ef0 100644 --- a/filebeat/_meta/common.reference.inputs.yml +++ b/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl @@ -65,6 +65,11 @@ filebeat.inputs: # Set to true to publish fields with null values in events. #keep_null: false + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + # Ignore files which were modified more then the defined timespan in the past. # ignore_older is disabled by default, so no files are ignored by setting it to 0. # Time strings like 2h (2 hours), 5m (5 minutes) can be used. diff --git a/filebeat/_meta/common.p2.yml b/filebeat/_meta/config/filebeat.inputs.yml.tmpl similarity index 72% rename from filebeat/_meta/common.p2.yml rename to filebeat/_meta/config/filebeat.inputs.yml.tmpl index b423f0b0494..a7bd1b5eaa6 100644 --- a/filebeat/_meta/common.p2.yml +++ b/filebeat/_meta/config/filebeat.inputs.yml.tmpl @@ -1,7 +1,4 @@ -# For more available modules and options, please see the filebeat.reference.yml sample -# configuration file. - -#=========================== Filebeat inputs ============================= +{{header "Filebeat inputs"}} filebeat.inputs: @@ -52,23 +49,3 @@ filebeat.inputs: # that was (not) matched before or after or as long as a pattern is not matched based on negate. # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after - - -#============================= Filebeat modules =============================== - -filebeat.config.modules: - # Glob pattern for configuration loading - path: ${path.config}/modules.d/*.yml - - # Set to true to enable config reloading - reload.enabled: false - - # Period on which files under path should be checked for changes - #reload.period: 10s - -#==================== Elasticsearch template setting ========================== - -setup.template.settings: - index.number_of_shards: 1 - #index.codec: best_compression - #_source.enabled: false diff --git a/filebeat/_meta/common.reference.p1.yml b/filebeat/_meta/config/header.reference.yml.tmpl similarity index 99% rename from filebeat/_meta/common.reference.p1.yml rename to filebeat/_meta/config/header.reference.yml.tmpl index c02e11deacb..9c9e00b66b2 100644 --- a/filebeat/_meta/common.reference.p1.yml +++ b/filebeat/_meta/config/header.reference.yml.tmpl @@ -6,4 +6,3 @@ # # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/filebeat/index.html - diff --git a/filebeat/_meta/common.p1.yml b/filebeat/_meta/config/header.yml.tmpl similarity index 79% rename from filebeat/_meta/common.p1.yml rename to filebeat/_meta/config/header.yml.tmpl index 10b4ef6956d..d0351dc0ff1 100644 --- a/filebeat/_meta/common.p1.yml +++ b/filebeat/_meta/config/header.yml.tmpl @@ -7,3 +7,5 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/filebeat/index.html +# For more available modules and options, please see the filebeat.reference.yml sample +# configuration file. diff --git a/auditbeat/_meta/common.p2.yml b/filebeat/_meta/config/setup.template.yml.tmpl similarity index 58% rename from auditbeat/_meta/common.p2.yml rename to filebeat/_meta/config/setup.template.yml.tmpl index 468cc1d45a9..290fbc27ace 100644 --- a/auditbeat/_meta/common.p2.yml +++ b/filebeat/_meta/config/setup.template.yml.tmpl @@ -1,5 +1,5 @@ +{{header "Elasticsearch template setting"}} -#==================== Elasticsearch template setting ========================== setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression diff --git a/filebeat/autodiscover/builder/hints/logs.go b/filebeat/autodiscover/builder/hints/logs.go index 05ec4ac7b8f..e2f37caee74 100644 --- a/filebeat/autodiscover/builder/hints/logs.go +++ b/filebeat/autodiscover/builder/hints/logs.go @@ -109,7 +109,7 @@ func (l *logHints) CreateConfig(event bus.Event) []*common.Config { } logp.Debug("hints.builder", "generated config %+v", configs) // Apply information in event to the template to generate the final config - return template.ApplyConfigTemplate(event, configs) + return template.ApplyConfigTemplate(event, configs, false) } tempCfg := common.MapStr{} @@ -163,7 +163,7 @@ func (l *logHints) CreateConfig(event bus.Event) []*common.Config { logp.Debug("hints.builder", "generated config %+v", config) // Apply information in event to the template to generate the final config - return template.ApplyConfigTemplate(event, []*common.Config{config}) + return template.ApplyConfigTemplate(event, []*common.Config{config}, false) } func (l *logHints) getMultiline(hints common.MapStr) common.MapStr { diff --git a/filebeat/beater/channels.go b/filebeat/beater/channels.go index 38874cf483a..de65fbf5c68 100644 --- a/filebeat/beater/channels.go +++ b/filebeat/beater/channels.go @@ -22,7 +22,9 @@ import ( "github.com/elastic/beats/v7/filebeat/input/file" "github.com/elastic/beats/v7/filebeat/registrar" + "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/monitoring" + "github.com/elastic/beats/v7/libbeat/publisher/pipetool" ) type registrarLogger struct { @@ -41,6 +43,23 @@ type eventCounter struct { wg sync.WaitGroup } +// countingClient adds and substracts from a counter when events have been +// published, dropped or ACKed. The countingClient can be used to keep track of +// inflight events for a beat.Client instance. The counter is updated after the +// client has been disconnected from the publisher pipeline via 'Closed'. +type countingClient struct { + counter *eventCounter + client beat.Client +} + +type countingEventer struct { + wgEvents *eventCounter +} + +type combinedEventer struct { + a, b beat.ClientEventer +} + func newRegistrarLogger(reg *registrar.Registrar) *registrarLogger { return ®istrarLogger{ done: make(chan struct{}), @@ -87,3 +106,75 @@ func (c *eventCounter) Done() { func (c *eventCounter) Wait() { c.wg.Wait() } + +// withPipelineEventCounter adds a counter to the pipeline that keeps track of +// all events published, dropped and ACKed by any active client. +// The type accepted by counter is compatible with sync.WaitGroup. +func withPipelineEventCounter(pipeline beat.PipelineConnector, counter *eventCounter) beat.PipelineConnector { + counterListener := &countingEventer{counter} + + pipeline = pipetool.WithClientConfigEdit(pipeline, func(config beat.ClientConfig) (beat.ClientConfig, error) { + if evts := config.Events; evts != nil { + config.Events = &combinedEventer{evts, counterListener} + } else { + config.Events = counterListener + } + return config, nil + }) + + pipeline = pipetool.WithClientWrapper(pipeline, func(client beat.Client) beat.Client { + return &countingClient{ + counter: counter, + client: client, + } + }) + return pipeline +} + +func (c *countingClient) Publish(event beat.Event) { + c.counter.Add(1) + c.client.Publish(event) +} + +func (c *countingClient) PublishAll(events []beat.Event) { + c.counter.Add(len(events)) + c.client.PublishAll(events) +} + +func (c *countingClient) Close() error { + return c.client.Close() +} + +func (*countingEventer) Closing() {} +func (*countingEventer) Closed() {} +func (*countingEventer) Published() {} + +func (c *countingEventer) FilteredOut(_ beat.Event) {} +func (c *countingEventer) DroppedOnPublish(_ beat.Event) { + c.wgEvents.Done() +} + +func (c *combinedEventer) Closing() { + c.a.Closing() + c.b.Closing() +} + +func (c *combinedEventer) Closed() { + c.a.Closed() + c.b.Closed() +} + +func (c *combinedEventer) Published() { + c.a.Published() + c.b.Published() +} + +func (c *combinedEventer) FilteredOut(event beat.Event) { + c.a.FilteredOut(event) + c.b.FilteredOut(event) +} + +func (c *combinedEventer) DroppedOnPublish(event beat.Event) { + c.a.DroppedOnPublish(event) + c.b.DroppedOnPublish(event) +} diff --git a/filebeat/beater/crawler.go b/filebeat/beater/crawler.go index 60b4ff0609e..9ac830f8696 100644 --- a/filebeat/beater/crawler.go +++ b/filebeat/beater/crawler.go @@ -62,7 +62,7 @@ func newCrawler( // Start starts the crawler with all inputs func (c *crawler) Start( - pipeline beat.Pipeline, + pipeline beat.PipelineConnector, configInputs *common.Config, configModules *common.Config, ) error { @@ -111,7 +111,7 @@ func (c *crawler) Start( } func (c *crawler) startInput( - pipeline beat.Pipeline, + pipeline beat.PipelineConnector, config *common.Config, ) error { if !config.Enabled() { diff --git a/filebeat/beater/filebeat.go b/filebeat/beater/filebeat.go index c339c0a6cc0..fb94a26762a 100644 --- a/filebeat/beater/filebeat.go +++ b/filebeat/beater/filebeat.go @@ -41,6 +41,7 @@ import ( "github.com/elastic/beats/v7/libbeat/management" "github.com/elastic/beats/v7/libbeat/monitoring" "github.com/elastic/beats/v7/libbeat/outputs/elasticsearch" + "github.com/elastic/beats/v7/libbeat/publisher/pipetool" _ "github.com/elastic/beats/v7/filebeat/include" @@ -66,6 +67,7 @@ type Filebeat struct { config *cfg.Config moduleRegistry *fileset.ModuleRegistry done chan struct{} + pipeline beat.PipelineConnector } // New creates a new Filebeat pointer instance. @@ -162,7 +164,7 @@ func (fb *Filebeat) setupPipelineLoaderCallback(b *beat.Beat) error { pipelineLoaderFactory := newPipelineLoaderFactory(b.Config.Output.Config()) modulesFactory := fileset.NewSetupFactory(b.Info, pipelineLoaderFactory) if fb.config.ConfigModules.Enabled() { - modulesLoader := cfgfile.NewReloader(b.Publisher, fb.config.ConfigModules) + modulesLoader := cfgfile.NewReloader(fb.pipeline, fb.config.ConfigModules) modulesLoader.Load(modulesFactory) } @@ -235,8 +237,11 @@ func (fb *Filebeat) Run(b *beat.Beat) error { return err } + fb.pipeline = pipetool.WithDefaultGuarantees(b.Publisher, beat.GuaranteedSend) + fb.pipeline = withPipelineEventCounter(fb.pipeline, wgEvents) + outDone := make(chan struct{}) // outDone closes down all active pipeline connections - pipelineConnector := channel.NewOutletFactory(outDone, wgEvents, b.Info).Create + pipelineConnector := channel.NewOutletFactory(outDone).Create // Create a ES connection factory for dynamic modules pipeline loading var pipelineLoaderFactory fileset.PipelineLoaderFactory @@ -246,7 +251,8 @@ func (fb *Filebeat) Run(b *beat.Beat) error { logp.Warn(pipelinesWarning) } - inputLoader := input.NewRunnerFactory(pipelineConnector, registrar, fb.done) + inputLoader := channel.RunnerFactoryWithCommonInputSettings(b.Info, + input.NewRunnerFactory(pipelineConnector, registrar, fb.done)) moduleLoader := fileset.NewFactory(inputLoader, b.Info, pipelineLoaderFactory, config.OverwritePipelines) crawler, err := newCrawler(inputLoader, moduleLoader, config.Inputs, fb.done, *once) @@ -283,7 +289,7 @@ func (fb *Filebeat) Run(b *beat.Beat) error { logp.Debug("modules", "Existing Ingest pipelines will be updated") } - err = crawler.Start(b.Publisher, config.ConfigInput, config.ConfigModules) + err = crawler.Start(fb.pipeline, config.ConfigInput, config.ConfigModules) if err != nil { crawler.Stop() return fmt.Errorf("Failed to start crawler: %+v", err) @@ -300,23 +306,24 @@ func (fb *Filebeat) Run(b *beat.Beat) error { } // Register reloadable list of inputs and modules - inputs := cfgfile.NewRunnerList(management.DebugK, inputLoader, b.Publisher) + inputs := cfgfile.NewRunnerList(management.DebugK, inputLoader, fb.pipeline) reload.Register.MustRegisterList("filebeat.inputs", inputs) - modules := cfgfile.NewRunnerList(management.DebugK, moduleLoader, b.Publisher) + modules := cfgfile.NewRunnerList(management.DebugK, moduleLoader, fb.pipeline) reload.Register.MustRegisterList("filebeat.modules", modules) var adiscover *autodiscover.Autodiscover if fb.config.Autodiscover != nil { adiscover, err = autodiscover.NewAutodiscover( "filebeat", - b.Publisher, + fb.pipeline, cfgfile.MultiplexedRunnerFactory( cfgfile.MatchHasField("module", moduleLoader), cfgfile.MatchDefault(inputLoader), ), autodiscover.QueryConfig(), config.Autodiscover, + b.Keystore, ) if err != nil { return err diff --git a/filebeat/channel/connector.go b/filebeat/channel/connector.go index 73da881ec32..279c50c58b0 100644 --- a/filebeat/channel/connector.go +++ b/filebeat/channel/connector.go @@ -20,9 +20,6 @@ package channel import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/common/fmtstr" - "github.com/elastic/beats/v7/libbeat/processors" - "github.com/elastic/beats/v7/libbeat/processors/add_formatted_index" ) // ConnectorFunc is an adapter for using ordinary functions as Connector. @@ -48,96 +45,15 @@ func (c *pipelineConnector) Connect(cfg *common.Config) (Outleter, error) { } func (c *pipelineConnector) ConnectWith(cfg *common.Config, clientCfg beat.ClientConfig) (Outleter, error) { - config := inputOutletConfig{} - if err := cfg.Unpack(&config); err != nil { - return nil, err - } - - procs, err := processorsForConfig(c.parent.beatInfo, config, clientCfg) - if err != nil { - return nil, err - } - - setOptional := func(to common.MapStr, key string, value string) { - if value != "" { - to.Put(key, value) - } - } - - meta := clientCfg.Processing.Meta.Clone() - fields := clientCfg.Processing.Fields.Clone() - - serviceType := config.ServiceType - if serviceType == "" { - serviceType = config.Module - } - - setOptional(meta, "pipeline", config.Pipeline) - setOptional(fields, "fileset.name", config.Fileset) - setOptional(fields, "service.type", serviceType) - setOptional(fields, "input.type", config.Type) - if config.Module != "" { - event := common.MapStr{"module": config.Module} - if config.Fileset != "" { - event["dataset"] = config.Module + "." + config.Fileset - } - fields["event"] = event - } - - mode := clientCfg.PublishMode - if mode == beat.DefaultGuarantees { - mode = beat.GuaranteedSend - } - // connect with updated configuration - clientCfg.PublishMode = mode - clientCfg.Processing.EventMetadata = config.EventMetadata - clientCfg.Processing.Meta = meta - clientCfg.Processing.Fields = fields - clientCfg.Processing.Processor = procs - clientCfg.Processing.KeepNull = config.KeepNull client, err := c.pipeline.ConnectWith(clientCfg) if err != nil { return nil, err } - outlet := newOutlet(client, c.parent.wgEvents) + outlet := newOutlet(client) if c.parent.done != nil { return CloseOnSignal(outlet, c.parent.done), nil } return outlet, nil } - -// processorsForConfig assembles the Processors for a pipelineConnector. -func processorsForConfig( - beatInfo beat.Info, config inputOutletConfig, clientCfg beat.ClientConfig, -) (*processors.Processors, error) { - procs := processors.NewList(nil) - - // Processor ordering is important: - // 1. Index configuration - if !config.Index.IsEmpty() { - staticFields := fmtstr.FieldsForBeat(beatInfo.Beat, beatInfo.Version) - timestampFormat, err := - fmtstr.NewTimestampFormatString(&config.Index, staticFields) - if err != nil { - return nil, err - } - indexProcessor := add_formatted_index.New(timestampFormat) - procs.AddProcessor(indexProcessor) - } - - // 2. ClientConfig processors - if lst := clientCfg.Processing.Processor; lst != nil { - procs.AddProcessor(lst) - } - - // 3. User processors - userProcessors, err := processors.New(config.Processors) - if err != nil { - return nil, err - } - procs.AddProcessors(*userProcessors) - - return procs, nil -} diff --git a/filebeat/channel/factory.go b/filebeat/channel/factory.go index b145c4a34f5..7962377856c 100644 --- a/filebeat/channel/factory.go +++ b/filebeat/channel/factory.go @@ -19,65 +19,17 @@ package channel import ( "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/common/fmtstr" - "github.com/elastic/beats/v7/libbeat/processors" ) type OutletFactory struct { done <-chan struct{} - - eventer beat.ClientEventer - wgEvents eventCounter - beatInfo beat.Info -} - -type eventCounter interface { - Add(n int) - Done() -} - -// clientEventer adjusts wgEvents if events are dropped during shutdown. -type clientEventer struct { - wgEvents eventCounter -} - -// inputOutletConfig defines common input settings -// for the publisher pipeline. -type inputOutletConfig struct { - // event processing - common.EventMetadata `config:",inline"` // Fields and tags to add to events. - Processors processors.PluginConfig `config:"processors"` - KeepNull bool `config:"keep_null"` - - // implicit event fields - Type string `config:"type"` // input.type - ServiceType string `config:"service.type"` // service.type - - // hidden filebeat modules settings - Module string `config:"_module_name"` // hidden setting - Fileset string `config:"_fileset_name"` // hidden setting - - // Output meta data settings - Pipeline string `config:"pipeline"` // ES Ingest pipeline name - Index fmtstr.EventFormatString `config:"index"` // ES output index pattern } // NewOutletFactory creates a new outlet factory for // connecting an input to the publisher pipeline. -func NewOutletFactory( - done <-chan struct{}, - wgEvents eventCounter, - beatInfo beat.Info, -) *OutletFactory { +func NewOutletFactory(done <-chan struct{}) *OutletFactory { o := &OutletFactory{ - done: done, - wgEvents: wgEvents, - beatInfo: beatInfo, - } - - if wgEvents != nil { - o.eventer = &clientEventer{wgEvents} + done: done, } return o @@ -90,9 +42,3 @@ func NewOutletFactory( func (f *OutletFactory) Create(p beat.PipelineConnector) Connector { return &pipelineConnector{parent: f, pipeline: p} } - -func (e *clientEventer) Closing() {} -func (e *clientEventer) Closed() {} -func (e *clientEventer) Published() {} -func (e *clientEventer) FilteredOut(evt beat.Event) {} -func (e *clientEventer) DroppedOnPublish(evt beat.Event) { e.wgEvents.Done() } diff --git a/filebeat/channel/outlet.go b/filebeat/channel/outlet.go index 3211a9d7293..fd5c9b12fc1 100644 --- a/filebeat/channel/outlet.go +++ b/filebeat/channel/outlet.go @@ -23,15 +23,13 @@ import ( ) type outlet struct { - wg eventCounter client beat.Client isOpen atomic.Bool done chan struct{} } -func newOutlet(client beat.Client, wg eventCounter) *outlet { +func newOutlet(client beat.Client) *outlet { o := &outlet{ - wg: wg, client: client, isOpen: atomic.MakeBool(true), done: make(chan struct{}), @@ -57,10 +55,6 @@ func (o *outlet) OnEvent(event beat.Event) bool { return false } - if o.wg != nil { - o.wg.Add(1) - } - o.client.Publish(event) // Note: race condition on shutdown: diff --git a/filebeat/channel/runner.go b/filebeat/channel/runner.go new file mode 100644 index 00000000000..941e1cc8161 --- /dev/null +++ b/filebeat/channel/runner.go @@ -0,0 +1,201 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package channel + +import ( + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/cfgfile" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/fmtstr" + "github.com/elastic/beats/v7/libbeat/processors" + "github.com/elastic/beats/v7/libbeat/processors/add_formatted_index" + "github.com/elastic/beats/v7/libbeat/publisher/pipetool" +) + +type onCreateFactory struct { + factory cfgfile.RunnerFactory + create onCreateWrapper +} + +type onCreateWrapper func(cfgfile.RunnerFactory, beat.PipelineConnector, *common.Config, *common.MapStrPointer) (cfgfile.Runner, error) + +// commonInputConfig defines common input settings +// for the publisher pipeline. +type commonInputConfig struct { + // event processing + common.EventMetadata `config:",inline"` // Fields and tags to add to events. + Processors processors.PluginConfig `config:"processors"` + KeepNull bool `config:"keep_null"` + + PublisherPipeline struct { + DisableHost bool `config:"disable_host"` // Disable addition of host.name. + } `config:"publisher_pipeline"` + + // implicit event fields + Type string `config:"type"` // input.type + ServiceType string `config:"service.type"` // service.type + + // hidden filebeat modules settings + Module string `config:"_module_name"` // hidden setting + Fileset string `config:"_fileset_name"` // hidden setting + + // Output meta data settings + Pipeline string `config:"pipeline"` // ES Ingest pipeline name + Index fmtstr.EventFormatString `config:"index"` // ES output index pattern +} + +func (f *onCreateFactory) CheckConfig(cfg *common.Config) error { + return f.factory.CheckConfig(cfg) +} + +func (f *onCreateFactory) Create( + pipeline beat.PipelineConnector, + cfg *common.Config, + meta *common.MapStrPointer, +) (cfgfile.Runner, error) { + return f.create(f.factory, pipeline, cfg, meta) +} + +// RunnerFactoryWithCommonInputSettings wraps a runner factory, such that all runners +// created by this factory have the same processing capabilities and related +// configuration file settings. +// +// Common settings ensured by this factory wrapper: +// - *fields*: common fields to be added to the pipeline +// - *fields_under_root*: select at which level to store the fields +// - *tags*: add additional tags to the events +// - *processors*: list of local processors to be added to the processing pipeline +// - *keep_null*: keep or remove 'null' from events to be published +// - *_module_name* (hidden setting): Add fields describing the module name +// - *_ fileset_name* (hiddrn setting): +// - *pipeline*: Configure the ES Ingest Node pipeline name to be used for events from this input +// - *index*: Configure the index name for events to be collected from this input +// - *type*: implicit event type +// - *service.type*: implicit event type +func RunnerFactoryWithCommonInputSettings(info beat.Info, f cfgfile.RunnerFactory) cfgfile.RunnerFactory { + return wrapRunnerCreate(f, + func( + f cfgfile.RunnerFactory, + pipeline beat.PipelineConnector, + cfg *common.Config, + meta *common.MapStrPointer, + ) (runner cfgfile.Runner, err error) { + pipeline, err = withClientConfig(info, pipeline, cfg) + if err != nil { + return nil, err + } + + return f.Create(pipeline, cfg, meta) + }) +} + +func wrapRunnerCreate(f cfgfile.RunnerFactory, edit onCreateWrapper) cfgfile.RunnerFactory { + return &onCreateFactory{factory: f, create: edit} +} + +// withClientConfig reads common Beat input instance configurations from the +// configuration object and ensure that the settings are applied to each client. +func withClientConfig( + beatInfo beat.Info, + pipeline beat.PipelineConnector, + cfg *common.Config, +) (beat.PipelineConnector, error) { + editor, err := newCommonConfigEditor(beatInfo, cfg) + if err != nil { + return nil, err + } + return pipetool.WithClientConfigEdit(pipeline, editor), nil +} + +func newCommonConfigEditor( + beatInfo beat.Info, + cfg *common.Config, +) (pipetool.ConfigEditor, error) { + config := commonInputConfig{} + if err := cfg.Unpack(&config); err != nil { + return nil, err + } + + var indexProcessor processors.Processor + if !config.Index.IsEmpty() { + staticFields := fmtstr.FieldsForBeat(beatInfo.Beat, beatInfo.Version) + timestampFormat, err := + fmtstr.NewTimestampFormatString(&config.Index, staticFields) + if err != nil { + return nil, err + } + indexProcessor = add_formatted_index.New(timestampFormat) + } + + userProcessors, err := processors.New(config.Processors) + if err != nil { + return nil, err + } + + serviceType := config.ServiceType + if serviceType == "" { + serviceType = config.Module + } + + return func(clientCfg beat.ClientConfig) (beat.ClientConfig, error) { + meta := clientCfg.Processing.Meta.Clone() + fields := clientCfg.Processing.Fields.Clone() + + setOptional(meta, "pipeline", config.Pipeline) + setOptional(fields, "fileset.name", config.Fileset) + setOptional(fields, "service.type", serviceType) + setOptional(fields, "input.type", config.Type) + if config.Module != "" { + event := common.MapStr{"module": config.Module} + if config.Fileset != "" { + event["dataset"] = config.Module + "." + config.Fileset + } + fields["event"] = event + } + + // assemble the processors. Ordering is important. + // 1. add support for index configuration via processor + // 2. add processors added by the input that wants to connect + // 3. add locally configured processors from the 'processors' settings + procs := processors.NewList(nil) + if indexProcessor != nil { + procs.AddProcessor(indexProcessor) + } + if lst := clientCfg.Processing.Processor; lst != nil { + procs.AddProcessor(lst) + } + if userProcessors != nil { + procs.AddProcessors(*userProcessors) + } + + clientCfg.Processing.EventMetadata = config.EventMetadata + clientCfg.Processing.Meta = meta + clientCfg.Processing.Fields = fields + clientCfg.Processing.Processor = procs + clientCfg.Processing.KeepNull = config.KeepNull + clientCfg.Processing.DisableHost = config.PublisherPipeline.DisableHost + + return clientCfg, nil + }, nil +} + +func setOptional(to common.MapStr, key string, value string) { + if value != "" { + to.Put(key, value) + } +} diff --git a/filebeat/channel/connector_test.go b/filebeat/channel/runner_test.go similarity index 90% rename from filebeat/channel/connector_test.go rename to filebeat/channel/runner_test.go index fe6e3299188..101904b9260 100644 --- a/filebeat/channel/connector_test.go +++ b/filebeat/channel/runner_test.go @@ -23,6 +23,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" @@ -109,16 +110,22 @@ func TestProcessorsForConfig(t *testing.T) { if test.event.Fields == nil { test.event.Fields = common.MapStr{} } - config, err := outletConfigFromString(test.configStr) + config, err := common.NewConfigFrom(test.configStr) if err != nil { t.Errorf("[%s] %v", description, err) continue } - processors, err := processorsForConfig(test.beatInfo, config, test.clientCfg) + + editor, err := newCommonConfigEditor(test.beatInfo, config) if err != nil { t.Errorf("[%s] %v", description, err) continue } + + clientCfg, err := editor(test.clientCfg) + require.NoError(t, err) + + processors := clientCfg.Processing.Processor processedEvent, err := processors.Run(&test.event) // We don't check if err != nil, because we are testing the final outcome // of running the processors, including when some of them fail. @@ -160,16 +167,21 @@ func TestProcessorsForConfigIsFlat(t *testing.T) { configStr := `processors: - add_fields: {fields: {testField: value}} - add_fields: {fields: {testField2: stuff}}` - config, err := outletConfigFromString(configStr) + config, err := common.NewConfigFrom(configStr) if err != nil { t.Fatal(err) } - processors, err := processorsForConfig( - beat.Info{}, config, beat.ClientConfig{}) + + editor, err := newCommonConfigEditor(beat.Info{}, config) if err != nil { t.Fatal(err) } - assert.Equal(t, 2, len(processors.List)) + + clientCfg, err := editor(beat.ClientConfig{}) + require.NoError(t, err) + + lst := clientCfg.Processing.Processor + assert.Equal(t, 2, len(lst.(*processors.Processors).List)) } // setRawIndex is a bare-bones processor to set the raw_index field to a @@ -191,20 +203,6 @@ func (p *setRawIndex) String() string { return fmt.Sprintf("set_raw_index=%v", p.indexStr) } -// Helper function to convert from YML input string to an unpacked -// inputOutletConfig -func outletConfigFromString(s string) (inputOutletConfig, error) { - config := inputOutletConfig{} - cfg, err := common.NewConfigFrom(s) - if err != nil { - return config, err - } - if err := cfg.Unpack(&config); err != nil { - return config, err - } - return config, nil -} - // makeProcessors wraps one or more bare Processor objects in Processors. func makeProcessors(procs ...processors.Processor) *processors.Processors { procList := processors.NewList(nil) diff --git a/filebeat/docs/fields.asciidoc b/filebeat/docs/fields.asciidoc index 5e163ab76c5..34d6b83ab7b 100644 --- a/filebeat/docs/fields.asciidoc +++ b/filebeat/docs/fields.asciidoc @@ -48,7 +48,6 @@ grouped in the following categories: * <> * <> * <> -* <> * <> * <> * <> @@ -1405,6 +1404,17 @@ type: boolean Fields for AWS CloudWatch logs. + +*`aws.cloudwatch.message`*:: ++ +-- +CloudWatch log message. + + +type: text + +-- + [float] === ec2 @@ -1664,6 +1674,7 @@ type: keyword -- The error reason if the executed action failed. + type: keyword -- @@ -3101,7 +3112,8 @@ Contains common beat fields available in all event types. *`agent.hostname`*:: + -- -Hostname of the agent. +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. + type: keyword @@ -4140,7 +4152,7 @@ type: date *`cef.extensions.deviceTimeZone`*:: + -- -The timezone for the device generating the event. +The time zone for the device generating the event. type: keyword @@ -26136,12 +26148,6 @@ type: short -- -[[exported-fields-netflow-module]] -== NetFlow fields - -Module for receiving NetFlow and IPFIX flow records over UDP. The module does not add fields beyond what the netflow input provides. - - [[exported-fields-nginx]] == Nginx fields @@ -26446,7 +26452,7 @@ format: duration The name of the upstream. -type: text +type: keyword -- @@ -26456,7 +26462,7 @@ type: text The name of the alternative upstream. -type: text +type: keyword -- @@ -26500,7 +26506,7 @@ type: long The randomly generated ID of the request -type: text +type: keyword -- @@ -28240,6 +28246,15 @@ type: keyword Palo Alto Networks name for the threat. +type: keyword + +-- + +*`panw.panos.action`*:: ++ +-- +Action taken for the session. + type: keyword -- @@ -28687,7 +28702,7 @@ The disk volume path. -- -*`certificate.common_name`*:: +*`santa.certificate.common_name`*:: + -- Common name from code signing certificate. @@ -28696,7 +28711,7 @@ type: keyword -- -*`certificate.sha256`*:: +*`santa.certificate.sha256`*:: + -- SHA256 hash of code signing certificate. diff --git a/filebeat/docs/filebeat-filtering.asciidoc b/filebeat/docs/filebeat-filtering.asciidoc index 87eaf2ce72e..b0d7efab0dc 100644 --- a/filebeat/docs/filebeat-filtering.asciidoc +++ b/filebeat/docs/filebeat-filtering.asciidoc @@ -37,10 +37,10 @@ The following configuration drops all the DEBUG messages. [source,yaml] ----------------------------------------------------- processors: - - drop_event: - when: + - drop_event: + when: regexp: - message: "^DBG:" + message: "^DBG:" ----------------------------------------------------- To drop all the log messages coming from a certain log file: @@ -48,10 +48,10 @@ To drop all the log messages coming from a certain log file: [source,yaml] ---------------- processors: - - drop_event: - when: + - drop_event: + when: contains: - source: "test" + source: "test" ---------------- [float] diff --git a/filebeat/docs/include/timezone-support.asciidoc b/filebeat/docs/include/timezone-support.asciidoc index e49bc860e73..50370e18a6c 100644 --- a/filebeat/docs/include/timezone-support.asciidoc +++ b/filebeat/docs/include/timezone-support.asciidoc @@ -1,17 +1,17 @@ [float] -==== Timezone support +==== Time zone support -This module parses logs that don't contain timezone information. For these logs, -Filebeat reads the local timezone and uses it when parsing to convert the -timestamp to UTC. The timezone to be used for parsing is included in the event +This module parses logs that don't contain time zone information. For these logs, +Filebeat reads the local time zone and uses it when parsing to convert the +timestamp to UTC. The time zone to be used for parsing is included in the event in the `event.timezone` field. To disable this conversion, the `event.timezone` field can be removed with the `drop_fields` processor. -If logs are originated from systems or applications with a different timezone to +If logs are originated from systems or applications with a different time zone to the local one, the `event.timezone` field can be overwritten with the original -timezone using the `add_fields` processor. +time zone using the `add_fields` processor. See <> for information about specifying processors in your config. diff --git a/filebeat/docs/inputs/input-common-options.asciidoc b/filebeat/docs/inputs/input-common-options.asciidoc index 8e08a8074e4..de22a519846 100644 --- a/filebeat/docs/inputs/input-common-options.asciidoc +++ b/filebeat/docs/inputs/input-common-options.asciidoc @@ -101,3 +101,9 @@ version and the event timestamp; for access to dynamic fields, use Example value: `"%{[agent.name]}-myindex-%{+yyyy.MM.dd}"` might expand to `"filebeat-myindex-2019.11.01"`. + +[float] +===== `publisher_pipeline.disable_host` + +By default, all events contain `host.name`. This option can be set to `true` to +disable the addition of this field to all events. The default value is `false`. diff --git a/filebeat/docs/inputs/input-common-unix-options.asciidoc b/filebeat/docs/inputs/input-common-unix-options.asciidoc index 443fe761274..f73278944a6 100644 --- a/filebeat/docs/inputs/input-common-unix-options.asciidoc +++ b/filebeat/docs/inputs/input-common-unix-options.asciidoc @@ -16,6 +16,22 @@ The maximum size of the message received over the socket. The default is `20MiB` The path to the Unix socket that will receive event streams. +[float] +[id="{beatname_lc}-input-{type}-unix-group"] +==== `group` + +The group ownership of the Unix socket that will be created by Filebeat. +The default is the primary group name for the user Filebeat is running as. +This option is ignored on Windows. + +[float] +[id="{beatname_lc}-input-{type}-unix-mode"] +==== `mode` + +The file mode of the Unix socket that will be created by Filebeat. This is +expected to be a file mode as an octal string. The default value is the system +default (generally `0755`). + [float] [id="{beatname_lc}-input-{type}-unix-line-delimiter"] ==== `line_delimiter` diff --git a/filebeat/docs/inputs/input-syslog.asciidoc b/filebeat/docs/inputs/input-syslog.asciidoc index 0c360a03f7f..f9a24c04b81 100644 --- a/filebeat/docs/inputs/input-syslog.asciidoc +++ b/filebeat/docs/inputs/input-syslog.asciidoc @@ -51,6 +51,8 @@ include::../inputs/input-common-tcp-options.asciidoc[] ===== Protocol `unix`: +beta[] + include::../inputs/input-common-unix-options.asciidoc[] [id="{beatname_lc}-input-{type}-common-options"] diff --git a/filebeat/docs/inputs/input-unix.asciidoc b/filebeat/docs/inputs/input-unix.asciidoc new file mode 100644 index 00000000000..a2f445159b7 --- /dev/null +++ b/filebeat/docs/inputs/input-unix.asciidoc @@ -0,0 +1,35 @@ +:type: unix + +[id="{beatname_lc}-input-{type}"] +=== Unix input + +beta[] + +++++ +Unix +++++ + +Use the `unix` input to read events over a stream-oriented Unix domain socket. + +Example configuration: + +["source","yaml",subs="attributes"] +---- +{beatname_lc}.inputs: +- type: unix + max_message_size: 10MiB + path: "/var/run/filebeat.sock" +---- + + +==== Configuration options + +The `unix` input supports the following configuration options plus the +<<{beatname_lc}-input-{type}-common-options>> described later. + +include::../inputs/input-common-unix-options.asciidoc[] + +[id="{beatname_lc}-input-{type}-common-options"] +include::../inputs/input-common-options.asciidoc[] + +:type!: diff --git a/filebeat/docs/kubernetes-default-indexers-matchers.asciidoc b/filebeat/docs/kubernetes-default-indexers-matchers.asciidoc index 3d7f8655cc6..287b3bdbb3c 100644 --- a/filebeat/docs/kubernetes-default-indexers-matchers.asciidoc +++ b/filebeat/docs/kubernetes-default-indexers-matchers.asciidoc @@ -8,7 +8,7 @@ configuration: [source,yaml] ------------------------------------------------------------------------------- processors: -- add_kubernetes_metadata: - default_indexers.enabled: false - default_matchers.enabled: false + - add_kubernetes_metadata: + default_indexers.enabled: false + default_matchers.enabled: false ------------------------------------------------------------------------------- diff --git a/filebeat/docs/modules/activemq.asciidoc b/filebeat/docs/modules/activemq.asciidoc index c276cd63952..225090f80ef 100644 --- a/filebeat/docs/modules/activemq.asciidoc +++ b/filebeat/docs/modules/activemq.asciidoc @@ -10,8 +10,6 @@ This file is generated! See scripts/docs_collector.py == ActiveMQ module -ga[] - This module parses Apache ActiveMQ logs. It supports application and audit logs. include::../include/what-happens.asciidoc[] diff --git a/filebeat/docs/modules/cisco.asciidoc b/filebeat/docs/modules/cisco.asciidoc index 14d571e6172..e252aacbf68 100644 --- a/filebeat/docs/modules/cisco.asciidoc +++ b/filebeat/docs/modules/cisco.asciidoc @@ -294,7 +294,7 @@ parameters on your Elasticsearch cluster: - {ref}/circuit-breaker.html#script-compilation-circuit-breaker[script.max_compilations_rate]: Increase to at least `100/5m`. -- {ref}/modules-scripting-using.html#modules-scripting-using-caching[script.cache_max_size]: +- {ref}/modules-scripting-using.html#modules-scripting-using-caching[script.cache.max_size]: Increase to at least `200` if using both filesets or other script-heavy modules. [float] diff --git a/filebeat/docs/running-on-cloudfoundry.asciidoc b/filebeat/docs/running-on-cloudfoundry.asciidoc index 34c225ed831..ae9603dc012 100644 --- a/filebeat/docs/running-on-cloudfoundry.asciidoc +++ b/filebeat/docs/running-on-cloudfoundry.asciidoc @@ -1,5 +1,5 @@ [[running-on-cloudfoundry]] -=== Running {beatname_uc} on Cloud Foundry +=== Run {beatname_uc} on Cloud Foundry You can use {beatname_uc} on Cloud Foundry to retrieve and ship logs. @@ -14,18 +14,19 @@ endif::[] [float] ==== Cloud Foundry credentials -{beatname_uc} needs credentials created with UAA so it can connect to loggregator to receive the logs. The uaac +{beatname_uc} needs credentials created with UAA so it can connect to loggregator to receive the logs. The `uaac` command will create the required credentials for connecting to loggregator. -["source", "sh"] +["source","sh",subs="attributes"] ------------------------------------------------ uaac client add {beatname_lc} --name {beatname_lc} --secret changeme --authorized_grant_types client_credentials,refresh_token --authorities doppler.firehose,cloud_controller.admin_read_only ------------------------------------------------ [WARNING] ======================================= -*Use a unique secret:* The uaac command above is just an example and the secret should be changed and the -`{beatname_lc}.yml` should be updated with your choosen secret. +*Use a unique secret:* The `uaac` command shown here is an example. Remember to +replace `changeme` with your secret, and update the +{beatname_lc}.yml+ file to +use your chosen secret. ======================================= diff --git a/filebeat/docs/running-on-kubernetes.asciidoc b/filebeat/docs/running-on-kubernetes.asciidoc index f104a06e245..0df3c811a95 100644 --- a/filebeat/docs/running-on-kubernetes.asciidoc +++ b/filebeat/docs/running-on-kubernetes.asciidoc @@ -1,5 +1,5 @@ [[running-on-kubernetes]] -=== Running {beatname_uc} on Kubernetes +=== Run {beatname_uc} on Kubernetes You can use {beatname_uc} <> on Kubernetes to retrieve and ship container logs. diff --git a/filebeat/filebeat.reference.yml b/filebeat/filebeat.reference.yml index af2831f8848..e34e85fd810 100644 --- a/filebeat/filebeat.reference.yml +++ b/filebeat/filebeat.reference.yml @@ -384,6 +384,7 @@ filebeat.modules: #input: + #=========================== Filebeat inputs ============================= # List of inputs to fetch data. @@ -451,6 +452,11 @@ filebeat.inputs: # Set to true to publish fields with null values in events. #keep_null: false + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + # Ignore files which were modified more then the defined timespan in the past. # ignore_older is disabled by default, so no files are ignored by setting it to 0. # Time strings like 2h (2 hours), 5m (5 minutes) can be used. @@ -760,7 +766,8 @@ filebeat.inputs: # Configure stream to filter to a specific stream: stdout, stderr or all (default) #stream: all -#========================== Filebeat autodiscover ============================== + +# =========================== Filebeat autodiscover ============================ # Autodiscover allows you to detect changes in the system and spawn new modules # or inputs as they happen. @@ -777,7 +784,7 @@ filebeat.inputs: # paths: # - /var/lib/docker/containers/${data.docker.container.id}/*.log -#========================= Filebeat global options ============================ +# ========================== Filebeat global options =========================== # Registry data path. If a relative path is used, it is considered relative to the # data path. @@ -824,7 +831,8 @@ filebeat.inputs: #reload.enabled: true #reload.period: 10s -#================================ General ====================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -932,7 +940,7 @@ filebeat.inputs: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -949,153 +957,153 @@ filebeat.inputs: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Filebeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -1108,11 +1116,11 @@ filebeat.inputs: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -1232,7 +1240,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -1346,7 +1375,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -1500,6 +1529,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -1522,7 +1554,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -1640,7 +1672,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -1674,7 +1706,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -1687,7 +1719,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Filebeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -1713,11 +1745,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -1761,8 +1795,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1816,7 +1849,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1830,13 +1863,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'filebeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "filebeat" +#setup.ilm.rollover_alias: 'filebeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'filebeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1851,7 +1884,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1906,9 +1939,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1975,8 +2007,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Filebeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -2086,6 +2117,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -2097,7 +2149,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -2121,12 +2174,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/filebeat/filebeat.yml b/filebeat/filebeat.yml index 581e1a43f23..51a0d40224e 100644 --- a/filebeat/filebeat.yml +++ b/filebeat/filebeat.yml @@ -10,7 +10,7 @@ # For more available modules and options, please see the filebeat.reference.yml sample # configuration file. -#=========================== Filebeat inputs ============================= +# ============================== Filebeat inputs =============================== filebeat.inputs: @@ -62,8 +62,7 @@ filebeat.inputs: # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after - -#============================= Filebeat modules =============================== +# ============================== Filebeat modules ============================== filebeat.config.modules: # Glob pattern for configuration loading @@ -75,14 +74,15 @@ filebeat.config.modules: # Period on which files under path should be checked for changes #reload.period: 10s -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false -#================================ General ===================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -97,8 +97,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -110,7 +109,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -127,7 +126,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Filebeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -140,11 +139,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -157,7 +156,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -172,7 +171,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -182,7 +181,8 @@ processors: - add_docker_metadata: ~ - add_kubernetes_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -193,8 +193,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# filebeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Filebeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -215,7 +215,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/filebeat/fileset/fileset_test.go b/filebeat/fileset/fileset_test.go index 59e5862eb2f..e7865074d8d 100644 --- a/filebeat/fileset/fileset_test.go +++ b/filebeat/fileset/fileset_test.go @@ -56,7 +56,7 @@ func TestLoadManifestNginx(t *testing.T) { manifest, err := fs.readManifest() assert.NoError(t, err) assert.Equal(t, manifest.ModuleVersion, "1.0") - assert.Equal(t, manifest.IngestPipeline, []string{"ingest/default.json"}) + assert.Equal(t, manifest.IngestPipeline, []string{"ingest/pipeline.yml"}) assert.Equal(t, manifest.Input, "config/nginx-access.yml") vars := manifest.Vars @@ -189,7 +189,7 @@ func TestGetInputConfigNginx(t *testing.T) { assert.True(t, cfg.HasField("pipeline")) pipelineID, err := cfg.String("pipeline", -1) assert.NoError(t, err) - assert.Equal(t, "filebeat-5.2.0-nginx-access-default", pipelineID) + assert.Equal(t, "filebeat-5.2.0-nginx-access-pipeline", pipelineID) } func TestGetInputConfigNginxOverrides(t *testing.T) { @@ -217,7 +217,7 @@ func TestGetInputConfigNginxOverrides(t *testing.T) { pipelineID, err := c.String("pipeline", -1) assert.NoError(t, err) - assert.Equal(t, "filebeat-5.2.0-nginx-access-default", pipelineID) + assert.Equal(t, "filebeat-5.2.0-nginx-access-pipeline", pipelineID) }, }, "pipeline": { @@ -276,7 +276,7 @@ func TestGetPipelineNginx(t *testing.T) { assert.Len(t, pipelines, 1) pipeline := pipelines[0] - assert.Equal(t, "filebeat-5.2.0-nginx-access-default", pipeline.id) + assert.Equal(t, "filebeat-5.2.0-nginx-access-pipeline", pipeline.id) assert.Contains(t, pipeline.contents, "description") assert.Contains(t, pipeline.contents, "processors") } diff --git a/filebeat/fileset/modules_integration_test.go b/filebeat/fileset/modules_integration_test.go index 8c5bc91bf70..5428fb1f549 100644 --- a/filebeat/fileset/modules_integration_test.go +++ b/filebeat/fileset/modules_integration_test.go @@ -115,7 +115,7 @@ func TestSetupNginx(t *testing.T) { t.Fatal(err) } - status, _, _ := client.Request("GET", "/_ingest/pipeline/filebeat-5.2.0-nginx-access-default", "", nil, nil) + status, _, _ := client.Request("GET", "/_ingest/pipeline/filebeat-5.2.0-nginx-access-pipeline", "", nil, nil) assert.Equal(t, 200, status) status, _, _ = client.Request("GET", "/_ingest/pipeline/filebeat-5.2.0-nginx-error-pipeline", "", nil, nil) assert.Equal(t, 200, status) diff --git a/filebeat/include/fields.go b/filebeat/include/fields.go index 839cce5e151..e0ea9afb7e4 100644 --- a/filebeat/include/fields.go +++ b/filebeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "eJzsvXtTHLmSOPr/fApdNuKHOdsUD4ONuXcjfgwwM8TamDH4zJ5Zb9DqKnW3DlVSjaQC92zsd7+hTEmlegCNTfkxy5zdGbq7SkqlUql857+Q3w7enZ6c/vz/kCNJhDSEZdwQM+eaTHnOSMYVS02+GBFuyA3VZMYEU9SwjEwWxMwZOT48J6WS/2SpGf3wL2RCNcuIFPD9NVOaS0G2kt1kM/nhX8hZzqhm5JprbsjcmFLvb2zMuJlXkySVxQbLqTY83WCpJkYSXc1mTBuSzqmYMfjKDjvlLM908sMP6+SKLfYJS/UPhBhucrZvH/iBkIzpVPHScCngK/KTe4e4t/d/IGSdCFqwfbL6fw0vmDa0KFd/IISQnF2zfJ+kUjH4rNgfFVcs2ydGVfiVWZRsn2TU4MfGfKtH1LANOya5mTMBaGLXTBgiFZ9xYdGX/ADvEXJhcc01PJSF99hHo2hq0TxVsqhHGNmJeUrzfEEUKxXTTBguZjCRG7GernfDtKxUysL8J9PoBfyNzKkmQnpocxLQM0LSuKZ5xQDoAEwpyyq307hh3WRTrrSB91tgKZYyfl1DVfKS5VzUcL1zOMf9IlOpCM1zHEEnuE/sIy1Ku+mr25tbL9Y3d9e3n19s7u1v7u4/30n2dp//vhptc04nLNe9G4y7KSeWiuEL/PMSv79iixupsp6NPqy0kYV9YANxUlKudFjDIRVkwkhlj4SRhGYZKZihhIupVAW1g9jv3ZrI+VxWeQbHMJXCUC6IYNpuHYID5Gv/Ochz3ANNqGJEG2kRRbWHNABw7BE0zmR6xdSYUJGR8dWeHjt0dDD53yu0LHOeAnQr+2RlKuX6hKqVEVlh4tp+UyqZVSn8/j8xggumNZ2xOzBs2EfTg8afpCK5nDlEAD24sdzuO3TgT/ZJ9/OIyNLwgv8Z6M7SyTVnN/ZMcEEoPG2/YCpgxU6njapSU1m85XKmyQ03c1kZQkVN9g0YRkSaOVOOfZAUtzaVIqWGiYjyjbRAFISSeVVQsa4YzegkZ0RXRUHVgsjoxMXHsKhyw8s8rF0T9pFre+TnbFFPWEy4YBnhwkgiRXi6vZG/sDyX5Dep8izaIkNnd52AmNL5TEjFLulEXrN9srW5vdPduddcG7se954OpG7ojDCazv0qmzT2nzEJIV1tr/xXTEp0xgRSimPrB+GLmZJVuU+2e+joYs7wzbBL7hg55koJndhNRjY4NTf29FgGauwFN3VbQcXC4pzaU5jn9tyNSMYM/iEVkRPN1LXdHiRXaclsLu1OSUUMvWKaFIzqSrHCPuCGDY+1T6cmXKR5lTHyI6OWD8BaNSnogtBcS6IqYd928yqdwI0GC03+5pbqhtRzyyQnrObHQNkWfspz7WkPkaQqIew5kYggC1u0PuWGvJkzFXPvOS1LZinQLhZOalgqcHaLAOGocSqlEdLYPfeL3ScnOF1qJQE5xUXDubUHcVTDl1hSIE4SmTBqkuj8Hpy9AZnE3ZzNBbkdp2W5YZfCU5aQmjZi7ptJ5lEHbBcEDcKnSC1cE3u/EjNXsprNyR8Vq+z4eqENKzTJ+RUj/06nV3RE3rGMI32USqZMay5mflPc47pK55ZLv5YzbaieE1wHOQd0O5ThQQQiRxQGcaU+Haycs4Ipml9yz3XceWYfDRNZzYs6p/rWc90+S8d+DsIze0SmnCkkH64dIp/xKXAgYFN6LdC1F2rsVaYKEA+8BEdTJbW9/bWhyp6nSWXIGLebZ2PYD7sTDhkR09ijO9Pdzc1pAxHt5Qd29llLfy/4H1a+efi6w31rSRQJG967gYt9wgiQMc9uXV7WWJ799xALdGILnK+YI3R2UBOKTyE7xCtoxq8ZyC1UuNfwaffznOXltMrtIbKH2q0wDGxuJPnJHWjChTZUpE6OafEjbScGpmSJxF2npL5OWUkVnOIwNtdEMJahAnIz5+m8O1U42aks7GRWvo7WfTK1kq/nPLBUZEn+Kzk1TJCcTQ1hRWkW3a2cStnYRbtRQ+zixaK8Y/s8t7MTEG3oQhOa39j/BNxaWVDPPWnitjpxHN+1t3lSo0YEnh2wWj+LJO6mmLD6EbjC+LSx8fWOtQmgsfkFTedWJ+iiOB7H49lpmwOg+u9Oj20iuwXTi2Qz2VxX6XYsxuiGDFMZKWQhK03O4Uq4R545EITWr+AtQp4dnK/hwXTSiQMslUIw0BhPhGFKMEPOlDQylbmD9NnJ2RpRsgJ9sVRsyj8yTSqRMbzIrbCkZG4Hs9xNKlJIxYhg5kaqKyJLq0dKZQUer+SxOc2n9gVK7H2XM0KzgguujT2Z1164smNlskBJjBri9FZcRFFIMSJpzqjKFwH7UxByA7Qy5+kCBMs5s6IvLDBZ+sIUVTEJAs1dV2Uuw63d2Ap3JeA4VhGVKQhXDqLONjl5I3wdCN7tohvo2cH56RqpYPB8Ud84GoXngHo8EyeNdUekt7W79eJVY8FSzajgfwJ7TLrXyOeICaCmXMZYjlid1+9IV+UjIGOpQu+TKc11fSNkbEqr3OCQzR8be/A2WhPM18HDz1JaGnz9+jA6g2nOW7rEYf3NHcrEgXvTHjZPj1Q7AuSG27OApO+3yR1BC95UempzSoJiM6oyEB6tbCiFHkXPo+A44Whu49Jqn9Nc3hDFUqtXNVTXi8MzNyreTDWYHdjsF/bxCDI4gJqJoDLYZ87/cUpKml4x80yvJTALarulYyGdqdCsZEW7xqRe11FgM2PawuGkcY8lo6jQFIBJyLksWJCPK416hmGqICveVibVSq1ZKzb13MqBIloL1Hj03M9OD8SdnbCgB4EeGCHAHUsLlpj5ba6niOFHjdYRkZ/A3l6VrixC3Ki1AsaFBe+flcANAH0MNSxvyewZrMavkKYzpBWscL/W4UR7E1IwPOF4G36eYCqEw4OiGs0yollBheEp8H720Tipjn1EeX2EQpTnCDrIdkaSa26Xy/9ktXJtF8oUKNyam4q67TiZkoWsVJhjSvPcE5+/ESw3nUm1GNlHvVCiDc9zwoRVLx3don3SCi4Z08aSh0WpRdiU53lgaLQslSwVp4bliwcoVjTLFNN6KJ0KqB21aEdbbkIn/wQ2U0z4rJKVzhdIzfBOYJg3Fi1aFgzssiTnGuxWJ2cjQv09KxWh9mL5SLS0dJIQ8o8as05MA8Nhza/njCh642HydD9O3BdjRFlTyhRWCa+FyKxC2yFejeOEl2MLyjhBsMYjkrGSicyJ+SijS1EDASq927Faikr+113gVCdPd3gE1WRhmL5HtI/2Hi08zdcagPxof0DrTvCwuDPpSAJZZ3er9nYagCFhD6B0OB6O4yeNOWdMJik3i8uBDASHVmbv3Z03VkdgNO+CI4XhggkzFEynkbEiTNaB71QqMycHBVM8pT1AVsKoxSXX8jKV2SCowynIyflbYqfoQHh4cCtYQ+2mA6l3Qw+poFkXU8Ae71emZ0xelpKHu6npHJBixk2V4X2dUwMfOhCs/jdZycHVtP7yefJia2fv+eaIrOTUrOyTnd1kd3P31dYe+Z/VDpCPyxNbNkDN1Lq/j6OfUOL36BkRZwNBKUxOyUxRUeVUcbOIL9YFSe0FD2JndIEe+nszWJiQwrlCiSpl9sZwwvc0l1K5i2cEFpU5r0Xb+oZC8HJSzhea2z+8hyP1x1pHIJxKE7lxwX/D0e5QwAU5Y9KvtmuHmUhtpFjP0s7eKDbjUgx50t7BDHcdtPVfD2+Da6Cj5mDqPWm/VmzCmoji5T0whAeaxHlyFoQ0zxHhsogpC42x3pDjXYsnZ9c79ouTs+sXtfDZkrcKmg6AmzcHh7dBTRo2b5O08dJ7rG/BzYVVL1FLOjmzEzmdAQNTTg8uggJOnrFkljhrEs1jQwFBbdMbmhqujXBWIp3TKrVgfhQzkkuakQnNqUjh6E65YjdW5QEdX8nKnugWxu2iS6nMwwRcL+Roo3i/1Btjw47/veADddsHyHuNVZ/h258k3W034ejsyTJC5+37ceb24Dbit9xJG6ZYdtknVz7e9WaVmzmfzZk20aQeRzj3CBZSlizzIOtq4sXRsP8/1T4evKai4ZwuOpUKwkiSGcj2SSqLFcI1WYk+t11PGE7jXEoZM0wVcBWXiqVcW10L7CgUtV9wxEIYUTXJeUp0NZ3yj2FEeObZ3Jhyf2MDH8EnrI61lpALtbCUaiQaDj5ye/Xh9TpZEM2LMl8QQ6/qXUVtOafagF8DY2lQMRfSEFD6bliew9ovXh/Vzt+VVCbV1Ur3Lq2R0SAJI8tL2P4vQBFsOrUH+JrZWZ1M4/bwGbt4fbQ2Qm/OlZA3wlvJGmARh/qRN0cCikpak70bD67ILvG05w3DWjzWGALq+b7JBkjmNoqpN2I52oHvG2RTaaaSYSkm1sjQcC0VmoPt5OijKhiYSeT0No5BBXl9dHAGoRC44qMwVEwqq93VsYLyfKDFWfGfwAReZkm6AEyrPO+RJL9Lw4xd8KomdkkwHSgY9JrynE7yrjB7kE+YMuSYC22YI7EGbsDO+tUIEGYfngJxkYPF4HTjUKYu5grX513lYJHcKHNqrATSQ6gI54DqcrwTOFkXiDnV88G0dcQU8B07j+XJqVSKWdG3EfA1RcM4MChBqJBiEYePohAXkcp7zVwwyxhWwTM0aMMHu7pxCDJMpZjiXtG8MScVmb2SakcO8VHBfUQ1SExTh5SCDgZzdqF4PAX5q7G087mVttGqAsGFXHQXHfE0Cjyt4TmWFS4vOI79F7f7jTHRgCDpBf8CDEXAGTpVNAQf12GV6ADCmCSvTkBkErk1jHJK3jCjeIrhTToOn6KCHB9uY/CUpb4pM+mcaTAqRaMTbrSLXK2BtJTbDLhuRM5yHcJymiC4cVUlXEisYoU0IYiHyMponrFopjZkCBMlLmbTL8gTmKhfdQaxZmw4DloPBMGpbnKv8tlhua5BdQh7iIswBXPtcFx/9aJGEM4FQbmx44RnIdDanegFyfh0ylSssIPZj0N4sb0H7TFcN0xQYQgT11xJUTRtRjVtHfx2Hibn2cg7ZYD+ydt3P5OTDEOhIUigajOXroD64sWLly9f7u3tvXrV8nOhiMFzbhaXf9aewMfG6kE0D7HzWKyg+xFoGo5KfYg6zKHS64xqs77VsuC5+LXhyOHExy2eHHnuBbD6Q9gGlK9vbT/f2X3xcu/VJp2kGZtu9kM8oDgQYI4jTLtQR/ZG+LIbKPloEL3xfCCKmbwTjWY7KVjGq6YyXip5zbOlHNGf7eOCs+YnTPzhjPN+6I0eEfpnpdiIzNJyFA6yVCTjM25oLlNGRfemu9GNZaFRfKBFOZv4Jx63+DqWGbvUfCaovTob97LMGDlv/HL7BX0xZ5q1E0Qa4hrcdBMuqFrApCRMqpcPOcTg8HtEqImUOaOiD20/4k8gydIShAWOcZYOFos+F9XT9akZVbHVMOwt8pIHVRtqqsGCXg6yjLuQti6WgdKZstdGakV1BKUnDr1COdyliczstZ2qRWnkTNFyzlPClJIK87g6o17TnGexR86qUarSxs9HXjN6zUgloqgtPIb+1foVfz7r8cOwN1STSqRzll6xnhj/43fv3r67fH968e79+cXx0eW7t28vlt6jCjMSB3JcnePwDYYdSD/wuzoMgKdKajk15FCqUjbC8O9dCqCRLXNf3nE8Vs+NVAzl03gre7aHpPOmyfrvdk8pRPrVr9/2HqRhYeKdD20ageRq+VitNYIo6uKgpMgXzRysyYIYKXONUWwUzAyQFcPSK5RNkQ47JPOwgwzE+pl47ec7aGKBK6XJga6ZsiJfRujMCuGRNjdnNQ8Vpilp9h432kD+PWdpGcTUFwcweUfG4c6Iv7wjDjg82Iz1dFGYnXzeKMOwZKldjQMyQIFE4Ozjzhsnp/EgUXJ4dFfNWV5GVg1QdNCLF4bWToUSC3uzGh7MVsvcWEMaHurF86wp/PGCzgYVRmOhCiYLIUQIkCW0ScVzY/XAHtAMnQ0EWU1ZDi46a5mZo5T1u6ePUtfvSF5vi+kwq8sDb8w74HbUi66jJIIcijQ7lCCKo5OCCjpD5s91TQgdIQpT5iM+EoUcx5zkqPX1HbwkevTu0HRkuNHTEHaEbvGNZuZ4z5hRNPp9cejIflwc+rcYKN2I814qWjrcMq7axCNFS4dhIWr6KVr6KVr6f3e0dHwwfVCNKy3T3q8vFTIds8KnuOmnuOnHAekpbnp5nD3FTT/FTX9PcdPRJfa9BU83QCfDRFDz0s4W3/T3hA2zRrxwqfg1NYwcvfl9rS9iGE4N6CHfVNA0ROlGxhm3UjDZ1LgxkkwWgIkjBiWGHn+FQ4RBP0Bs+3Kx0LfS8tcOiM46EuVTVPRTVPRTVPRTVPRTVPRTVHSb4J6iop+iop+iop+ior9llvbZUdFZjteL9369fg0f7y7Lu0zEFcSb5HyiqOJMk2whaIFqlEe5pJmvfOyKrIJJxv38hoqFq1IXF2l1JaMkWdFzCkmOjXlWXIFcHz6Lhh4fSzepQjV8CPBgBseDWvQ0zz3qpjLP5Q0Xs30Pzd/IES5gPefiys23IM/GSZbn4zVX+M6riFKQ37jI5I2u3z9HcN9iZM6zcaJl33vvBf+4DjJbZ+0dWBpgLHI+6RuwoOnb8+Vdgc2wvOQ7intrQf4UBvfth8G1t+yvExXXWtlTkNxQQXItRD/FzN2CJysxJkW2OxBDfHO0i1M8CB49p1sDAXT+y8HWp0G0vftiOJi2d198GlS7zn47CFS7W9sPg2ogDt3Qdp1w074261KaBS21N3rHPB1aHUlBMq6vusfmiinB8ufbiZd8l1huSc1Qat1PVZ4jxHaSztpbwB/uf3CC5QesOf18+8MnLQgsjCUVi4GWdRLKzuA0nQ0a+WSYjEBrjqLkOVuHGNdHvYhLlkSADb3alov8ExZ7RuM4gvsXZ4e/7K2V/viru24WTn/gyl4kz5NXLzY3k62XO1u7D1ii7+BzCWsdNNHNLfRziPX87ODk9CI5/o/jByzRNdAZel1ums9Z30o4jR8+Hhx7NRf+fhsUVuRNK3cjIFggRKOs/tHp+X0WiJ8asbZ2wqPTc/JHxcDSYAVVKvQNi1p32d9dYrYTWBmHZNdQSrmuee/HWpBScQm2hhkzWEkah3WDPhtnQkOa4z48P15zTXQWfpJ4dLA6+1LMaC6r2xm5EXHaEDqs0VlCdWybcDCgWH3DFKv3Di2nXOM4XSjx1fHaQyKDGyt+9Jj11QNBqFJ04ZGBWHbvo5uIpnMHBtGu6rliplIiMmj6ZniuDFgkMTAC1u0rtnAoq+N1/d7gFmjm+7I1wpEnC3J8eF63zXiHJdxxrLmV4aGtQmwEKOrl4I9+ckFu7FvHh+du+HYEkt1mS34Q9YR+fOxaAr80Q8rtc57MyYEhBRe8qIqR+7K2CrhFFVbjiztoje0sYwscpP53lsF17RsZWWErDEntaCkIK9z4No5Uk1JqzSfob8igIrm9+WltKnFGQx933A8o1STFjjaNOPYWRSZpTgeLWMecfYrROWFDfG5BhhTDofERxpRgYf8Oszw57QU9qtswiIsboI24I0YstDpFusPBKBZN8HF0+GrJRKa97wWyrIFheZTEA/q1dwTtrc3E/18vFoaMW7xoOuEtxUXpyi3QSYll7nWzcRB1xhA5JYenB2+O7YGYMIss+35+zbJRzJxWVzUZo7OkZjEmyl+QwjdekkoxXUqL4mDZiwaBc5mQk8CrhDTe094e0zc3HEN7Bh8sP7Y3D4PGpJ1tubm5SW4Jw/A7Y8wyLufbApUs7iEzB2LIrsFCajk3rBcQ0LsJ3uZE03nM2NkU+FIjz4LrlKqMZQn5nSnpc+gLsNnMXSgqstAaf5MaaThFT1x7P50OWMfgYl7XMPhEFgOk2bQYMJoxdTnNfXPIIczfcGfLKdkmOTOGKeCSODOBmRuFSEpsZVQXO9gnBwcjcnE4Iu+ORuTdwYgcHI3I4dGIHL3tkKz7uE7eHdV/NuPHB3NP2x2yS8PYvdhNTTWYjeuWt0rOFC2QAkOb3oAE+wiIZZhcEw0EWWslr/NxkDnoHg1qe2trq7FuWfbEFT/64p0nSgo0l6MYhemwzhx9xQUE0KEA25BpSWhpGkcvQS9G43FXN4fBwHIcBmVkwAw4CeMxb8XRr++P3/2jgaPAGb+YxODa/LjbAvWSe4WDBgMf8l6EC7EFWnzvBXNaqyCTkGK9VFwY6NeXzim0tFaaPJuwXN6Q59uQeGchIFvbL9ZGEe1L3Xij5uVBQ8J2TEyntLRnimpGtjbhCpnBHB+Ojo7WajH8R5peEZ1TPXca3x+VhKSmMLIbKiEXdKJHJKVKcTpjTnfQKKPmPEq/mzKWxSOkUlwz5YKDP5gR+aDwrQ8C6I85n8aD7tiwzV89FvYp/vWbiX8NRBGQPyQxhElAxastC26BdQvBDol2GYUbaA4qoUusAKCBEYaZRjVqdDXZtuvcShxWgDRGDZzXEDacjF57rcdYGSGJCEmMojyH7oJMcdkv+PYj/Sn6GNnfU/Txg6KPa/r5MgqC05PuFioODg6akrHXVS8/J4fooGOiy3NycmZlOAa1wMaxaWPcsjH4H8fe1Odoh0+nPK1ysCBVmo3IhKW00sEyfU0VZ2bhlaOYUAtqtFUK7VAOrIQcfzTKt/wD+KIKAx5Qg+3PJQGraISccS2uQst3boI5C3slZOyjfbuwVBIPjSIBvgS/M6o5hKiFEevmeiipWOF2Krt1FYN20zadNL/bam8wSMJfQhHwc/WnGp6+hVigBnQDno3V+HAEA78P2chGDtFWJgX6a15e0MOwLtcTOQgglGXGr5mG7oWRa6HRzhAeSxWLQ6UyocMoU4St7SNYFooaAG/wd+6ABhCt+aGNOWChZMqt/5ks0fqaL+wQWspwrzhtDU/HWkIORAb1WlMpasXVYbV59m93VHh7vtXjHE/o8NJg+A3V9dKGC+j48D4X0Btm6HpsrPbVmZw1evnCfve1mVbsj4orlkGhs0eIcDg+PA9+VLjHAn7tYjQxMiFjlurEPTTGCH8PRs0EQTAC1lNpg/UJIdo777QPJeS3ORO4Z7CB2LU/yGtcZDxlmqyvOyOpc2BYgCw+dc5nc5P3FaWNVgPvR8G1ObMs2upvyrUppdk/Lag+TTGds4K28E8873dL6BqVk81kM6YcpWSjENhx+GLpEGZoQ++dQS7iEsh3AXaNgMf32NC2QPkBn3NuoLJkUNAlZ1gC2aLZMwIIwk+pvYVu8PYJdgzce240y6e1ok0Fjv4AN91AyeWATDT6tNwJCOCdNrhhYvpDekgPBM7QdA8YUfB9z2K9saoxsDY0vbq00sVfIQ3qAoMvU2jenLLg+wGMWmItc/ARso+tfkZfSNANuzvCk+ZK5ZpgYovDF9jHlJV1pnHEKv5Jr2mSUzFLTqs8P5Pgjjj2j8c85LrVUfz4eomG4qGRb28hQd8duT84PJdeXcGag4qnDV4QWM6BfbTVstyyh/ad7G9iaAhWMDPHcxp4U60pvJaBM8HFwUWaV66OO3htqAmuMtC0xKweI9QUtxPVi3Dj+aGoT+ewVKaML2LvStPXDdadTR0VmpDW7sb0/m/Q/eLE7RGW9+rp0j5h5saK+TS0Y3byjLoObmaczDU4Z1DDP82ltms78DtxP7qxlIQ/x1JBbS0otpOTglFdKVZgFwAImu7DbPQYBPoaesUCDcdojsmjxnHBCgkRKkxDP203XFZj2rXVvuaBZxlWgCG/Uiwh5wz3fIzl5+xFN8Zlc+MKPANT0HUL/MiTH45wHJHgILXzamP19MYlvlw1/iWq7XyyroCjBwXBOx+a9feclSPUk8FCk3FYhIjeIidQ+hNIoBZB51R4vPpO6OPadB021zKMMSBknWbZeETG7tysw7lh8NWU52wdxfxsjL4j70Fp3AYg30dBK1gfs8yBwvpq+FeaqfWSam2RuY5hSU2ZwoE+zHZgAgwcpCmZWjXIypKHOKcvkoaBXqhhg5RKDe5IbQsDZcUZtNzW2IE88GTOmaIqncdxxO29qcU/3O6VCZ+RSQX1NlYsfNGInOmmUS2SyHPDlON2rSn23c6OycJdFkFMx94izsrlHgtjQtoENwvnO0PJmmvkWfki7kviZrSbMnad/l2KkWVj9YhEVxMPVpvqw/hejXPzgg2N5rm8sRBa3TJtbpS7d9ySIlMcNVYOga0J+kaEya5qWJm5FfWiulu3y7iPZ0o4cfJlGrk5QzQdLApyxUG/hoy4CHNRdUsfslVpFi6NjOlGZw8nYGpSiajU5YgoNqMqy+PdB+4PTxMrx1T2D6mIXR7ocaBP4UUjr5mCW8Zq8UFk8pIdj7eE+aBNlHPIyVF3G3Ze7Ow1kY8c6B5ekNXGiCZ+3WnAQTrtaNgG3I83VksNvBVuxSlXUUKNYhR4m6XOGeyJVPYzWFFKXrIcej/cQtMZtzJE6orn/F+oH2poUSLboCb+ysRtUE1sJQ+3OUNro5X3fDGeEI3TvlJOBCnslay5qVAZHrmQQ3MjSZjWHbQJ61G5kfX7j2kczSJ8pjVmLOUpJBS5Sjw5hNWgYBRbm1yEgou3RBKvmUQstsC2wKuAdNyTkLGbEW4cl2hBUkjBjazj++ohVldBLfY7Zj/6Xi5GkivGSlKV6EaAl+LD1cSqVasR0iYe7dWKJy6l+Sje2dq9G+Wmx1lV25tbL9Y3d9e3n19s7u1v7u4/30n2dl/+3oxCzKihmt1XQenzKz7gNK3ANNHACLpWwBFeYClbKjDYzOlTVoWQyl83WN+Lpo17JpezkdP/cjlbG8WTh1vESCfjLOratdF5TWURld/Ddlc12LDpiqWyKIBnQy62kCZYtmB4K/c05gZVLwTJFTKr8pr0sYYHJmuj1ENJJrH9legM03PZlDSdsyTCRdjeSi1T+LGnQlbrTS7Kylz6HwUV0kXCef2vMvEDVL/hec57n0EHG9DIVi/hHLmpGzY0Ap7AMG2TkpBPIdbtmcfPzKpNijkfpKmdfo24xj5e5BkNzC4yrwrYPeWd6iJMLBO0dduVUoPauU3aFwnSm704/fderAqA27sGfIZyAupiq6r9gGU9fqF6Tp6VTM1pqe3h08Z+M+VixhSE26yB84/euJvMSLsBFP1Ske2nkEIbZZcPJgMwvFrJsU30dT+pvr8Ofjw8+mJWvZMju5pQMj1Sxlow79Gd6e7mZtaETMxYN6l6eZnkItwJQBeBq1Kl+LWPwGRQfFTR3AWUGqk6EgbIFr7eBAgD4/rCiWXxFl16cSFfEJmmlVIsSxynrG/iXMvO6A1pKp6gYBR7ovu8ZUzwsfd1VImfBAGKaHrTqwOfCKdU2tOFSr9Vw7SuCisxCEns2kDbGQVJwd293jU1V1LIXM4aRT/sVSOvfFgA1/sNXJH/r724+hu/3eOl7uzdZGtz6/els6OveJsZfWN6rg/g+iRFF4076FG0A637Udq2SUhP8WJD/LPp1OH3XBcDcKDFFtrxIkecL1IdHKK13aRXg3bxwV5rQX6HYvus4npOaM6U8YIMnIWGdawVd4CXVnO0loyKayRzeePkcYsqgKCRLRZdcGRORZZDXOGcLcBVdmNVZWGiY6qYXTMYK+svUcwAhCiZ16vmBkaBkw5NYSAASxtLDDdzBmlqIaIdW4qCo8+AW3BW5VSFUPtadVRWuOoReXLm6n4Gp0ksUw0myOIsUY4JRD3DWtqSovOKO/UBFBTkVVVZSuVMNKkUKSsh5AmHRo0ir2YgCXQtKbVbnsJJEF56Rnn4AERBuH/XRv7c4MjjVvhZQxWsXRFgBrTP3yZnNrDuef8QeH9nmTr7aILxwJKzMFyF0/fekf8dUsMtSrSV2CEWhqF0l8n0MuphmHFtJZMMDKNYDgzUWWY5E8tqorfSv4vfgShgozi79rr0+BL3pofVn7OSbL0im3v72y/2tzbR0n14/NP+5v/5l63tnf/3nKWVXQB+ImZu7xFoEcMUfreVuEe3Nt0ftRRoeYGu4JxOK3svayPLkmX+BfyvVum/bW0m9n9bJNPm37aTrWQ72dal+bet7efNOruyMlYx+qYvF6s+ferd4tY39sF4GRMQiB1zLrwxIiMr9VgGX06tM1KeW6klGFRKpnyYdbg/oIo7GmwwnZllvSLMqTQuVQHFO5/eCzWfnSsgMvRnDRMlcgvM72pdfJZX+6ItEXev764WYkbQehctdngn8tomEi0wAv3AXgUiwO8FUYqhcXAJlLLy+hp5FtaGn12SGd7PYdA6PBdFMrdG0PXrimh1cmyoSxO0b7xP7ejRfahDxBUyZnkN1TniDV5qW6/jsBK3sXHI1k+VAnqq0SJcwqzj7GA6g4RcK91qLVPn4cN9uEXkMA3uVtcWsYPXKJi23LSWMvysZh6b3vetRDFu9G6lYhFEFlBCOeQMesBIJhny1YJe1bujmdA9V4lDa4PFDNzGdvU8xKf1nTM0IsOpwuvZh9KeL7SzPHVtzq/lLLKxFigsNS7WOijOK2b+TulpFEG0nJobqthd2VfusMB1f77QhZXO5saU2Ro2v56ib8T1OHIDt4vwhRGfYdmVUV2dZN0tcd3fQesHlVWdxGzttio0jW2ESoSROeXR9/Gdn4C8f/ea5Fxc+djqu4vZeRdIWyjwo2D1RPD58jT2ITscRiOQg0iCH4XrqJHIHykt+yCuWhaqGPK9QgrwrgAzDB4a7M3VQbLdXb2/seG6Wl0zkUmVpLLAnmsb/7K5CaaPZbVExfXVpY4u79uu82kuaW+M0TuurwiMAOKq4lJxjHBuU6h2RES0zCvQv6Psp/eaOWM+rAzM6c71gEx6zlS7GV+A/dJq9kvQ2K2LWD0F0wD/k2Uw7D0LGmFMgk4peKTCIjYt2WxtbvaYUwrKXQlLV5d2ISvY9qaB2x1VLDAH6Zg6Akg3/Rl2iBtnHtHMkpOol4FYc4GRcH1hyc2WyVKzP6olT+jDelScu4F9a7VbeC1EbrUehfBQhN87AsAUrjtuyRF4ZehVM4WcfaSpIVJlzncdVN/IPxl7J8OpDuazYJjuYOuaRR2AHqXNBGYwYrBNmKB5fhri1l3+o99CrniQ4sKIcU55lK+AT3kzt3f30ihc2jMnnTifR1V6U0gUjhF2AoJ33KzcKVGpFJprEwtEjjJjywdce/YK7K3r4C7fsJ4Js2iGvobjXM4SDb8n/vcklRkbJ573+q/rpIjYuFgHy2LNFTdFW8xtOqmQq/k2KfXRPDk6X0t8NlnjjSAXObIm3OrvNyLMiJHwVh6vQ9zDuKksMQjm9uVGURNhwd1L5GWTpg1dqkXN3W4L9Inc67hwYUCx6yKiCHRh1G7yW3wX9pz+WXeZHCAL427tobEkeyBqxmF3OCwILQsuGNHB3BRHcsVotnCU5C5rT+i1/Tm6JvEAeuIg0ioQN1w3VK00ZSVmNIdJfX4R1Cmg9vhLATL5yZGbfOW4UrJkGweFNkxltFiJsp3pZKLYNSof/vHzi5U11AXIL7/sF0XNTDjN/VPrm7v7m5sray022o26/cbMB2bO1SeGYEG0UtMy0IosWtHVZB1jsVbgph8hSWFcU3R3kFpR7cR3IXkiTx8RJux+6yhgy/HVDPydMrJI4KIg97BUdktB5nTatk/ravcb+4KhVE7hX5SdxmWVGqptyGpbexAwNhSY8xKZdM0pK3uEr5k2fOZX11S9l1AsBJxbPzSmUHCxnrHSzDuj45XUbNVO0L0GQlOIdXe5YgICb0mZ05Tdqp3copXUJ/6ztJNi4fSTYuGyrK2GAnNs7G6/3MpYNlmf7k4213e2t/bW915ON9d3aLqz93KTPt+bsru1F08PU+6M/C7G/Sf/+Y4Q9wMsTNqKh4bCHR3/EISaazKxclEzWMyFbNtfIXbOBynbsd3K/f7/BJVbXR0wJ3ZFphw44GDx9Vvko8D9ZyqyDanqxZJG1MvIVaIIdsPJAqc88XZv8qb2OvznTydv/suXTNR1vLe9ZHnK9FqCL7vwf2eF6Wn8TSHVmGWIzdZ6/HGMvMLO1PSguGmMxfoMwWT1NXVeYhJq6FrRwg/da1n1Jrh6KzWGbxlF0yswqaAVsCf8gxqj+KTqdDYeoEgR4j3MF1//4UtsFIHs+ZqqhaWN0G2G/MIUhqlBFRT2cU4rDeZLSGCXU3e3NLm1ZQvM1z7y8fTueNr7kF+zEdhyIZE4G9X9fewdBY0AYpcJ+8jSyrARmfMsY2IE4ZD4bynyxchxyBG5Udz0mA5X/3PFP7syIiv49Mp/fWql9afOEE+dIZ46Qzx1hnjqDGG+784QvaH9D5MdQA6CcUAYhLrRS4oLEFGHxNZ4vykspFH42mNJN7VA4GQuihE2kAnVL+/gb6GALQzjNhAlh6oEO864sFONncrH7VlhmoxhFeNIX8Vgf8zjwNrbwapnHx1ZTTMNw3lt0sMdV/Bu4auR9/fYVxw2SHa+ad3y1gWA2kSpW/31g7AzFJShwWHIug/qDLRyd1Eqjk3FebCZ4tdRdAQUuHRmh8gU0FnhxlwWbIPmHvNhpXa4SxzmcxfbS9xHCkRRLMR5x2qbhglgzIrl7JpGlua6dVlvNF2UPlGWTFlFFy+AhvkOrs+8r1X+4bJcCVAzYFMDYFlhks5els6uFJrmD1Zh9Ezxwl4E2O7y5Ig8+/nkaO3Oo7S6tbm51TzwtX44NITt3gE9LQbbB+CL9h76Sg2GvmIXoa/YKqiOxR8uOfPEjl3biL2gitxNhL+9Kal9VrZ3Xzzfe948LQUv2OWA1SzenLw5xjhqf7v47E+AFpTCZrciRbRRjELcyWRhIlNCpaEEgzMW3tzcJJwKmkg120CfNySAbhQs43QdLMHx38nHuSny/zw5OD2oWfx0ylNOc7Qb/9fIXRm+3FmC5YJ6csms/FGC3D9x1QTDmJjeGGK/o6X7TLtlGX8xHCW9sYQUo50LIlMrtgfqor2lRFY3X+xstkjoMyXSHoE0SJIUQolBdWgeswFLA5+2G2jhZR7q/fibso73N3FH6g7KfHHP9kUqb8RgkWpoPrYTrIIFRUHa3/330+O29/pqdX2glRh0EYv0k1FrI2FvsTRoR/ht6KdZJFQ+TPjduG3vn7qOPXUde+o69tR17Gt2HYtCefifDwzk6zF62UGsGAEyW6Qxv42Va+SeUMrHRTxwTVbsx55Cw1svnu/tNAA1VM2YufyL3FIXsBq8pyCYYlGAr/+LlZqDfQMJ9RlSYcYVeKgdJGsd6gvu5BBcMWi/ESu5gCHgPRgCVB0LHJVBfHbeshKg4HO7rSBYChhmjbs4gJ/dxzvCAH5mMq6VmVKlFpjEh04tWgv+YGrCDm2hMFGwpTdjPVwzVxleib1lobw4pmJjwCNL55A3XqcYWMhOzryLVCqnbKh1XVk9JdjGlyqhyc1iKP/Sod28XmH0jRRW72tmAmDsDBOD+btOG34uN1m3nrNUZk4OsLBdC8BKGLW45Fr2lJ1+HJThFOTk/G1/tenDg16QhtpBB07vJh5SQVvWbU/V94AyY/KylLHsFauIUsy4gYqKIiM5NfChe8L/m6zkUqzsk/WXz5MXWzt7zzdHZCWnZmWf7Owmu5u7r7b2yP+sfilVcvW9PYI+ZKglnNKAmpH3d2CQnZySmaKiyqmKXdfQTjOFCCvLbKIr9jAuRhLJFly5VGmItMZKS2SaS6lcyPwInXZxlb8wKIKXk3K+0JglB/mGI2APGCPS6tlYpzFBSCIXhFZGFsD9IvbWvegnUhsp1rO0sS+KzbgUQ56sdzDDXQdr/dfDPpgGOloOnt6T9WvFJiz9oc/O7e+v8MXtN5i9VNF4HZVq7Qlnh2d0HbzTco7EYe3LFxgftqdIo1hU8HiZsGDIDimYSyq5raUPFeT10cGZvUEPMC2z9p7F3USaLGQwIej2os+4KNeXEi2+GyFK60vxtxjnAFDyQ0+pIEefv/jP95QSnmPVHyDPmiLrnBP4neYzqbiZF6GyLFcu9CyKoWR55qLZsBIxhKXOsVUWhpq/OdodgQNjDei8VMxx64QcZJkHYxpCHjEC1w0xWUDCuEqp9kalJnDIjC2AaLvGehaQI6ZZSRU1MnQUproRXf1MC3qF8bMjgnlwc/r8cndr+yFNi7+0q+nLe5m+joPpS/qWwnmSulGb+xf/+c64ZQgSbsctu+xusDRUBsuoaENFlDx1fHgO7yZ/84fg1oz4bpwvTCpFXeQ51ntCEW1QNUGhua8YNKwVnTQtC+2cquyGKjYi11yZiuakoOmcC6ZH5EimV0yFTqLKpW78ezVhSjCIdJUZe1BVZpXOuWGpqe5NfP2UjX/bSrFuzNeRCD7uvbh8sfO1bli8C+U02jtPav6ave2OrQMrUPZMY/HVDrK6qm+7fcOIUpFTZn48eXve7fL1movqY8/YNdDRTGFEuPd9BYGeeI23pxdvz98GzNxjU5sxmXxDijSA860r0wjkN6dQx2B9I0q1BembV6wtkE/K9bepXNu9+RYV7Aiur6lkN6WugSBZ/cWNHd9IjUrBdT+DkCF941P1xx6yMSg29vy6hr5eK4T72IlD9yisj7Mep62iHBDHDR/ogEdfOo3mN3ShSQWvjCBX0FUaCEaHglHBxQwKX7i620xccyUh0KfRVt3tH/SerhSoiZUv+DaeMGqAEY3bWCjvwUJ/E0gQRnlZNz5s9V6i6QDI/cVt5m2zDkWjp3fSZ9R1EikzosqIGt8L/tEXEnGMEorK/VHRHIJ7wpiRLOfb20BlB9djPTT0qDRTiasCAl16M5byDKqtWXEUSKlm7tBVs7X5UidTWvB8qAiMt+cExyfPvJNGsQzStjM24VSMyFQxNtHZiNygONz1t+GTHbir/BFTmr+a/7Oj7uCuN6N0QsyD677WL/LS1OL7jfwnvWZtbEUFpgbY5fYacLYANqjbit64Qi4dyHeSnWRzfWtrex10cp62oX9cAepb2+s4gs6h7LbN/Y82Zry180vtrJ/PnWcr90k9ItWkEqa66wxTdcM7Z3jYkKEO8MvS49ZmsrWTNPvqDlZ2w5VXbl0rVoM/zGWVBWXc2wnqindOqsHgBSihPTbbScEyXhVjKKJzXbRKGzYsAcEm1Gish9XvwMIbu+BrOSSM2CePtKpOlEuGxd4WVXOObQpqSS4UFUAze3Pbnm/vNqe39+PXcrhA2MaQ/hZYHSsoH4qtW9WSwARe3kq6ANhr+JHD4b4af7YLXtUglvlreEroNeU5nfRkthzkE6YMOeZCG9ZiboAb9Ab9dT1+0SK/aedfBOeX9gO2gBiwc4hXPIHvgAcOyu4oDL1q8HJo3ugYlCBUSLEo+J9xN2lAYfj4PhReHMMqeDa2lIIfvPaN+k8qxRT3ql3wQGSuAngYttl0qYGnL9M8OCTEw5xdKB5PnfxqLO18LpUPtYXaEbXpv150Ixtigh0BgunHmEaAxS8XF2fw+XaH20/ebR1i/uxLUfNC1zmbjCuV+2pcmmEpThNh2AKpcg+vYn9UTD8g1MK/MJHZIomzqB5YqDN+tYncONq3BSaBWdvo3dt7eTuILuHnL3CRXjjjBm78nRj5heW5JDdSubYaHcwMsG8XEmsz3LF7zyywwLTmjFrpu6vSbO0879/Mgpm5HOo+XG2gFKdqpWZH5e2wqfOExcVtjQwBG1iV7I+KqYXVg0IX4EymVeHT38LYvvfvyomvXGp1q+PD856w9RkzI1JCh+eyMr1oggLXarDsr3du+LrwWoy5zm76jMpJLmeJz1hKZbHRgl2XUmj2xXkKTrssU4mB/Otylbtwcjtb8bj50nzFQftpjMUBjZVwehxVn19zuolTVy+o11+1s9mMtxjWiANw3WYV2wIjTZ11bpia0rRR2PCk8eXdQaFhgE4Pf4gLTaXKCBczqwljf0T8szkvaYi9kOqjWCmVK3VEhS/Mq9pFkImSFWRX5pJmZEJzKlKm1sKowWjDPoZ08TAW9KGC7kg9vfATaOFm6q4hbszQKSQMU6MAgfNjaSa0VK50e0kFsStaw6IhMRyJw08PKnpCp5aX5WjO6VA12gKJ4CzopKh3rFYvRz0OaL97gZuFst7Y2RdNaxaVXGiesRGRlXF/KJIVf4YWHzXqBS36zJLuxR/u4ZqDx+PW+Do5aiOrQd41ts5P35x1zgkhJ0c93G9z2QUOnYTp94LdThHdPHczvwf+OiVkFvOp1+7jHXGMR50Qw1BE2xcFLFg6p4LrgkSVAkMzlijZCjrL1GGN0Csl7Na9oY2d6dy4oes01BDz5VfD/FG8fNP8hPXYw0RYnd6PCZ7NuGz738aNhfi34laDnTr/rRUKaWARLIvH/1so4jupDFHUGcF9sd+/gdXDKtDww/HhuUPfA4IngVCbRPs4foS3vuOHRWSI8nGb1W3oOe2p04X4cv4GDeE5YSgFclwFnYh8uf1GkT9X+Qt7QFNDZpLV7QVgEHRJxE3HM8m0WF01oY+0FFEvJl/Nv6xMvJ+Bmizdh24DULIkNPOJex2sdXrzI9Uh0Y9vqBLjERkzpex/OPyrvrVo3tMDAIptNrfV0pIaYF8vWp2NcCJ3l0D5N6zAgrd8XS60AjKPS7LEo6Q51T5KALrzeNUwzAC3ky+5TNJKG1n0u52lmiUsp9rwFPv6JRMpjTaKlsmP/q8GsjCVHooGJDlfqhUBdCIMCO5gyI7S6pUSSqhQLrwb3ZEduNBdy3I8Ne3eUNGRaa12Z/vWpQx4HbWp4JEWF5UyNI5yLGM0XZrrL+0Vtjf5J72mvYipRDpgyYsOXtx0roLjXGYdVNyzv/Y09CxkmM6c/rgC44z5t+/USdv9zEH9jZ4IGzthU0ioKXNuMJfBkKpsNAcoqWr0xD3BqCUFlYcwl23shvVGWUReHN+E1f0VhSLWdsRmCX8WA9doJdhYhl/sqLMg39UtjIkt/FyvD+iEgLWQUideU8zsRv83E6mEoBmpiGA3wBes6FbI6/gQSJJC3daqbIP8uY1OiZauj6m91iYMbGtxaNfEx3mAde6z+51CAC04xt8sgkQZ8nPgIlzi6GGJffcVfrjsI+vO2XNXbSiW2uzzxWOxAvJY7NVdcBNzpGtO3TAJOcuZVU81Y+TdT4ea7O5s79itfL71YifpWVoypSnPfQOfx7aIrEYr9C2m/IQd2artKg7rO4jbINWrsjRkl+XOSLuaJhX+ygvdpTbDkPbd7edd4th+fieOBr6ffOcd9tGsT6hVBJZGVmsdQNQv+9biG8o9+la3tvmWxnWfvsWsHpJrskf+ViPnX4OkmjR5T93QzaobyN9D/wDXUgVYsqOeQCgw89arrZ5iMs93+9Da6IP1MNzee2LaTdnuPzF9zb9czy+L45phxKpKnRnbnrjmNIClts3t5Oh8bRRrJVat6ADvTuZM9jYJuxP00LfMKznU9bBPTat1mb0N7mpd1m7itlS/sl6eEDZ8yMyUb4EYmg38wqhLEQGYWW+hgEip/YqbH0HR7bbgdNRgLENDbmxyOo2+uicd3ZuBmzm0aI8uiko4cQzLOMlrFvoa1wm7BIWyqEGPy4HVDWuOe+KTMm796D7SwA3bbhkUOgg/IOe11rKHOi4HqMnM+DUTro9WNKuzw5RKGpnK3Kn6XkFXE24UVTwiHCwG65pVG3tYNMrIBZROc02LRiCQ0lxLmGyBikD9sL5alJFJhqd/jOzNxSZSXo2IubGynPKtzOL6rlbz0NxUTkqvq5Bj190wIpSzAljqIk/2FspCUae6uyUcqY2MaUNOzrC+lR6BI0KPSDTmDVe+qu436BmnvGiQVo8jcpmeqLc6IVfRC4neR5C4wQ8OOzKR9txAZJ/dliafHbvOofDmGISIsUW21Zu5FOF7xciVkDdiRMb+sLqfUFSJ+tnrqui5kV7sNRDgOIhZXA7msVg9wIg4aKaH5mAB2ZJ+ceTkDF16jpqoJjcszx2TC+vxx69OP2zyv9oCR6GnyTqdCamNvfkMFRlVQGO++nMYdpo36+u/ZlS5isvUhMiEGTfzagIxCZZAcj6bm42AvHWerdtLpkfo25+//Vd9uvPLv775effNPzb25ifqP87+SHd+//XPzX9rbEUgjQGsHStHfnB/+3t2bRSdTnmafBDvmF0P7Dmptev9D4J8CMj5QP5GuJjISmQfBCF/I7Iy0SfuykziJ9+JED9VAgj3g/ggfpszEY9Z0LKMWj8C08HLyykzRd0JzrlgR+FCiuwc8ZiBc0GSvSaQgAzdwTi7SRCGWyb2qJGKlEzxghmmEJAG0MvBVAPSgMD+F0QeN1k8cpg0WelayADbDbqZSnVDVcayy8/JJjw583HmdZtYd1yjn5y9rFTyYzfsY+vVdrKVbCVNKy2ngl6iOjUQgzk5OD0gZ547nKLm9uzeKu2en6wjcN0vsF571MP23PERuK98tzn/lnb8h+bQ+xw4GEg8p8z8lMsb4HAa/nLBmWHcXM68Q6By0Zl9a+rW020iWixXzfuTDE5OXE1gkthxSbPMcWPXa80yWX81XedUuIdjA6DPRkejJQwJNev//vrgFKnvj3Uu1v/ALwxFf2fUgo4c5FZWiGKmESDf9ITYiROO1kL4G0tznAD0EVQtz2SlozEBEM1E5ty4lk3ijgar7t7mdrL1B2EipaW2Jx/kLSs/tmI3WsrP74xdjchvXDE9p+oqWQsovy+swC4gcasb6DgB0rvBBY1Ak87RXzpuIFrBgPrvW6fM4WJuCyO4dTkPDPYYOq8B1ZLJgkhIqpMKaMzJvbquBuGPXXs5P0O46m98yhtglzS9Yve2jbzd3gSirhvkk4Rd926PuFv/0iPw+h9rzciJvv0i73YzYs7z6wGkrNXXLz2jrKVV5DzsYwKy5IjkwMv/SVOrw4XgjKBbfns6U0hCCHGmHuohUHjuzqrf7Eh8QH0ZEr6or2dnl/jvOE98DIkXc2sM53RhxYIqK0fEpOWI8PL6xTpPi3JEmEmTtW8P8yZtIX6gNFgXnvj2/ATasuQovt7E6aqerF9bLCYWdzuIwcg+UWqWjkjJC0Dot4dOC3QDn9/zPfpXuEGDm9+NAk87++jb+Lu76gtGMY+d5uglg95KjpeMQvF2LOzRMStip8YQSJcxw1Iz8uNjVA4G19074npTxncKpr3nsKG4btZeD6nhIdzHlxXEQSn0y1fQ8B2W2mryLsWUzypV77skqhLLI4BoOTV2usSXsmmXOfT2ej0iN2wCGiBn0JjfqAoS+xFdXIqNUsF6YVxfcsXLw7Xa/IM/wVZAdsPGIEUzgn87lxo0gM7QFqsHZ28canTyQ812An1GFm2KnT5vMWi7e8PHHPMpoWLhmRxgHdepA11oH2qJtKFr4f8OfMMqvA4WusyTNy725I+KVTgwOb54DVUypQAS8savUsmUaR1ZL8IwoZ6rYuD+SCUErFnJzOMDogOPD88fYIVncWj5o+uX/rgnLqx/LlGfqyPYwSQehWmjmg/tLmkRmcktY0Sa+FOKZuqtkQSj7/h04fMHvP2LkHOMxqeqaFic6qvG2cTbul0rLt/7TDA83+rzt4TnYywMNWwmFf+TBUiWvQFwAUlASfIUpv9gza2Dw7983H5nxd9nIH9nQd+zLBcv4TsX6TqLskx4KNuIY8PA5+U0+CKCse6O1REjw4GKeTCkNNSeKaoYBNa5y8KP7Oqh+65aI3LsXB31NXT05vcR+eXdiLxmM/uEVTHbGD2rJjlPL3EYtnTPt6fCvk+FfR8OUu+GPhX2fSrs+1TY969X2Ldd17d5qde+mC+j0/m07eGVOj/T96vVudGe1DryOdnXHST+5fW67pK/d8XOr+h71uwaa/jLqHZ+VV9Qt+MilUUciPFpul2dj05x1KZel3h21dHrQJ8Lo96j1x29+X1pVH5ayFYdklVXuem/44epBf/m4PB2ABrzDymlH9aZ0V0khM2qo0LhQbDhu3DnON47vNmI7p6zvJxWeVyjt77upnUkUHBWBAcCxWxJlteFbDCFU6oZFfxPlKkbcRFCxsnekPnIWMYypwBgKifClbOpIawozaIn5vQS4vPOf25sxFO1effDt1aB/Kna/FO1+adq848M/OdUmy+VzKr0EYv2ddJ13Qy33FwtEPX25mYDPs0Up/mwMdVed3eTOc28KVoMVpV/7srqt8usgXWeGkogYgLEwamSRTNmTrkGP1En1RCrXY+0KJlO+krS+Gh6Na7FvbG/3aE+TabhPyX8B25a+EPmOYMqNmg/sH/VQQk9OYIN7bku5xclaD0mUv8OAy9HcOeLggrTMlb1nt/H6TnpNyViiHUBkFpWgnd9dFD7+3tSKONxfCQIE4qncyQoCAFpVMwOeY2pLEoqvNRkxUCwpzaIsZXkGOdU6lDP0IqSkG1KlaJiBvE8U54b5qy9UH3ZC4lQ7gJCfgU86AXNAEa9nodUwPoKleKb4i4ZTDX4eld9TFteXKtvvgbZhmvqHK6pe0j3AoIyPf34kgP9ZCpbN+Dy1R2/S63gSSVo4eh2leA71gf+KhzikZWB71gT+ObVgDg5xtf4ctz7LPrqTqZd3/m382y447WhORauwuhbP6uH78TUpbt8x/Seofxro+DNQgKLGIfmf8ajQtGBMLQDBMd0gbD1WIb7/hVpdIkvVbjh1mblj7bjbk8e3Kd8UvE8uxyWGlcPXEpk767ZUw9Q1Ns0dfmQjiwCnwlUEb6JCriGlNFUFgU35PyXA4xSEBiFziCD2g/RUxBgujN9yfZeZdmLrcnmq729ydY2Y5ubm5NXe69evNh78fLl1mZaO3jvMWinc5Ze6Woo3nTohu8gy68Q5M5rpkKVum7W7N7k+farjL7ae/WcPd/ZfPUqfZnt0Ww3nbxKX+00de1o8oFWdNSMLoH06iYXCJC/LZkIdXiUnClagBKcUzGr7NqNdCSlwRW7oVjO6SRnG2w65SmvQ85JHfDf1A8QnZc6lW3d/hGdhxlsjZiRubyJFwx16sKOuiC7SjO1DiEtIzLL5YTmHbzg130LYcvoOxk1/S0PLOODLOBe+JqYy3nKhB7M1fEah3cFkzFXvI05f9ibzaMIJTr0IXI4hZglN2KssilZkPOzo/8gfrrXXBusH1MzI6k1n+SszrDXZfYRsuvdkHpjrctnDkqazlkYeDvZHFDS670ioilqypFNwYqaoTqEnVEzjyrx+H3jHYKKoNuotNoA0t84ZHlO1cZMbmwlW9vJq3ZnFCi5lQ6Fwl9kYUFGm0WYjLx/9zq4u7wEA50SuK5FEl6XKL296mAosyItL7PEtOx9YwWbJVb9oIqEnmIazUS698j29vP72pQ+YkE3ZxDtygLgrnThSV7ejEkM6hXbmUe+qrqZ0+YjBRW0rvBMXM6yzwTbJ6osRiQrr2YjMlHsZkSE/WLGihERFXz9T6q6Z16VxbLbOKwk5je0OUvcyWQ7eRUL/025/5j8Au1iPkXy/w2VI3ImlbGkT44/srTCP5+dHa+F+q3Li9VNi+QgsT1WZHXTNGzGlpZGvtpfRqiBp3jO1q2W0NVeodyZnBpyKFUpVTPZ8h6SGF70CkvNujLYA1d6RuMw6HtWZsceWPcIS2spFw9c1ovkefLqxeZmsvVyZ2t32fX5CtOXsNCh49DsKj+HRs/PDk5OL5Lj/zhedn3DOgjDovq8hA9c3Eo4gR8+Hhx7ZgR/t23RK3evPlp76qNdPX+MvrrbD7OUYcRP0e9FSamoPSl1h1WX+dps/wT1Jv1whGcbESm6Wl+N6udgcB/76UvotDo1VucydKF9EyicinCjWT4lVITdtasqOeaO2wdRLfFlwMB6i+DWwfTLWVFmQ4X/rh4oRReuihUgiaoZVFnQI7toBfQBeLQLohMt88owrDQaRdlB6dVwr0WyyRu6IBPm3FyImVJJw6ACq9Acuh1He9aRIdzHdZSFJ1xs6NDEd52s5+FPqyaGD1ubif3f1osOIi8h2+ZhAmNLE2NiZuZBVXfEYscGx96iv4q9C9uqsJlvXOHClZmzKLCfJlV6xQyhguYLzTWRwmrJYcjC3shhk8iN1ScCN4AWrlTFZ4i8gUKG4YUCNySq8c+dOo53hK50yVMuK123jO3IdTvLMspUZuxS85mgYJdjH7m+t97QRMqcUdGH+x/xJ4ywL+2QkJ9PwgxxjbA20KtGVWz1EyHHlnyDncL77IQpUwYNWr47YE98Y0RbvkVUqhalkTNFyzlPsXOOro9zPOo1zXkWZy1B66hKGz8fec3oNSOVqOsmuBYD/tX6FZ+nV48fhr2hmlQCjISh+XRcOPndu7fvLt+fXrx7f35xfHT57u3bi0/dsgrTVAbKsDnH4RuXM3jnoPKvelRJuLUyQPJSlq07ztLquZGKaVckqd7ons0j6ZzyOFT173bHUXaoX7/tPc9yrJwC5S9Yhpk8jQ5Wrg81arGQY9Mo0TFZQElXjdG7wJlYvkBjM9ofkEo7BPVZpx4o+zPR3M+zIHiEzzi2LI24F1qurWQ3o1xo07hiJ1xQtSCuqWyzZm33bNLGXtxz8B6Kp6KgIrtcsoHU1/HPNvfhpyrPPdzYsgpICe5L15jI3Zlt97uXesJcTvppST1I1DTP69u23fyscw1/ulzUkIfIOhRFVi25Z5kkfYhlGrD28+1xQW0pH6XvZgoZMhW83lyHwTrdA4OmwBuCleF0HM1XX2RTcgMh/40K6WCIhZxcDwgGIMDhef/+5Ghk1aJCCq/dkJ/fnxzpUXw/0qiudWGPn11qvgglprE0cKjcA0657qoPpdBGVanB/rGoNOQLN1yMOchhsCQsBSmVZYIpuHwKbvgsvmTPTo6IYpVmjVLade1rXxprCt1WcHnQN8DqkCNC7VWl2yFnxGdPWuxJbXqYbbqd7uzuZq+mr149f7m7tMuwPkPfLC9ZPtbjoKUjxbTe0JHuOM8t7HDzCU2nuzGQdiAUUZq6S51MjqXTmVVEoipVvSUpo25JEytuu0stBN/Wk/nzjl0nsP5tbESw/wAX7nEabble3EsQkT2KSZHtDsTI3hzt4hTdSfWcbg006/kvB1t3TLu9+2K4ibd3X9wx9e7W9nBT725t90z9FwkGW/UXCobxNSQEy381SV1AA3r4nYahiOYFz/vcLG2OUVJlj+3XsRsNYvx5uM1nGStujaYnq9CXtAo5xH+/xqH+BTzZiL59G9EtO/fXMRX1L/DJYjSUxagf30+Go/vQ9WQ/+kvYj9x+PpmRnsxIX92M5Gnx27cmDWMwegiKnkxKy2Pri1qWHgjWl7M9PRywL2idejhwX9B+tTxw37SF6wsZsZbHVjlbSt54UOT3SX1NOo4GsVmRpYvpBoOeMDu+vRYfutllG/plGs/eEbMeoty6ObbbO9sPBa4D3WNE1UNXcIe5VVL2g7r1QFCB0S8B661ZPlYf5QVrbKsT67t2ou3NrRfrm7vr288vNvf2N3f3n+8ke7vPf3+oBmTmitFsubKGD8LyBQxMTo4egwwclANG8Dpwe1Pacfb1pYsteqC5+V5kv8BGAeaWVGRpEb4foWKAfDXUlqM6UCumaxxSgXm9E1Y34d8PQ0YV7AglEyVvNJT3MaAxcOOA8BIoNPmhM0bSStmBcug+KCITwLL7UZUW8s8QNc9ZKkXW5Luh9VFVdpO5n28vHaruYLyR6oqL2SV2LJTqEZMrhqQfSyYOdBJAbzshOorDXBZsg+Y8XbrgZ8mS/yVJJyVL/rp5JyVL/uqpJyVL/vLZJyz535iAEiHgWxT8A3BfXqwPU39toT3k5H5DInm4ar+iwN2C4VsQpwNI37Sw/AlRNd+fJO3x8/XkZA/B9yMFL08YjyAi11UWZlwbhxWX+/gu/u725MefMHnRNYW1lOHzwv0AvoAfNEsnS6YGQt44VCcYiJ+svnXCFNZAIDeKG8NcauWEavZihzCRygyKaoXN+UmqsEDVXWBdW+qcmb/TvGLHH8H7+Y7Nfq2YWrjvRk2PP6RP6hJpXNbOO2hBhQ69cV5e2u/GSQh5kb41wqQyXm6px5wwY5giiqXymik64Tk3C4CldkfUznF78t8d/3z548npwbt/4MqZa2vd48j6/dcfq4PDzYO///rjxcHBwQF8xn/+bVlhB7YYb5/7gqM+rYY+xgRgnRu7vVA9DeZzVXLrbT0LiKCaWB4JUYB9b8K+uD3yBJAAWWjoxxOGdM8HIoEpyTOL5PPfR4Ds4/84Ozg9ujz/fQ3pIXYUBRh4KNxCoGSqq/OGU7I/KiZSbFTgJgQCtqO/ef/64gTmgrH9cNAjOIx4TRXUUSI5hPnhsKKCPnOw1pqi7ZhHv719d4QEffzz5a/2UwP0iPrabYixAWHKC5oTxVy4GnrOnrFkRsYrWyvjHrfW6n+uHO5/UIZ+UCy7NKb8MOHiQ7GgZZmwj2zlv5a22gDBDVTa+dxQkVGVNfcbL1THRXyQim6vEEli2VXM+fUQCziYTBS7xkq/oBV5V6Sdr3ON/PLvr98sC/AVWwwA7y/8mmErcn7tPMxyakfq3nnnb3+6+O3g3fGHWmPzLPz04sMhyi5/R5X+w0lhBZqfeKhnYgkUm9DoDzdcWEAt3S2t0nUKLz3K8iFox44dx+TYrRrZ4eCEAu/u27gPn42QcMx7EPPhiE2qWV1z5/4CORGcQzXWhDn8Hd/tarMUxLWwVPe/D7JS/dWddSJCfLRmxl7hBaPC2OtkSlN7QVPDSMmvJca6KOj5SknJWWqX4uGDmjruA4RPwQMa+/7UEbQuBltbIRliD8WClDlNoQO+vWGOD89d1AK5iEFwQ2sGtSfFzPOCYoSlvOvbSU4hrgumQFnB3Y1cRUJNrV/i4rkgY4fFZBxWcmAZZKqYCTFKFkNxP6CRKw/ng8uhYtxcahM61quRD3iqKcK3vB2RNOdMmBHxj0I3PmzHlPjq+NklLxNyMsV65mXJXOjayZnn20bW0PNyPMJ6HVh3SjikAcao68JzckaM4tec5vliRIQkBQXRLK4+xw1MRhXLRlbcC9Hy0VT7W6+2k81kO9naHT+gysac6qFKvx3kOd4RVM+ZRjKQwiJEecJykhWGDHryh7Y/NRepNKqXENBf48+NGuqicEE0N5VrwYcV5xayWlWWFHSlGMSx1fqWA4zQfCYVN/PC0tMzDLdlik0lvGEJyrJMuPQCAGvLtzUsl0Buf68riz7HoE7OetHXVKP1YE0x/EZCrKSd7XZo7uePVd4oMvbOf76DM9pnfB2c0FQqig8Gi4aLyMNAQbGoe16EvhJ0ZgV+C4CLjvYhi4TmTBlNpCISCsUJiYXKYGG1JuALw9kpovBJN9oNSOderkUVIAIcL2K273mKByoruAZ3gRUAlcxD1Wk9Cq05JTIycnJ0vnFydl7/ENpvjcgNm/ghSwwfx54P4YFK5S5wVo8IExmojyRjhqWYUiGsfGpZsmbk2fHRuzVXTTqEbTKTPqR+T2Xm7Z4ej9cnD4p6xj0WoLlmqVmVSbEIdXIRCAg3hb8sZ5AkVYyaqNBw2CtPWYEygCs16LuTpHVuqFp/HfeCva+KAPbmG8qneFA3/0MaQPHGDYVLdDHArqUHcliPhIAVy2Vr8vCxxL3IIAfGsKK06sFJJGO8ZvRqaf1rcPfjBTa5b3seYePdhns89C/yx1ymV0RZtVobkGVK6GRPjk7PMQL4l4uLs3OyQS5en0Ngukxlrpe+K4YKIz/ANZ4cIaPi2kdHW9XbVfeCysfIO5FRRlJTbWHwDLKXcB5EMFubSwc8DVtiOFYE8luqDd/OGwJqMCbXCu00Y3dUfHX1gH0d4CWWP6jbpNF/HdcJxiqfYbPcuXj99vDfL49Ozy/tIbi8eH2+7NqGLuC7+q5RtNdIqy7cnU8Y73XY3d77IPxq0WiHT6FpNkedDbtbiEyq1VVNMplWdV5GczZQKOzJXF2t6UlIU1PRyIq/aeSdoSTn4grWQwoZ9ilHhwuiYOKl6vqac7V0Qdzp2tJ8MWImkht+xUuWcQr1re2njU/aXitrsaH89actytXMjEgpc54uRiiboEyArlx/61pFAU72g25/DOgvWN0NLjYhOfPe5Zlj+Zc/oZy1LJ6q6hvh/WB5kCoEAQQcwZWg6ztBj1qXAWd6qeugyTC718LW5ib+/9IGokGDei6iPkQbRLFrrtuiw4TZVQPtgF7vctW7S0vuWVPU59B3E3ZK0nn9zR1q0oF7zm6y7wBItfNFgKnF/iailv+pFMJtzzSI6qj0EMVmVIHhUDNQUPQoeh73f8LRtYj8dJrLG/AoqazWmX6SilwcnrlRsaOvDmAibCnj13UAChfccJqT83+cQqFuZp7pNfejG9QOWMOCbgmkxSB0tWdyDDJfdPDxQ80FPF6MokJTNzjY0JwmRGhqKswvc91HDFMFWQnjrVj+AbdaNKyHQrQA1wnQl/vZ6YmOeTPfkKa+LLzhDVv8UJfypltTxOtwVpbzxgSoQcMq3IhRFiyoof+sBBIFuGbQLube7husRq2QpjPkFFiw3cZ1OJxtpfoQh9/wS2h6f9DAQ7OMaFZQYXiKjpKPxrWvZh/TORUzNmowda5DB2sjyTW3y/W90LF5oYBkX9qwGnnLngpzTK3q7McUvoc2XiRo2nNOOW14nhOGhibMkHUt10UWmxkBYVMedeigZalkqTg1LF88RL1Gu+dQghO2CIWrz21M3ffcriEwmGLCZ5WsdL5AaoZ3ApcHj6IO2THQkJQKcnI2IpRksrAbAMbQSvCPREtLJwkh/6gxS/MbutBoWm5e2fTGw+Tpfpy4L8aIsqaMJqwUVTtRs8pn2YPRNuHl2IIyThCs8YhkrGRgnybSyQyk7vwPVlmuW8EsVCdL96e9LZ7FJf3iOITm0ICqLq9MKyOFLGSlfctDwHv9dQDQd13DgZ4dnJ+uddJs7b3NaDqvbU2ISgyGZD039O7Wi1ftNTeaXX7T6VzLR9D09rdsoOJnKWc5I69fHzbw0ROYskwwZPxas8ILhKBAaihU7474vSMJZNHdrdprNv9Cwr4Hsk/ybyM0OH7TLD1jMkm5WQxVZOSQm0X/7ryRwijW6o8E4EhhuGBisMInp42CJ26yDnynUpk5OYBgCtoDZCWMWlxyLXtSlh8HdTgFOTl/C/nFHQgPD24Fa6jddCD1bughFTTrYsr357sHnBmTl6Cc9837WooZN1WG93VODXzoxtz+N1nJpVjZJ+svnycvtnb2nm+OyEpOzco+2dlNdjd3X23tkf9Z7QA5oBFn9b1mat3fxy0DJw3tC0eEoskBpTA5JTNFRZVTFZc2MnO2IClUdrBiZ6PQgrs3TdNoxF0b55QJdC1AtHwuMVJowlSdFO9F2/qGQvByUs4Xmts/0LA4Iqk/1nEc1qk0Fk/2QZTAsWt0ZWQBF+SMydCssWPdmEhtpFjP0s7eKDbjUgx50t7BDHcdtPVfD2+Da6Cj5mDqPWm/VmzS6oPedmR2YOh3Yq7WHvrQMst1X68pCx32rY7f5OTsesd+cXJ2/aIWPlvyVkHTAXDz5uDwNqhJwzJrks9w8K5eWDXTKV6QchErChPoX3l6cBH0b1fxgTvJrD6zkpSKX1PDyNGb39cimbd5VkCbyyXNyITmVKRwWiMHoVREycoe4haS7TpLuVRqw4NSCGIE2PG/YRSgBvsAqa7Th4uZT5PhWrkunW34zDwbh/bbSBwDFpli2WWf9PiIfd4gmHA2Z9pEk3oc4dwjWEhZsiyAXE280Bm2POoRO4oCcWE4p3FOpSIrUymTGUjwSSqLFcI1WYk+t6sIohfVBRdlDGu7QKUHlnJtNSrXdwd03JxfuTQe9BDqajrlH8OI8Aw0ktzf2MBH8AmrSa0l5ALDe4xE88BHXgRz9GSBXU4XxNCreldRJ86pNsTcSJLTCcs1qt9CGkgFwFpGdu0Xr490iNxdSWVSXa10b8waGQ2SMLK8hO3/AhTBplMGJezsrE5ycXv4jF28PloboUvkSsgb4W1hDbCIQ/3ImxsBRSWtyd6NhykwHeJpzxuGtXisMQTU832TDZDMbRRTb8RytAPfN8im0kwlw1JMrHfVOS8hcily4RA5vY1jUEFeHx2c2avgAFd8FIaKSWW1uzpWUJ4PtDgr5BOYwEsm3fCvZFrl+SNn/n4184td8KomdkkwHagRd/jV8wlThhxzoQ1rNd8H3IA19asRIDrUBqdAXORgzsTbyxE6h6HzJ4LdccMHsvUQKsI5oFIc7wRO1gViwNBXX7gR+A6EmRoZde2LIw8wFhgZlCBUSLEo+J9RcBqiMHx8j6WM+ZSMYRXQrU+5D3Z149BkMJViinvVjnYQUIO7dtcQX9mxj6juzex+FFIKmhbM2YXi8dTgr8bSzkM/coKFqLnoLjriaRR4Wssz7MuXRK5h/9XdTSj92x1Ho4l/w2BJ0FHq+KeMGuqAu6GapDLPWWqijuuNVpWhTeWUiwxpLVB+LmfakXyooennhrQU9LU/wA/GyjkrmKL5gGVYj/0cMevz8W0e/Gd8CjYMLOi+1qlCngHxgC6KLkvtS4UqBkn+Guuwjt2AcLIzybQVx7oS1h7dme5ubk4byBjkqPZUoQ3xD0JghABCjIFMNTVBa9CiVFxH/ExOMdlEyIw5c2FjybWHLmSqA8GAXJqxbnn3kLPaKSEbA+MyYwt6xTThpu7nH3PmWtK2dGoJ0jdYhYMhWIdqmykb9sBY3YKnVU4VwBuGZAU3vmRyO4LsVBrnNuaYWyKY62DAWP2CxnPZAAPiwmUD7XW8ZuSgxshvvKGpIWP7nrsu7O0BHy32QX6iPQWvs+cv2S6bTNkmZS/SnVcvt7MJezXd3Hq5Q7dePH85mext77ycvmhZjgaxXTYELU9s6NePuBNgqxWmJ3pehDKr7mTCPQyJOY5eaJ7LG9z+jGuj+KSKI8fdGC4FQFWQFBFMmFDot3n1o0HCR1toQyFBFyxd9QkRwcgegX+C36ZUwwqOrdLGU5cR0zhFXgpod8ZP80qbTrt7K3v+yKjRfYOg5uguOKifXIYqAuFRu5HjWl7BLK6pPRiA7rj6dJeuWLyOdXfcmkQkMzaoA8VTEw0kAVO2+ExECeZGIi8KpGRH8C97ruilYfsbHNMooDSusAFpteDEx7SjUbQJfumBLdb+j4mvmR0GdddJgMynmPnRlqOlFkuOQOhSVAsA+yzueRRd2CRUR4OJBcFO71O1GidZMi1WV2upa06vmfempqw0uLgwG0IMKPbClQPS5StFDWeipA8JJ5qLWcX1POxafSjhSNv7glRl46p395zUFlQSS9GuzoLDi2DaW6wDS6iHb3GhJtXUDMZTzxpZR64QcOwWVVCBIWma9YgJfr71TfdPqzm0jlI6H9WTi3nCOH5rrU3pfqCcexB5fcTzg+8JeDGiGggLBh23R55tyAnhho4Ec7+SaJJjv0EnUxxEqjAGVawFXfuE3sJ6b7zkNG5w1fE9XLexHb3xtI+zI39vFsbzGxKC8hq6RXdXah5sJMmlvCLUXkmYiccMNkNp6RZRLb7A3bvYeJ5sJzuxngWxew01q/7mDi0Ln7o/ktMHB2JPA3AObTRFwuZIUcjmPcGasfvMRWx+kyGFLjjyKaTwKaTwKaTwGwkpxDPpK0zVjOQrxhUiSE9xhU9xhY8D0lNc4fI4e4orfIor/K7iCuGy+O7iCh3UZMi4Qne13xNPR3MXhFafWhlC7Xpj6qJUNmIUBWVLzL75GMNb0ZF8Jj6+wRjD5YW6Lxho2EPzXz3QMBY1nwINnwINnwINnwINnwINnwIN2wT3FGj4FGj4FGj4FGj4LbO0zw40hJ4pCIxzgF3U39zhAHP9HiwN5lRrPl34yCVs8g5lNmmaSqwsA/WrcC5i6EcpZOFNRv7itzC/4UYxcnBx8X8O/51MFS0YFOXtDT6E+hpSwTqbgLjZQTWiobYqV6GKJ+h+bsyTo/MROf35p99GUPVyzQc0hA7iHlz0lOAaEgNdxZO/ARS+erMbMS5WavUPJ+yFslRufxw2UA9d4UVJU7Oy1pyFpXMg6uRvXv2q1x5qRvv5XA1bLkCXAXGNpnMoBBUqQYINzYDb1dM5TDWCHUpTWZQ51xhlNJM09+BFVUSFPfpWt0Yf68raA/yOYUu/AI92+A1TBu/+tFJQQSgUz0SbrSefhhiL+wy/h80IMZHMqs4Q5we7RX4KU7mxeMOuTLzMHnqLQcAVlM0Ss1CClTAr4GMTCkO4mFn9FRvOS0UUM0rqEiXnPAKWzma4PF91p3Xy35xcvDt2R6upfCEpD3bDW3rmqF4jMhvU6HH3D1c821dbijlBWOQbahT/SC5wnGbx01HctSghz9jHJNS5o8bQ9Cop7JhQ5w4h0RsXB5ubO5sbYYK1NtbwgT58fSFJI8S1LI+7Gl0xN/3yuEOW1oe7oYtBXsDp9PUgK5V/pxh80Ai1vOEvjS9xpANTbOIV97n/VIf1PjpePTB642Jr59Wru861/f0WtP1FtN1GEPR3uk23ix237N3X4SxLY7chWwzEXJbH7oPGCLh2ZfK8tuBqxD6kMxz9/9n71uY0cqXh7+dXqJwPiZ+CMWB8y/vkbDlgb3w2tzcku6fOqS0iZgRoPTMi0oyJ99c/pVZLo7lgwDFJNrVbqS0DM91Sd6vVavUFqmb7ZR1Lhv1UhLmyB/+iBq0t+Eh4plg8BZuMQyclKEoZ3xJ6IzjU329HbJHNXYHOwmAzQ/gcHHXOrLHOZGYMNdP5dYvedCFfzHfWiWFkunjxNAIjEqutGpRGzKJcuq8xBNcjaU3hvRyNLwbDFxfjd6Pz8W9X71+Mzy9G427vdDx4PhiPXpz3jo7/sUbDuJmbChYe7XZEhbcXr9q2B53KaBq1aSxSVuKagOB6V+kexwaucif6cAYyUZVJbup6ttnnMM4VvwEF+bE+pXE4pzz9SBRPQ/R4+y2KiLkmMDlgrmRkzFU9TufV1VUQbNxIZNVIdkTic9vAx6e1h7wWHV+ifnG0mUM05mpe3IsHRcCz5QLN8P6jnDw25VJlJbGwmTBzF1DW0NGhxJn2/Rg1p2oeJNHRjvgzKCmodMbkQuodsSjB/Gp4RCIOx0QxJcOLd46N5QhvSMjbYOVcmqwKxVXG0hBvk0zRXfA7mgZPLW8vc5dSBVOMZ7DopJgvFkxCFgrQq7pEOpcnx4OTy97g6Oj55fBkeHpx+vz0sv/88vllZ3B2MbgPT9Scdr8ZU0Yvzrt/ea6cXRyeHQ7PDruHp6enp8Pe6Wnv+HjQG551j3rd/rA77A4GF8975/fkTrHjfBP+9I6OmznkaOjlFHw5hwqohlMPs26OT08uj4+PzztH/YvL7sl55/Sid9nrHvcuzp/3B88HnWHv+OiiOzw5PTl6fnHSf355ODjp9gbnZ73h+eXGrSlwjlypfGcmz7DI0bLNJ7W9n0/+YKG7WjcjsJ/Akmvcj7C0dI1LVQIOXj97dTs0V2DvhMjI4LxF3nx4dpVOJVWZzEPwrb5nNGmR4eBZcmsDR4aDZzaOYXMC/kEPd7WP46UQpBYX4fkGL+adaqN6LpYmRnPBpBY2LWSj0cuDwtAmZE7TSM3pdf1ONOqzo0n3NDqeHB2FJ93eSe/07LDX64ZnxxPa628rT6nIxnSabSRSq3rpD2nGDt7zhPnGMrTsxXrmJatAkVRAPBPDxRrppeyvzYb+/497nV633dH/3nc6T+Ff0Ol0/rNxz1lvvhNI/fyKE0bbaOPJds9OOg8xWVPR7YGDByrt6pQgIY1jrS5TMnp9hVo1Y3FcKpdv7kbmQmUp9verdwZB6nFFqOlxhRdXeKoKyG+axp7W1k+WGrdUmh/PmCb7gmOSkB+Th2lCNeIvl8sAM/aCUGxLcKMqv6V6rinkQhE7sqxVyMmt7dD55sOzYamfzkPpYZUvzOXN2Bypd5UK505XiKbZdiid5c03cxbHYuW5ZcVpvnd0PP558Eqf5g9P+w1PXwyGGzz/OAiCzRd7LquNqHftBNEYizYscFUJ2e+Gxi2jC7E3YlNgj2Lhond0LDfuPMNURicxCP4GM50IETOaNk3oufmJTGNamhafWmcXSdlMZNxI+5JCXFzIlJrmMaGpl9MuaaqgvxX61FLC0lDeQme+LE9TFm98kE3Z52xs3WtflZXOp2da65hxsyggb5lhLDYT9oIkIb/w/PV50WH9ifVjauXJaWpaWVGl+CzVmkMdZLFqw0y0Na/n0DZwV/4QfJ5nSfyIxou0bcfY5pHar5yvsNd+Yb7HYgk3y6oudXqUB2tbA/lx0ipPdipwXFUcsSBwiBfCJwpfV2o8XfrdipRuLGZYdfa79Bri2Lb1Gtan9K28hqtGsut9bQdeQ58X9+LBd+01xOH+MF5Dy62/stfQ58mP4TX8llx5aK9hhTs/iNdwQw75h/W/nNcQ57hTr+FoK/9gzS9YbBVeTfxv4B9E9H/Qw50dRZsdhNjl86EchIdn/X6/SyfHRydHfdbrdU4mXdad9I9OJofH/W60JT0ewkH4nif6AJcsav4ydA59Dw5Cb75f7CDcdsJf3UGIk92tv2q0sWeqopIbVIA+WdqVHYQi2YkK2G1/29c51Akp5SnanWpBpbL1x/T3QvIZT2mM59sGCQh6GzMbkezawfAaCnvyP1lkDuGw+zn/Argr/Wmum2K2rpu/i4eSNLTJjzYmyvtqdVzUsCgyaoE016yFMKY/mdXH1BxppMhnc5Hb1UNJwkMpXIVlGc55xoxk0jjWBxt9BL7hbFmcrIqAf1wE3sCJlzpBJPuUM31ibRdCYrv3LtnE/m6PT1Mp0qzN0qhSG6+tp/MpZ1JvPNA+H+dR1GyY0PDaf3OLeCw9+h0Gva4ujmwQF/lU5+YbM1xVzA0TZExGbtF4GM/KE6Z3HZKJGdPWH1iGDmSRyWfyuizB9UYcG+Z5hSczJtvo1WEeJWsptf3J9Kw3PTw6OZkc9iN6TA9DdtY7izqsw/onh8dV8rpWyd+GyA59hdT2e5uPbZP+XZ0ayMlIGFW5xLINkODjCjur3LsK0ha0oy9EK+K+UCNfpzPtHJ9Q2pnQs05vcuJphVzGvkb48O7lGm3w4d1LG/9oS4viHQU4uWGdsoxhm3tYeB/evVQtCIPEJ63G0jSYSAZJ2SQSy1SLhCAqnLOEtVzlgwXN5vi+INaPt8lC223GKxrbNotNxq0iN7x8PbZXrnOrRMKw0iwFeib01gTrooP86q2e7YEmoaarSaeNb1sgESLPXFVBB9Vk8F/hrZ+GbVL4vZo0phLnTNjKGx/xag+LCNaEpuGGz10zWE/0rkj7fo5BtjafU6EbTCsni7zBDMDV4MiSy7hSRbUCgitTo1MxqHPOM/R4tjQXU5FpVShvIX56Duut/H4FeMwoJBEumOQiIkmuMgAy0boujPOIRQ1lFswZGR6eMLK3SGd7hZ9Dv74X6O/qHFrgDuglrc2SojjMg3PlrZCZVyxVEwWOPEacHn305D8Ti70KcT4++mgOLeUSFHbQlezbaR4/oAH2zXIbrqYmi1+rQEiG5Ile0pgQCY3dc8WKBXvr+UqgGGhxxuEp+ajlWcP7CHeH4HuBBY8FzhWRTJ+OwNTXh2Rpzw7W4CnXLfWr3jSE25c1wNN+//DAVOf96dOzUrXeR5lYlLhnF+QPwMHHH9JERFApvtAzIPqKKMbSEmXrFb+8Ngqpqz6aiJRnQpvzRgOICezckdsMJkyrGhSclqlHTpUvChQuW6FOs4GhX4UMgoyl5I8cSgkVB0fQXXofrdZocZLjsnTdaw4sBUt/SZUbaKu0zzc2A7mXEGloK34uydeCKuVJzYPfyyH4yqkiqIwh21UJhbc0m1dwe7oVCbRXGc4OKpX5FbJq4+j3D2uao98/LA1KH6Fud2kkAAIUYldzEcZrfsF776Y5+Hb0XkXYanvXT7B3wX1e5DsgfCxQg98YdM5qSYV+F1aol6hmfHfe2G2bGmlitQDfJM/cUy0PmZmsMVMcRFNIKSUsWWTFeGDo5smP+HalgHyp4wOZsGzJWDmEIVsKY6tWNuhvXR1Nq+C/S6N9P6XRzKFtV0IwAuirdSLsNnuVfddkQX582mh3mvGu2LfK/oS/i76Rv4u+3avo2w5Dij8g+AYbxR9BybljP6/pygeOu2rHiFINJdc1Ah415i1kzrIb6s4X6Gcod5HAJFstH9BCB9rTQSFsvyCu/oYzhTuqrSRFEgHVaqhxEfPIHpOtI4qmhEK8DxrcsFsrzz+cbFEC5oet1/ctS/X9XaWvsUrfj16g7y9Qm+9bl+X7uyLf2op837wY3991+IxRMaYz60b0TAtSfLuBgWFgWDOj6EMrEoYF8chEiqV3h+hX17tFR5eaiyXRyiuF6117qwzty0KRaOPQndXxVj13Q7Xn5C1sAuYaUX4FLYHYqizhb+e2QdNqwdzJgArS1QY1olMqeWlQ370TuKIHPPkYl+SjOtdX4k8ex/TgKOiQJ4Yb/48M3n5AzpA3I9LtjbvmcPOKhvqLf++T88UiZr+xyS88OzjuHAXdoHvkhvfklxfvX71smXd+ZuG12CfYnO6g2ws65JWY8JgddI8uuv1TJPfBcaePeRqO6CqY0oTHu/K6vRkRA588sWciyaI5zVokYhNO0xaZSsYmKmqRJU8jsVT79eRceLI27h/jyufNgknqFUq0tiGcRmx8rgu9ldAmZUVbJyM6r8Qf9IZVqXXNZMp2ZcbX5mCwuWGb0AO6XLVC+kE/6LS73V57xlImeVgd/Q9yBFjBa3tN73F6FXP/XaWMtU6/FmctPlzPIUszoVokn+Rplt+1hqlc8toa3m1oYG3wm8pjtxN0q5pyt0OtNBa9Y+fU2t2zr25i1IxoWf368vz1JjaVfq7cnNN4+F3j+dNOL+h+IhmdPVH7fp9P60Whyri/qCI8nUHMiDbNmfkT4FOlRGiy6Uw759ReCcJ5AQ4UetauxLDX99Qgw07IrvoXPvfa3IwGevZNs5AsFDLS4Hg6i3G2GZ1BqVm4Qs0hEAGSBy3zvHbSn9o8bX8iLA3pQuVmlKqFx52mkZHSbadrxYWg/cK41F3rKpYqIbES8X8Yu26R37hkak7l9T7cWUIpXKzHazsrSzqd8rBGCZ6mTK7kqgFBzEM4uYLBijyxrjSEir+V57+/YpJ3T69UlHrbWd4xvVJNAgjKsfdU+iQaRRwly46nJCvQBiky4dJIjozOZqALEOSbic3y8ITbSm/gSznm8jbIn30cQTrZ9o+zEL/uVgWGUtpDcMRVKBkcuqsrDGHCCDx4q/jitW/C3k0tc6LzuzxtcbTZmXMGJnQ1NJYiFqLGOHZH/bq+/seajfgrnHzeLEzBRjMDODJvMweRZ4pH7O6JOK2fxymTdMJj26LQqv/aD6v3Ab0NlABt4MSnDahJzaNvE/dv3Aa2Ud1JLCS/I/6U2qmjQaD1uR9RDhPJanShcLvjao/bgv0YemNNorZb30+mvg90CMcXjWv0YXSxr/8AM5fG8KADWrxAMzqBnUiSS1y3+6W7t6I2wKecxrdqllMZBebvIBTJwaclm8xZvDiYijFEkMUH16lYxiyaMQ36oDTBsa3LylQwz5L//n8A5AZWJkbx7O/7jdFBNjTRXq/Ub78e/3fPzmvv9y3K7zQUn99FIdwyIpdUUqKCCoUsLMsSc4pDuh/UBMlIUMEhvFHqoFa0dvDraLQpJbwRf7enohpVK/1X6ySFxYd7lnJbOI1hN/SxNb29YnmEN8yr/ws67GBKP4GYx4/CGzaG28SxNzg1DiWjGYv+O4BGGQ6tr1s5M3vxxeeFUFpzDH698Gf4e42/VylJaPhmREwaHOkF3V5w3PLDeMrkwEDBd28HW2ThszRP4NCz0wVitah3g+KVreHqDtbUF0cTixpWx8WmJNhxdXgzY1QNT66G+zZwAjvKL4qo5+bNkpgL7IBc+XfO2IO+igCB2vupOl2ru8emor+c02zM1VgvAR7to6xXZdxBr8n61fD3Bh61e53uWbvT6XS2KAez28rm50Qy20N0lYIp2c+obUwGScIzPjPHH0cLywwn/VGFL1XCNHMknPH2hKf6W3DnhTP+k/7jmaPjcbe7BRm14I13Kvx4ihSSqJCmzaJam7yeSbfTPQ22EQoNP2UyuGFpJHaVYf++3K67tsHDEIgZQr3uOEvpJF5jrvsTEpIF2vLaYDLTWNDGZuyPRxqMCYeRNJ3h1Vcn6GiLu9sJOsaZCH/a2lNzRhKhMqLYDZN+rPlzbWIqhCj06VNbbEoxpRK4awOtvYgFzyxREpZJHiryxJTWJzdwlV+kn5gw78/QqHwh+Q2P2YxhMhfeEmdMmqy2/RZ2Uimg+ne+GoaDq1+bSQALbbhM1ASMaR9TvUKxYCuMgAbzy5rqILrtCGvx7dcs1aPgaDsWs/SGSwH1uTa6yvpKvL7wh7WO6TS9JS6JAaQEOdQi9+EQXMhyyaBm2XfAoowlCyG/J+68xxGtYwzc/SQ0yw2hNUkjLKkHs2iV9mvLq/Dh1sWGFN6trxwO8q+p9baUtLY7Oj95/etwv9js9dGYZzTjN35llBsmQT5pes3TGbio916K5V6L7L1iEc+TPSPNey/4bL4HLNDHNHLT00x16tNBBElQVQekKcHgcGWAqoB1GHQwMvcWfIgRm/K0nMilIRQPl3jkSRE8wRURyxTqxkYkoSmdGd/T5dW70fvgjZy1yFUaBuQJfKGVJ/kwapsiKamAqoBT7h215Iymrl3Lci60MuDKJkNmgsxZvAC9Dx51xUIQTm3Zgp7Q1tdCpH6LGEYTRWgohTKG81LIOFohoulNFKRcZcFM3IDPoo2qCMS1rgzM5chmooos2aF14bjeaGFAUKumHigKuwna9i+yCIUgei8VkmfICCLZjJr+k54KuB8Fa0a8RhM61I1UbGuCPCUT006TpuFcSPOxHdojM/ojn5tnSpT5J8Ae2JwXbEc5gaaGeHVhoyJhKcUxZstpZoATrsl7aG7LbCXkO9jXMBb935AtJAuhj04bkiwNQBv6ZD7x8h2Z1tD+JR0hL2wlZuQ4/lwa6QRaYPKE/WnjcuxAacxd2t6CZvOn6EKtPJzwmTniPyWZzFkZuqFNCazwy9GYD+MtKOM4BRYc7CqzXAJ7DLKm+dWYUJ+b5pX/3J3TAqCN3K0DbhSFO6FrAiso3xHwVGW0OI6upRMULDfvEvsu4ZFdJGEs8qhYDwP90W5LUi96GtGMNi+RV/irsS3C0qtwfi2uFWgUjeGBsQWpnwyZUubsYldMadbwQrCQQktEEW5bJIybX9qf75YPP+QLX9Hr9mdI/jAzNgukATlP6Iw1oKYJb9NJGHV7h43atcB+pSGQq6E7lhs6WVagbD4i51pM4CERR/4qsQPShAscSYDIa+Ss8eE75czDYQdYHNnvRuMm5J7fGtMGS6eCa9P142FLaDjnKQMFsxEyfCHwXtgUl3/KGG+gTe9+a1OsKOObMq62vjbFI9msMKLvxlF6tBG+1UeRCK9BVlEhDe3nhuVlfiMqo3AlHcem7g5oI/ObXtdqLmQ2NttCYWdZq8DgaztltGL3dsMiDZeF5VdKSsRsTX7n9WZieQRrfqWRaCtQaY2zPTbQdN6C2hJr5c3NkN4fHaZ+kkfk/Zvhm6fkhVhq0yehUPRYsZ9qYylZGeRuS4Os1ufE6XQzhMBKrt7PC7l9YT41ALlKp8KXVtwW9OvE6hpPQPX3jeKJ+8bFYORH1HAbQxKwUAW3CVajf4RXwhT7o+ujVPFmJXVDuJIzqyV9NWtK+RXNpdLXkXdaUAQungq21/EKFUxyHtdR1jnqdu+97umw2znb22w4b0YEMPhu+OaBhCJijevgrrGoTLIsnG8+GIvFJGilt04Cr/MJkynL4F4E5fAX/7sGuMXvztgrW24FUOJL4d1atXhprWYtDfpumatSfCGiZrWz1WL2KLAQpsFKnbkaVd6gw++L6a2IyIerYR2R/r9a0PDhJlVArCMTUU3lfyEyG/1dR4bq8n++WDF7P48TuljwdIbP7v3PhqvIGzFuJAld1IcMWVzmdu27G7c3tubBSwaNWBTLHpbFBdwVjI7YIha3ifVOPBjiAu4KxNoQZNM8fvApe4BXoF5jB90XsQO7Fm2z0ffleA1c3GBQlxe7y1v3RQNc/LHYV9yhtmkfKGCTrTYB9nlTsxMxBOwzC/PMux0lDaYnzvgPEYtrTts0z0TEFVx8FNP/l/mVDPGXW+I/R7yT91rvSQMofxfGcTiQq7yM+FxgXEzle44tXGo23B/DO8TUDcDzJzbj5He5plegu6DhHHMYTVlCF2yCDeSw/gbjUCPOxQ1j+y6VUZnli5JPk5gCOImJc3FOwQzLLtOEZXpiEu++gG8sA5PclGmAL/THFgZTwNDAY05jKECijBP96m3LupZA3HnUgqxkuAwrDQlc55kCyjSTEGNvF1JEeZhtT0iIDnRrF8FoM9HN7S609xaXEtrHyuWxPPEw769B7QVSbInZvGtJXUzfkwVFZJ6mphFW8zhs4ditsX949xJL9+ujCqBDaYWR3EX0MJebd5QqsP7mSiXa+S2pciKOR0qaZ3OWZi5G1JS1s2otFrNCi70UM1MHFCKL0nX3IbF9POZp+bqjNM1YzAL9WOAVlmsiLV6+35WWViY44AadaXQDFMDhKcMmWjQK/GDfUsFVOlEizjMGG4K9+oRBOgzeHdVT8vHghsqDWMwOMIw2FrOPQX2eWCYRS4Q81GRHJirMFrytTlnM8NrJzpscEKhzqB9sGKSYThUr6xSvet792GBgYsEeTCQAXoBKVoRW75b0YZcmD0UhLbkGoklTkViwttABpqAkLsjHKotEnj3Wq0H/zaR8XB4eTxd55nt6i+GAVbCWKgDAxJ9W+FXwygTXw0ZTrlkJpBReEV28SS/XqzDlaTWKj0SYcGW8gjfIFWZ8oT685DGDu0ujIFDcy0y5VbBaaVhKmvhyEUGAhH3OJC18s2a3hBvj2+ah2F8fbCgWoCuXBnhKxZwqQ7B342M4ID6kApvnCTWyCneUFtHdTNn5MCyiyjCs0byQYiYfbuVWYxsRfIPamsZ0ptYBa1b38KrF0MTqeZYtAhsAEuD2N45ZOqtsWQ2Xw6VXJyK6DSa3hRPrzjuUSs2R9aedwsXo5lx/ZfURqVqlqs7B0vC0hqgcobe0iZzOQVAmstnpPWMrNTHEok5ElMebxR6UHr2T7FrUx5nt0bMRcMy02AS6uSEKaJbJB41u8OEW4o1eK0i3LEInyQ2VXK9mRZaSZxlL9fnRQHisyL9Gb14Db/TCmkGTAsm9XD+bN+ZdJ0DiahHusjSduDESzDM5cQcqw8XtqRp/wcNkAa7y7YXravDqLfi/m0DWbnQ3B2lOZGWQs/uD/LkAWYJJ/8wr9ZU28nBoUZznk5Wrt/j+Du+OHwVhIQY1XCUjkTRtvWvQ6OVvgNSBs/RTznJmFmENR+Q3KF2Lw8KC0Jg6KujVod8dNzjtt5uNA0WKnlwlD5zKEybHZVV8LxbhMRrg+TXVSCkq8FPOIFgCXDNfODcLzSVHlOX1mk6v6dbymokFVki5NyV+0YgR0GrefsHkDYKCtUjNh18PBhGekwR2f06YUnTW4M+9ZrcPQbhrdtsyFfG0fRJhTVm98s3veLqAhgxWda8c0yQW4XVt3yT3WLdICwjEfRKKZKHPtSzaNyhIgaI2hjmjEZOqhhvydzdDfm6LqYopDsQAxRorqkgOREq0XFetonyN+W/vf6/Z7T+fkv8FOv5zL/jH/wUAAP//r2U63A==" } diff --git a/filebeat/input/kafka/config.go b/filebeat/input/kafka/config.go index b132a843055..0e4888b90c3 100644 --- a/filebeat/input/kafka/config.go +++ b/filebeat/input/kafka/config.go @@ -180,7 +180,7 @@ func newSaramaConfig(config kafkaInputConfig) (*sarama.Config, error) { k.Net.TLS.Config = tls.BuildModuleConfig("") } - if config.Kerberos != nil { + if config.Kerberos.IsEnabled() { cfgwarn.Beta("Kerberos authentication for Kafka is beta.") k.Net.SASL.Enable = true diff --git a/filebeat/input/log/input.go b/filebeat/input/log/input.go index ac0d71cf53d..b3cf4049551 100644 --- a/filebeat/input/log/input.go +++ b/filebeat/input/log/input.go @@ -161,7 +161,7 @@ func NewInput( // It goes through all states coming from the registry. Only the states which match the glob patterns of // the input will be loaded and updated. All other states will not be touched. func (p *Input) loadStates(states []file.State) error { - logp.Debug("input", "exclude_files: %s. Number of stats: %d", p.config.ExcludeFiles, len(states)) + logp.Debug("input", "exclude_files: %s. Number of states: %d", p.config.ExcludeFiles, len(states)) for _, state := range states { // Check if state source belongs to this input. If yes, update the state. diff --git a/filebeat/input/syslog/config.go b/filebeat/input/syslog/config.go index 5b6ac1452b4..ff009bfb1dd 100644 --- a/filebeat/input/syslog/config.go +++ b/filebeat/input/syslog/config.go @@ -30,6 +30,7 @@ import ( "github.com/elastic/beats/v7/filebeat/inputsource/udp" "github.com/elastic/beats/v7/filebeat/inputsource/unix" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -98,6 +99,8 @@ func factory( return tcp.New(&config.Config, factory) case unix.Name: + cfgwarn.Beta("Syslog Unix socket support is beta.") + config := defaultUnix if err := cfg.Unpack(&config); err != nil { return nil, err diff --git a/filebeat/input/unix/input.go b/filebeat/input/unix/input.go index 12c091f00da..19609cb5ab8 100644 --- a/filebeat/input/unix/input.go +++ b/filebeat/input/unix/input.go @@ -30,6 +30,7 @@ import ( "github.com/elastic/beats/v7/filebeat/inputsource/unix" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -56,6 +57,7 @@ func NewInput( connector channel.Connector, context input.Context, ) (input.Input, error) { + cfgwarn.Beta("Unix socket support is beta.") out, err := connector.ConnectWith(cfg, beat.ClientConfig{ Processing: beat.ProcessingConfig{ diff --git a/filebeat/inputsource/tcp/server_test.go b/filebeat/inputsource/tcp/server_test.go index 15831666206..032f7d33e29 100644 --- a/filebeat/inputsource/tcp/server_test.go +++ b/filebeat/inputsource/tcp/server_test.go @@ -213,7 +213,7 @@ func TestReceiveNewEventsConcurrently(t *testing.T) { to := func(message []byte, mt inputsource.NetworkMetadata) { ch <- &info{message: string(message), mt: mt} } - cfg, err := common.NewConfigFrom(map[string]interface{}{"host": ":0"}) + cfg, err := common.NewConfigFrom(map[string]interface{}{"host": "127.0.0.1:0"}) if !assert.NoError(t, err) { return } diff --git a/filebeat/inputsource/unix/config.go b/filebeat/inputsource/unix/config.go index 79b2a43dd08..5051ab86e75 100644 --- a/filebeat/inputsource/unix/config.go +++ b/filebeat/inputsource/unix/config.go @@ -30,6 +30,8 @@ const Name = "unix" // Config exposes the unix configuration. type Config struct { Path string `config:"path"` + Group *string `config:"group"` + Mode *string `config:"mode"` Timeout time.Duration `config:"timeout" validate:"nonzero,positive"` MaxMessageSize cfgtype.ByteSize `config:"max_message_size" validate:"nonzero,positive"` MaxConnections int `config:"max_connections"` diff --git a/filebeat/inputsource/unix/server.go b/filebeat/inputsource/unix/server.go index 965a3300282..ee9a0f4564d 100644 --- a/filebeat/inputsource/unix/server.go +++ b/filebeat/inputsource/unix/server.go @@ -20,10 +20,16 @@ package unix import ( "fmt" "net" + "os" + "os/user" + "runtime" + "strconv" + "github.com/pkg/errors" "golang.org/x/net/netutil" "github.com/elastic/beats/v7/filebeat/inputsource/common" + "github.com/elastic/beats/v7/libbeat/logp" ) // Server represent a unix server @@ -55,13 +61,91 @@ func New( } func (s *Server) createServer() (net.Listener, error) { + if err := s.cleanupStaleSocket(); err != nil { + return nil, err + } + l, err := net.Listen("unix", s.config.Path) if err != nil { return nil, err } + if err := s.setSocketOwnership(); err != nil { + return nil, err + } + + if err := s.setSocketMode(); err != nil { + return nil, err + } + if s.config.MaxConnections > 0 { return netutil.LimitListener(l, s.config.MaxConnections), nil } return l, nil } + +func (s *Server) cleanupStaleSocket() error { + path := s.config.Path + info, err := os.Lstat(path) + if err != nil { + // If the file does not exist, then the cleanup can be considered successful. + if os.IsNotExist(err) { + return nil + } + return errors.Wrapf(err, "cannot lstat unix socket file at location %s", path) + } + + if runtime.GOOS != "windows" { + // see https://github.com/golang/go/issues/33357 for context on Windows socket file attributes bug + if info.Mode()&os.ModeSocket == 0 { + return fmt.Errorf("refusing to remove file at location %s, it is not a socket", path) + } + } + + if err := os.Remove(path); err != nil { + return errors.Wrapf(err, "cannot remove existing unix socket file at location %s", path) + } + + return nil +} + +func (s *Server) setSocketOwnership() error { + if s.config.Group != nil { + if runtime.GOOS == "windows" { + logp.NewLogger("unix").Warn("windows does not support the 'group' configuration option, ignoring") + return nil + } + g, err := user.LookupGroup(*s.config.Group) + if err != nil { + return err + } + gid, err := strconv.Atoi(g.Gid) + if err != nil { + return err + } + return os.Chown(s.config.Path, -1, gid) + } + return nil +} + +func (s *Server) setSocketMode() error { + if s.config.Mode != nil { + mode, err := parseFileMode(*s.config.Mode) + if err != nil { + return err + } + return os.Chmod(s.config.Path, mode) + } + return nil +} + +func parseFileMode(mode string) (os.FileMode, error) { + parsed, err := strconv.ParseUint(mode, 8, 32) + if err != nil { + return 0, err + } + if parsed > 0777 { + return 0, errors.New("invalid file mode") + } + return os.FileMode(parsed), nil +} diff --git a/filebeat/inputsource/unix/server_test.go b/filebeat/inputsource/unix/server_test.go index 36e75c757e9..a9043d14a8e 100644 --- a/filebeat/inputsource/unix/server_test.go +++ b/filebeat/inputsource/unix/server_test.go @@ -23,7 +23,10 @@ import ( "math/rand" "net" "os" + "os/user" "path/filepath" + "runtime" + "strconv" "strings" "testing" "time" @@ -35,6 +38,7 @@ import ( "github.com/elastic/beats/v7/filebeat/inputsource" netcommon "github.com/elastic/beats/v7/filebeat/inputsource/common" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/file" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -207,6 +211,92 @@ func TestReceiveEventsAndMetadata(t *testing.T) { } } +func TestSocketOwnershipAndMode(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("changing socket ownership is only supported on non-windows") + return + } + + groups, err := os.Getgroups() + require.NoError(t, err) + + if len(groups) <= 1 { + t.Skip("no group that we can change to") + return + } + + group, err := user.LookupGroupId(strconv.Itoa(groups[1])) + require.NoError(t, err) + + path := filepath.Join(os.TempDir(), "test.sock") + cfg, _ := common.NewConfigFrom(map[string]interface{}{ + "path": path, + "group": group.Name, + "mode": "0740", + }) + config := defaultConfig + err = cfg.Unpack(&config) + require.NoError(t, err) + + factory := netcommon.SplitHandlerFactory(netcommon.FamilyUnix, logp.NewLogger("test"), MetadataCallback, nil, netcommon.SplitFunc([]byte("\n"))) + server, err := New(&config, factory) + require.NoError(t, err) + err = server.Start() + require.NoError(t, err) + defer server.Stop() + + info, err := file.Lstat(path) + require.NoError(t, err) + require.NotEqual(t, 0, info.Mode()&os.ModeSocket) + require.Equal(t, os.FileMode(0740), info.Mode().Perm()) + gid, err := info.GID() + require.NoError(t, err) + require.Equal(t, group.Gid, strconv.Itoa(gid)) +} + +func TestSocketCleanup(t *testing.T) { + path := filepath.Join(os.TempDir(), "test.sock") + mockStaleSocket, err := net.Listen("unix", path) + require.NoError(t, err) + defer mockStaleSocket.Close() + + cfg, _ := common.NewConfigFrom(map[string]interface{}{ + "path": path, + }) + config := defaultConfig + require.NoError(t, cfg.Unpack(&config)) + factory := netcommon.SplitHandlerFactory(netcommon.FamilyUnix, logp.NewLogger("test"), MetadataCallback, nil, netcommon.SplitFunc([]byte("\n"))) + server, err := New(&config, factory) + require.NoError(t, err) + err = server.Start() + require.NoError(t, err) + server.Stop() +} + +func TestSocketCleanupRefusal(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping due to windows FileAttributes bug https://github.com/golang/go/issues/33357") + return + } + path := filepath.Join(os.TempDir(), "test.sock") + f, err := os.Create(path) + require.NoError(t, err) + require.NoError(t, f.Close()) + defer os.Remove(path) + + cfg, _ := common.NewConfigFrom(map[string]interface{}{ + "path": path, + }) + config := defaultConfig + require.NoError(t, cfg.Unpack(&config)) + factory := netcommon.SplitHandlerFactory(netcommon.FamilyUnix, logp.NewLogger("test"), MetadataCallback, nil, netcommon.SplitFunc([]byte("\n"))) + server, err := New(&config, factory) + require.NoError(t, err) + err = server.Start() + require.Error(t, err) + require.Contains(t, err.Error(), "refusing to remove file at location") +} + func TestReceiveNewEventsConcurrently(t *testing.T) { workers := 4 eventsCount := 100 diff --git a/filebeat/magefile.go b/filebeat/magefile.go index 0c3d8881892..6270c51f205 100644 --- a/filebeat/magefile.go +++ b/filebeat/magefile.go @@ -176,8 +176,6 @@ func ExportDashboard() error { // IntegTest executes integration tests (it uses Docker to run the tests). func IntegTest() { - devtools.AddIntegTestUsage() - defer devtools.StopIntegTestEnv() mg.SerialDeps(GoIntegTest, PythonIntegTest) } @@ -185,7 +183,13 @@ func IntegTest() { // Use TEST_COVERAGE=true to enable code coverage profiling. // Use RACE_DETECTOR=true to enable the race detector. func GoIntegTest(ctx context.Context) error { - return devtools.GoTest(ctx, devtools.DefaultGoTestIntegrationArgs()) + runner, err := devtools.NewDockerIntegrationRunner() + if err != nil { + return err + } + return runner.Test("goIntegTest", func() error { + return devtools.GoTest(ctx, devtools.DefaultGoTestIntegrationArgs()) + }) } // PythonIntegTest executes the python system tests in the integration environment (Docker). @@ -196,10 +200,14 @@ func PythonIntegTest(ctx context.Context) error { if !devtools.IsInIntegTestEnv() { mg.Deps(Fields) } - return devtools.RunIntegTest("pythonIntegTest", func() error { + runner, err := devtools.NewDockerIntegrationRunner(append(devtools.ListMatchingEnvVars("TESTING_FILEBEAT_", "NOSE_"), "GENERATE")...) + if err != nil { + return err + } + return runner.Test("pythonIntegTest", func() error { mg.Deps(devtools.BuildSystemTestBinary) args := devtools.DefaultPythonTestIntegrationArgs() args.Env["MODULES_PATH"] = devtools.CWD("module") return devtools.PythonNoseTest(args) - }, append(devtools.ListMatchingEnvVars("TESTING_FILEBEAT_", "NOSE_"), "GENERATE")...) + }) } diff --git a/filebeat/module/elasticsearch/deprecation/config/log.yml b/filebeat/module/elasticsearch/deprecation/config/log.yml index 14cdbbe9414..292a917e767 100644 --- a/filebeat/module/elasticsearch/deprecation/config/log.yml +++ b/filebeat/module/elasticsearch/deprecation/config/log.yml @@ -10,5 +10,5 @@ multiline: match: after processors: -# Locale for timezone is only needed in non-json logs +# Locale for time zone is only needed in non-json logs - add_locale.when.not.regexp.message: "^{" diff --git a/filebeat/module/elasticsearch/server/config/log.yml b/filebeat/module/elasticsearch/server/config/log.yml index 7d7e969a9b8..98be6d7adb6 100644 --- a/filebeat/module/elasticsearch/server/config/log.yml +++ b/filebeat/module/elasticsearch/server/config/log.yml @@ -10,5 +10,5 @@ multiline: match: after processors: -# Locale for timezone is only needed in non-json logs -- add_locale.when.not.regexp.message: "^{" +# Locale for time zone is only needed in non-json logs +- add_locale.when.not.regexp.message: "^{" diff --git a/filebeat/module/elasticsearch/slowlog/config/slowlog.yml b/filebeat/module/elasticsearch/slowlog/config/slowlog.yml index d6a75034b2d..03c14625b28 100644 --- a/filebeat/module/elasticsearch/slowlog/config/slowlog.yml +++ b/filebeat/module/elasticsearch/slowlog/config/slowlog.yml @@ -11,5 +11,5 @@ multiline: match: after processors: -# Locale for timezone is only needed in non-json logs +# Locale for time zone is only needed in non-json logs - add_locale.when.not.regexp.message: "^{" diff --git a/filebeat/module/logstash/log/config/log.yml b/filebeat/module/logstash/log/config/log.yml index d90907fb16c..c32b22b82d5 100644 --- a/filebeat/module/logstash/log/config/log.yml +++ b/filebeat/module/logstash/log/config/log.yml @@ -13,5 +13,5 @@ multiline: {{ end }} processors: -# Locale for timezone is only needed in non-json logs +# Locale for time zone is only needed in non-json logs - add_locale.when.not.regexp.message: "^{" diff --git a/filebeat/module/logstash/slowlog/config/slowlog.yml b/filebeat/module/logstash/slowlog/config/slowlog.yml index e8c035e32cc..270ba255609 100644 --- a/filebeat/module/logstash/slowlog/config/slowlog.yml +++ b/filebeat/module/logstash/slowlog/config/slowlog.yml @@ -6,5 +6,5 @@ paths: exclude_files: [".gz$"] processors: -# Locale for timezone is only needed in non-json logs +# Locale for time zone is only needed in non-json logs - add_locale.when.not.regexp.message: "^{" diff --git a/filebeat/module/nginx/access/ingest/default.json b/filebeat/module/nginx/access/ingest/default.json deleted file mode 100644 index 04efd885e69..00000000000 --- a/filebeat/module/nginx/access/ingest/default.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "description": "Pipeline for parsing Nginx access logs. Requires the geoip and user_agent plugins.", - "processors": [ - { - "grok": { - "field": "message", - "patterns": [ - "(%{NGINX_HOST} )?\"?(?:%{NGINX_ADDRESS_LIST:nginx.access.remote_ip_list}|%{NOTSPACE:source.address}) - %{DATA:user.name} \\[%{HTTPDATE:nginx.access.time}\\] \"%{DATA:nginx.access.info}\" %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} \"%{DATA:http.request.referrer}\" \"%{DATA:user_agent.original}\"" - ], - "pattern_definitions": { - "NGINX_HOST": "(?:%{IP:destination.ip}|%{NGINX_NOTSEPARATOR:destination.domain})(:%{NUMBER:destination.port})?", - "NGINX_NOTSEPARATOR": "[^\t ,:]+", - "NGINX_ADDRESS_LIST": "(?:%{IP}|%{WORD})(\"?,?\\s*(?:%{IP}|%{WORD}))*" - }, - "ignore_missing": true - } - }, - { - "grok": { - "field": "nginx.access.info", - "patterns": [ - "%{WORD:http.request.method} %{DATA:url.original} HTTP/%{NUMBER:http.version}", - "" - ], - "ignore_missing": true - } - }, - { - "remove": { - "field": "nginx.access.info" - } - }, - { - "split": { - "field": "nginx.access.remote_ip_list", - "separator": "\"?,?\\s+", - "ignore_missing": true - } - }, - { - "split": { - "field": "nginx.access.origin", - "separator": "\"?,?\\s+", - "ignore_missing": true - } - }, - { - "set": { - "field": "source.address", - "if": "ctx.source?.address == null", - "value": "" - } - }, - { - "script": { - "if": "ctx.nginx?.access?.remote_ip_list != null && ctx.nginx.access.remote_ip_list.length > 0", - "lang": "painless", - "source": "boolean isPrivate(def dot, def ip) { try { StringTokenizer tok = new StringTokenizer(ip, dot); int firstByte = Integer.parseInt(tok.nextToken()); int secondByte = Integer.parseInt(tok.nextToken()); if (firstByte == 10) { return true; } if (firstByte == 192 && secondByte == 168) { return true; } if (firstByte == 172 && secondByte >= 16 && secondByte <= 31) { return true; } if (firstByte == 127) { return true; } return false; } catch (Exception e) { return false; } } try { ctx.source.address = null; if (ctx.nginx.access.remote_ip_list == null) { return; } def found = false; for (def item : ctx.nginx.access.remote_ip_list) { if (!isPrivate(params.dot, item)) { ctx.source.address = item; found = true; break; } } if (!found) { ctx.source.address = ctx.nginx.access.remote_ip_list[0]; }} catch (Exception e) { ctx.source.address = null; }", - "params": { - "dot": "." - } - } - }, - { - "remove": { - "field": "source.address", - "if": "ctx.source.address == null" - } - }, - { - "grok": { - "field": "source.address", - "patterns": ["^%{IP:source.ip}$"], - "ignore_failure": true - } - }, - { - "remove": { - "field": "message" - } - }, - { - "rename": { - "field": "@timestamp", - "target_field": "event.created" - } - }, - { - "date": { - "field": "nginx.access.time", - "target_field": "@timestamp", - "formats": [ - "dd/MMM/yyyy:H:m:s Z" - ], - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, - { - "remove": { - "field": "nginx.access.time" - } - }, - { - "user_agent": { - "field": "user_agent.original" - } - }, - { - "geoip": { - "field": "source.ip", - "target_field": "source.geo", - "ignore_missing": true - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "source.ip", - "target_field": "source.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.asn", - "target_field": "source.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.organization_name", - "target_field": "source.as.organization.name", - "ignore_missing": true - } - } - ], - "on_failure": [ - { - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - } - ] -} diff --git a/filebeat/module/nginx/access/ingest/pipeline.yml b/filebeat/module/nginx/access/ingest/pipeline.yml new file mode 100644 index 00000000000..3a41265875b --- /dev/null +++ b/filebeat/module/nginx/access/ingest/pipeline.yml @@ -0,0 +1,167 @@ +description: Pipeline for parsing Nginx access logs. Requires the geoip and user_agent + plugins. +processors: +- grok: + field: message + patterns: + - (%{NGINX_HOST} )?"?(?:%{NGINX_ADDRESS_LIST:nginx.access.remote_ip_list}|%{NOTSPACE:source.address}) + - (-|%{DATA:user.name}) \[%{HTTPDATE:nginx.access.time}\] "%{DATA:nginx.access.info}" + %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} + "(-|%{DATA:http.request.referrer})" "(-|%{DATA:user_agent.original})" + pattern_definitions: + NGINX_HOST: (?:%{IP:destination.ip}|%{NGINX_NOTSEPARATOR:destination.domain})(:%{NUMBER:destination.port})? + NGINX_NOTSEPARATOR: "[^\t ,:]+" + NGINX_ADDRESS_LIST: (?:%{IP}|%{WORD})("?,?\s*(?:%{IP}|%{WORD}))* + ignore_missing: true +- grok: + field: nginx.access.info + patterns: + - '%{WORD:http.request.method} %{DATA:url.original} HTTP/%{NUMBER:http.version}' + - "" + ignore_missing: true +- remove: + field: nginx.access.info +- split: + field: nginx.access.remote_ip_list + separator: '"?,?\s+' + ignore_missing: true +- split: + field: nginx.access.origin + separator: '"?,?\s+' + ignore_missing: true +- set: + field: source.address + if: ctx.source?.address == null + value: "" +- script: + if: ctx.nginx?.access?.remote_ip_list != null && ctx.nginx.access.remote_ip_list.length > 0 + lang: painless + source: >- + boolean isPrivate(def dot, def ip) { + try { + StringTokenizer tok = new StringTokenizer(ip, dot); + int firstByte = Integer.parseInt(tok.nextToken()); + int secondByte = Integer.parseInt(tok.nextToken()); + if (firstByte == 10) { + return true; + } + if (firstByte == 192 && secondByte == 168) { + return true; + } + if (firstByte == 172 && secondByte >= 16 && secondByte <= 31) { + return true; + } + if (firstByte == 127) { + return true; + } + return false; + } + catch (Exception e) { + return false; + } + } + try { + ctx.source.address = null; + if (ctx.nginx.access.remote_ip_list == null) { + return; + } + def found = false; + for (def item : ctx.nginx.access.remote_ip_list) { + if (!isPrivate(params.dot, item)) { + ctx.source.address = item; + found = true; + break; + } + } + if (!found) { + ctx.source.address = ctx.nginx.access.remote_ip_list[0]; + } + } + catch (Exception e) { + ctx.source.address = null; + } + params: + dot: . +- remove: + field: source.address + if: ctx.source.address == null +- grok: + field: source.address + patterns: + - ^%{IP:source.ip}$ + ignore_failure: true +- remove: + field: message +- rename: + field: '@timestamp' + target_field: event.created +- date: + field: nginx.access.time + target_field: '@timestamp' + formats: + - dd/MMM/yyyy:H:m:s Z + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- remove: + field: nginx.access.time +- user_agent: + field: user_agent.original + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- set: + field: event.kind + value: event +- append: + field: event.category + value: web +- append: + field: event.type + value: access +- set: + field: event.outcome + value: success + if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code < 400" +- set: + field: event.outcome + value: failure + if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code >= 400" +- lowercase: + field: http.request.method + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/nginx/access/manifest.yml b/filebeat/module/nginx/access/manifest.yml index d3d6211dccc..8d04dd32e5c 100644 --- a/filebeat/module/nginx/access/manifest.yml +++ b/filebeat/module/nginx/access/manifest.yml @@ -9,7 +9,7 @@ var: os.windows: - c:/programdata/nginx/logs/*access.log* -ingest_pipeline: ingest/default.json +ingest_pipeline: ingest/pipeline.yml input: config/nginx-access.yml requires.processors: diff --git a/filebeat/module/nginx/access/test/access.log-expected.json b/filebeat/module/nginx/access/test/access.log-expected.json index a121dd67613..12c94f2996d 100644 --- a/filebeat/module/nginx/access/test/access.log-expected.json +++ b/filebeat/module/nginx/access/test/access.log-expected.json @@ -1,12 +1,19 @@ [ { "@timestamp": "2016-10-25T12:49:33.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 200, "http.version": "1.1", @@ -15,6 +22,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -28,7 +38,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36", @@ -39,11 +48,19 @@ }, { "@timestamp": "2016-10-25T12:49:34.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", + "http.request.method": "get", "http.request.referrer": "http://localhost:8080/", "http.response.body.bytes": 571, "http.response.status_code": 404, @@ -53,6 +70,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -66,7 +86,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36", @@ -77,12 +96,19 @@ }, { "@timestamp": "2016-10-25T12:50:44.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 404, "http.version": "1.1", @@ -91,6 +117,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -104,7 +133,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/adsasd", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36", @@ -115,12 +143,19 @@ }, { "@timestamp": "2016-12-07T09:34:43.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 200, "http.version": "1.1", @@ -129,6 +164,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -142,7 +180,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36", @@ -153,11 +190,19 @@ }, { "@timestamp": "2016-12-07T09:34:43.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", + "http.request.method": "get", "http.request.referrer": "http://localhost:8080/", "http.response.body.bytes": 571, "http.response.status_code": 404, @@ -167,6 +212,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -180,7 +228,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36", @@ -191,12 +238,19 @@ }, { "@timestamp": "2016-12-07T09:43:18.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 404, "http.version": "1.1", @@ -205,6 +259,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -218,7 +275,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/test", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36", @@ -229,12 +285,19 @@ }, { "@timestamp": "2016-12-07T09:43:21.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 404, "http.version": "1.1", @@ -243,6 +306,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -256,7 +322,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/test", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36", @@ -267,12 +332,19 @@ }, { "@timestamp": "2016-12-07T09:43:23.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 404, "http.version": "1.1", @@ -281,6 +353,9 @@ "nginx.access.remote_ip_list": [ "77.179.66.156" ], + "related.ip": [ + "77.179.66.156" + ], "service.type": "nginx", "source.address": "77.179.66.156", "source.as.number": 6805, @@ -294,7 +369,6 @@ "source.geo.region_name": "Rheinland-Pfalz", "source.ip": "77.179.66.156", "url.original": "/test1", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36", @@ -305,12 +379,19 @@ }, { "@timestamp": "2016-12-07T10:04:37.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 404, "http.version": "1.1", @@ -319,11 +400,13 @@ "nginx.access.remote_ip_list": [ "127.0.0.1" ], + "related.ip": [ + "127.0.0.1" + ], "service.type": "nginx", "source.address": "127.0.0.1", "source.ip": "127.0.0.1", "url.original": "/test1", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36", @@ -334,12 +417,19 @@ }, { "@timestamp": "2016-12-07T10:04:58.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 0, "http.response.status_code": 304, "http.version": "1.1", @@ -348,11 +438,13 @@ "nginx.access.remote_ip_list": [ "127.0.0.1" ], + "related.ip": [ + "127.0.0.1" + ], "service.type": "nginx", "source.address": "127.0.0.1", "source.ip": "127.0.0.1", "url.original": "/", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0", @@ -363,12 +455,19 @@ }, { "@timestamp": "2016-12-07T10:04:59.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 0, "http.response.status_code": 304, "http.version": "1.1", @@ -377,11 +476,13 @@ "nginx.access.remote_ip_list": [ "127.0.0.1" ], + "related.ip": [ + "127.0.0.1" + ], "service.type": "nginx", "source.address": "127.0.0.1", "source.ip": "127.0.0.1", "url.original": "/", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0", @@ -392,12 +493,19 @@ }, { "@timestamp": "2016-12-07T10:05:07.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 169, "http.response.status_code": 404, "http.version": "1.1", @@ -406,11 +514,13 @@ "nginx.access.remote_ip_list": [ "127.0.0.1" ], + "related.ip": [ + "127.0.0.1" + ], "service.type": "nginx", "source.address": "127.0.0.1", "source.ip": "127.0.0.1", "url.original": "/taga", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0", diff --git a/filebeat/module/nginx/access/test/test-with-host.log-expected.json b/filebeat/module/nginx/access/test/test-with-host.log-expected.json index 38695946ca5..a641922d139 100644 --- a/filebeat/module/nginx/access/test/test-with-host.log-expected.json +++ b/filebeat/module/nginx/access/test/test-with-host.log-expected.json @@ -2,12 +2,19 @@ { "@timestamp": "2016-12-07T10:05:07.000Z", "destination.domain": "example.com", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 200, "http.version": "1.1", @@ -18,11 +25,13 @@ "10.0.0.1", "127.0.0.1" ], + "related.ip": [ + "10.0.0.2" + ], "service.type": "nginx", "source.address": "10.0.0.2", "source.ip": "10.0.0.2", "url.original": "/ocelot", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0", @@ -34,12 +43,19 @@ { "@timestamp": "2017-05-29T19:02:48.000Z", "destination.domain": "example.com", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 404, "http.version": "1.1", @@ -48,11 +64,13 @@ "nginx.access.remote_ip_list": [ "172.17.0.1" ], + "related.ip": [ + "172.17.0.1" + ], "service.type": "nginx", "source.address": "172.17.0.1", "source.ip": "172.17.0.1", "url.original": "/stringpatch", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox Alpha", "user_agent.original": "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2", @@ -64,12 +82,19 @@ { "@timestamp": "2016-12-07T10:05:07.000Z", "destination.domain": "example.com", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 200, "http.version": "1.1", @@ -80,6 +105,9 @@ "10.0.0.1", "85.181.35.98" ], + "related.ip": [ + "85.181.35.98" + ], "service.type": "nginx", "source.address": "85.181.35.98", "source.as.number": 6805, @@ -93,7 +121,6 @@ "source.geo.region_name": "Land Berlin", "source.ip": "85.181.35.98", "url.original": "/ocelot", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0", @@ -106,12 +133,19 @@ "@timestamp": "2016-12-07T10:05:07.000Z", "destination.domain": "example.com", "destination.port": "80", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 200, "http.version": "1.1", @@ -120,6 +154,9 @@ "nginx.access.remote_ip_list": [ "85.181.35.98" ], + "related.ip": [ + "85.181.35.98" + ], "service.type": "nginx", "source.address": "85.181.35.98", "source.as.number": 6805, @@ -133,7 +170,6 @@ "source.geo.region_name": "Land Berlin", "source.ip": "85.181.35.98", "url.original": "/ocelot", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36", @@ -146,12 +182,19 @@ "@timestamp": "2016-01-22T13:18:29.000Z", "destination.domain": "example.com", "destination.port": "80", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 25507, "http.response.status_code": 200, "http.version": "1.1", @@ -163,6 +206,9 @@ "204.246.1.1", "10.2.1.185" ], + "related.ip": [ + "199.96.1.1" + ], "service.type": "nginx", "source.address": "199.96.1.1", "source.as.number": 19065, @@ -176,7 +222,6 @@ "source.geo.region_name": "Illinois", "source.ip": "199.96.1.1", "url.original": "/assets/xxxx?q=100", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Other", "user_agent.original": "Amazon CloudFront" @@ -184,12 +229,19 @@ { "@timestamp": "2016-12-30T06:47:09.000Z", "destination.ip": "1.2.3.4", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 8571, "http.response.status_code": 404, "http.version": "1.1", @@ -200,6 +252,10 @@ "10.225.192.17", "10.2.2.121" ], + "related.ip": [ + "2a03:0000:10ff:f00f:0000:0000:0:8000", + "1.2.3.4" + ], "service.type": "nginx", "source.address": "2a03:0000:10ff:f00f:0000:0000:0:8000", "source.geo.continent_name": "Europe", @@ -208,7 +264,6 @@ "source.geo.location.lon": -8.0, "source.ip": "2a03:0000:10ff:f00f:0000:0000:0:8000", "url.original": "/test.html", - "user.name": "-", "user_agent.device.name": "Spider", "user_agent.name": "Facebot", "user_agent.original": "Mozilla/5.0 (compatible; Facebot 1.0; https://developers.facebook.com/docs/sharing/webmasters/crawler)", @@ -218,11 +273,18 @@ "@timestamp": "2018-04-12T07:48:40.000Z", "destination.ip": "1.2.3.4", "destination.port": "80", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.referrer": "-", "http.response.body.bytes": 0, "http.response.status_code": 400, "input.type": "log", @@ -230,43 +292,53 @@ "nginx.access.remote_ip_list": [ "127.0.0.1" ], + "related.ip": [ + "127.0.0.1", + "1.2.3.4" + ], "service.type": "nginx", "source.address": "127.0.0.1", - "source.ip": "127.0.0.1", - "user.name": "-", - "user_agent.device.name": "Other", - "user_agent.name": "Other", - "user_agent.original": "-" + "source.ip": "127.0.0.1" }, { "@timestamp": "2019-02-26T14:39:42.000Z", "destination.domain": "example.com", "destination.port": "80", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.referrer": "-", "http.response.body.bytes": 173, "http.response.status_code": 400, "input.type": "log", "log.offset": 1269, "service.type": "nginx", - "source.address": "unix:", - "user.name": "-", - "user_agent.device.name": "Other", - "user_agent.name": "Other", - "user_agent.original": "-" + "source.address": "unix:" }, { "@timestamp": "2017-05-29T19:02:48.000Z", "destination.ip": "1.2.3.4", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 200, "http.version": "1.1", @@ -275,10 +347,12 @@ "nginx.access.remote_ip_list": [ "localhost" ], + "related.ip": [ + "1.2.3.4" + ], "service.type": "nginx", "source.address": "localhost", "url.original": "/test2", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox Alpha", "user_agent.original": "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2", @@ -290,12 +364,19 @@ { "@timestamp": "2017-05-29T19:02:48.000Z", "destination.domain": "example.com", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 200, "http.version": "1.1", @@ -308,7 +389,6 @@ "service.type": "nginx", "source.address": "localhost", "url.original": "/test2", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox Alpha", "user_agent.original": "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2", diff --git a/filebeat/module/nginx/access/test/test.log-expected.json b/filebeat/module/nginx/access/test/test.log-expected.json index 247b7a12e21..22959d1a8be 100644 --- a/filebeat/module/nginx/access/test/test.log-expected.json +++ b/filebeat/module/nginx/access/test/test.log-expected.json @@ -1,12 +1,19 @@ [ { "@timestamp": "2016-12-07T10:05:07.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 200, "http.version": "1.1", @@ -17,11 +24,13 @@ "10.0.0.1", "127.0.0.1" ], + "related.ip": [ + "10.0.0.2" + ], "service.type": "nginx", "source.address": "10.0.0.2", "source.ip": "10.0.0.2", "url.original": "/ocelot", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0", @@ -32,12 +41,19 @@ }, { "@timestamp": "2017-05-29T19:02:48.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 404, "http.version": "1.1", @@ -46,11 +62,13 @@ "nginx.access.remote_ip_list": [ "172.17.0.1" ], + "related.ip": [ + "172.17.0.1" + ], "service.type": "nginx", "source.address": "172.17.0.1", "source.ip": "172.17.0.1", "url.original": "/stringpatch", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox Alpha", "user_agent.original": "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2", @@ -61,12 +79,19 @@ }, { "@timestamp": "2016-12-07T10:05:07.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 200, "http.version": "1.1", @@ -77,6 +102,9 @@ "10.0.0.1", "85.181.35.98" ], + "related.ip": [ + "85.181.35.98" + ], "service.type": "nginx", "source.address": "85.181.35.98", "source.as.number": 6805, @@ -90,7 +118,6 @@ "source.geo.region_name": "Land Berlin", "source.ip": "85.181.35.98", "url.original": "/ocelot", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0", @@ -101,12 +128,19 @@ }, { "@timestamp": "2016-12-07T10:05:07.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 571, "http.response.status_code": 200, "http.version": "1.1", @@ -115,6 +149,9 @@ "nginx.access.remote_ip_list": [ "85.181.35.98" ], + "related.ip": [ + "85.181.35.98" + ], "service.type": "nginx", "source.address": "85.181.35.98", "source.as.number": 6805, @@ -128,7 +165,6 @@ "source.geo.region_name": "Land Berlin", "source.ip": "85.181.35.98", "url.original": "/ocelot", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36", @@ -139,12 +175,19 @@ }, { "@timestamp": "2016-01-22T13:18:29.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 25507, "http.response.status_code": 200, "http.version": "1.1", @@ -156,6 +199,9 @@ "204.246.1.1", "10.2.1.185" ], + "related.ip": [ + "199.96.1.1" + ], "service.type": "nginx", "source.address": "199.96.1.1", "source.as.number": 19065, @@ -169,19 +215,25 @@ "source.geo.region_name": "Illinois", "source.ip": "199.96.1.1", "url.original": "/assets/xxxx?q=100", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Other", "user_agent.original": "Amazon CloudFront" }, { "@timestamp": "2016-12-30T06:47:09.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 8571, "http.response.status_code": 404, "http.version": "1.1", @@ -192,6 +244,9 @@ "10.225.192.17", "10.2.2.121" ], + "related.ip": [ + "2a03:0000:10ff:f00f:0000:0000:0:8000" + ], "service.type": "nginx", "source.address": "2a03:0000:10ff:f00f:0000:0000:0:8000", "source.geo.continent_name": "Europe", @@ -200,7 +255,6 @@ "source.geo.location.lon": -8.0, "source.ip": "2a03:0000:10ff:f00f:0000:0000:0:8000", "url.original": "/test.html", - "user.name": "-", "user_agent.device.name": "Spider", "user_agent.name": "Facebot", "user_agent.original": "Mozilla/5.0 (compatible; Facebot 1.0; https://developers.facebook.com/docs/sharing/webmasters/crawler)", @@ -208,11 +262,18 @@ }, { "@timestamp": "2018-04-12T07:48:40.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.referrer": "-", "http.response.body.bytes": 0, "http.response.status_code": 400, "input.type": "log", @@ -220,40 +281,49 @@ "nginx.access.remote_ip_list": [ "127.0.0.1" ], + "related.ip": [ + "127.0.0.1" + ], "service.type": "nginx", "source.address": "127.0.0.1", - "source.ip": "127.0.0.1", - "user.name": "-", - "user_agent.device.name": "Other", - "user_agent.name": "Other", - "user_agent.original": "-" + "source.ip": "127.0.0.1" }, { "@timestamp": "2019-02-26T14:39:42.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.referrer": "-", "http.response.body.bytes": 173, "http.response.status_code": 400, "input.type": "log", "log.offset": 1184, "service.type": "nginx", - "source.address": "unix:", - "user.name": "-", - "user_agent.device.name": "Other", - "user_agent.name": "Other", - "user_agent.original": "-" + "source.address": "unix:" }, { "@timestamp": "2017-05-29T19:02:48.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 200, "http.version": "1.1", @@ -265,7 +335,6 @@ "service.type": "nginx", "source.address": "localhost", "url.original": "/test2", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox Alpha", "user_agent.original": "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2", @@ -276,12 +345,19 @@ }, { "@timestamp": "2017-05-29T19:02:48.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.access", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "access" + ], "fileset.name": "access", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 612, "http.response.status_code": 200, "http.version": "1.1", @@ -294,7 +370,6 @@ "service.type": "nginx", "source.address": "localhost", "url.original": "/test2", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox Alpha", "user_agent.original": "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2", diff --git a/filebeat/module/nginx/error/ingest/pipeline.json b/filebeat/module/nginx/error/ingest/pipeline.json deleted file mode 100644 index 473fa087922..00000000000 --- a/filebeat/module/nginx/error/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for parsing the Nginx error logs", - "processors": [{ - "grok": { - "field": "message", - "patterns": [ - "%{DATA:nginx.error.time} \\[%{DATA:log.level}\\] %{NUMBER:process.pid:long}#%{NUMBER:process.thread.id:long}: (\\*%{NUMBER:nginx.error.connection_id:long} )?%{GREEDYMULTILINE:message}" - ], - "pattern_definitions": { - "GREEDYMULTILINE":"(.|\n|\t)*" - }, - "ignore_missing": true - } - }, { - "rename": { - "field": "@timestamp", - "target_field": "event.created" - } - }, { - "date": { - "if": "ctx.event.timezone == null", - "field": "nginx.error.time", - "target_field": "@timestamp", - "formats": ["yyyy/MM/dd H:m:s"], - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, { - "date": { - "if": "ctx.event.timezone != null", - "field": "nginx.error.time", - "target_field": "@timestamp", - "formats": ["yyyy/MM/dd H:m:s"], - "timezone": "{{ event.timezone }}", - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, { - "remove": { - "field": "nginx.error.time" - } - }], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/filebeat/module/nginx/error/ingest/pipeline.yml b/filebeat/module/nginx/error/ingest/pipeline.yml new file mode 100644 index 00000000000..5a33c34710c --- /dev/null +++ b/filebeat/module/nginx/error/ingest/pipeline.yml @@ -0,0 +1,51 @@ +description: Pipeline for parsing the Nginx error logs +processors: +- grok: + field: message + patterns: + - '%{DATA:nginx.error.time} \[%{DATA:log.level}\] %{NUMBER:process.pid:long}#%{NUMBER:process.thread.id:long}: + (\*%{NUMBER:nginx.error.connection_id:long} )?%{GREEDYMULTILINE:message}' + pattern_definitions: + GREEDYMULTILINE: |- + (.| + | )* + ignore_missing: true +- rename: + field: '@timestamp' + target_field: event.created +- date: + if: ctx.event.timezone == null + field: nginx.error.time + target_field: '@timestamp' + formats: + - yyyy/MM/dd H:m:s + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- date: + if: ctx.event.timezone != null + field: nginx.error.time + target_field: '@timestamp' + formats: + - yyyy/MM/dd H:m:s + timezone: '{{ event.timezone }}' + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- remove: + field: nginx.error.time +- set: + field: event.kind + value: event +- append: + field: event.category + value: web +- append: + field: event.type + value: error +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/nginx/error/manifest.yml b/filebeat/module/nginx/error/manifest.yml index 641ec771bbb..b83c154693d 100644 --- a/filebeat/module/nginx/error/manifest.yml +++ b/filebeat/module/nginx/error/manifest.yml @@ -9,5 +9,5 @@ var: os.windows: - c:/programdata/nginx/logs/error.log* -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/nginx-error.yml diff --git a/filebeat/module/nginx/error/test/error.log-expected.json b/filebeat/module/nginx/error/test/error.log-expected.json index 6252e87d66b..8896a490705 100644 --- a/filebeat/module/nginx/error/test/error.log-expected.json +++ b/filebeat/module/nginx/error/test/error.log-expected.json @@ -1,9 +1,16 @@ [ { "@timestamp": "2016-10-25T14:49:34.000-02:00", + "event.category": [ + "web" + ], "event.dataset": "nginx.error", + "event.kind": "event", "event.module": "nginx", "event.timezone": "-02:00", + "event.type": [ + "error" + ], "fileset.name": "error", "input.type": "log", "log.level": "error", @@ -16,9 +23,16 @@ }, { "@timestamp": "2016-10-25T14:50:44.000-02:00", + "event.category": [ + "web" + ], "event.dataset": "nginx.error", + "event.kind": "event", "event.module": "nginx", "event.timezone": "-02:00", + "event.type": [ + "error" + ], "fileset.name": "error", "input.type": "log", "log.level": "error", @@ -31,9 +45,16 @@ }, { "@timestamp": "2019-10-30T23:26:34.000-02:00", + "event.category": [ + "web" + ], "event.dataset": "nginx.error", + "event.kind": "event", "event.module": "nginx", "event.timezone": "-02:00", + "event.type": [ + "error" + ], "fileset.name": "error", "input.type": "log", "log.flags": [ @@ -49,9 +70,16 @@ }, { "@timestamp": "2019-11-05T14:50:44.000-02:00", + "event.category": [ + "web" + ], "event.dataset": "nginx.error", + "event.kind": "event", "event.module": "nginx", "event.timezone": "-02:00", + "event.type": [ + "error" + ], "fileset.name": "error", "input.type": "log", "log.level": "error", diff --git a/filebeat/module/nginx/fields.go b/filebeat/module/nginx/fields.go index df2afc9669e..2f9e50ceb60 100644 --- a/filebeat/module/nginx/fields.go +++ b/filebeat/module/nginx/fields.go @@ -32,5 +32,5 @@ func init() { // AssetNginx returns asset data. // This is the base64 encoded gzipped contents of module/nginx. func AssetNginx() string { - return "eJzsWM2O4zYMvs9TENtLC8z4AXIoUGyxwBxaFEUPvWUVi3aIlUWXlDObty9kOz/jkRM7md32YJ9m7PD7PooUJfIJvuB+Bb4k//UBIFBwuIIPv8f/PzwAWNRcqA7EfgU/PwAA/Ma2cQgFC9RGlHwJYYvQmoDjEgpyqNkDgG5ZwjpnX1C5giANPgAUhM7qqoV6Am8qPNHHJ+xrXEEp3NT9m4SG+HxqgaAQrsYExOec75zT5DmqHl+niC+Qx+cj+2DIa0/RrshJSIcf9RylpOScSxKsOOCa6rUjDa9+cpBnRMx+8OWCxPj84jsr4KJngOc/wFgrqIqawXMAUjAQSWGDuWkUgdqXOVcVewgM5HPXWHyEDSpZ1NbT3BH6oVA4g398RdXFaovGoig4+oLw+e+nTywvRiza+Nfn7A3an2gcKDeSt8JJQVADC9qo63P3JaP6zDS5uhu2+7WiD9lmH1DTy+vIDL/UJmxXsA2hzgS1Zq+YRawkTEWlmC4Sfb6/FdIoyjr+OVNCtMsSdlM4Kwxbtrf5/E+DGrIkwiR3xc11VFzGQiV5MzSdQhhlr3coSuxv8ThtOoX5kB/rnO3c6L5OMA0mNJrCmaajQBGUe+I9gjGF3pRvq8KU5F63hnNDP77HxnUMqzyMVOZzSIs7yofRuOxa0r0OJ7WRL3k5VDNiPlPLnSI4RTJTAmtWNM6lCuI8KWMI8/XcKyWdv7doGUeatRtKZBqm++0bIWcfyKMP96x4f2yXyNlVvKkLn3Pjg+zXpJyqnDdJu4o4VZzjvP3Z/aIuIE0VI1gS+3eK32WwycGjsH+vhLoANXOF3i+VrgOOSTtIQhGWb9ewtPCz+pWcvcc8tG6l75aOfTmvW/l4xASy6AMVhHLlZu9wh3PvmI7LLGU35X5Tj3g7zlYLx24we2s5hS/czBe2gsZmN7FWqGrKudfYtNW11CZfxhZxHY8CYefwm+T5c8fS5/uJa+nV/y+9+g+HTHg6i47WmFNBeR+P0Z7v2Lo49GXYTi1IBUtlwgpS7fyVcP21RehJoSOFH7vFJ1+evpDHx34pH8F4e/yyYbv/6bo/gUZmBZabjRt+OvhjG0ldE665RBUCOlMrWlDyObbZU5BEvXGJ4AUlum3safaWSK5jJ1ZrEDRV6lbdeRHw6zAvJ6x7hIvbJNIfOS4rMC6geBNoh+Pzl/vVnNFMVXbs+79r6vYp28s+aADexOqJZ+E96ARF2Q1GAuO+fNe03SJEvlgsfAD2IJgj7Q7T6aNzYz6BUVDM2VuFFwpbqMg56t5Ea3bNG1Xjro+Pb269F0UHO1SIqO8UtFdlZuSWcduGEOMtV24PJXoUE9DC868n1S3j5cUc6VjfvJ4g5nSwDStGvzgZPBeguEOJ51f7qq9z8RwyeZRvGzmv6v0Vi3z5GBFJzs5OIwiKten83uzbs9nolSJQs6QvErdlS4Qbc/fyfXqZlC+T8mVSvkzKl0n5Mim/rGeZlC+T8mVSvkzK/+NJ+b8BAAD//1+3mU8=" + return "eJzsWM9u48YPvucpiP1dfgUSPYAPBYotFsihRVH00Jt3rKFkIqOhSo6c+u2LkeQ/kUe25GS3PUinRDK/7+OQwxnyCV5wvwJfkv/7ASBQcLiCT7/G/z89AFjUXKgOxH4FPz4AAPzCtnEIBQvURpR8CWGL0JqA4xIKcqjZA4BuWcI6Z19QuYIgDT4AFITO6qqFegJvKjzRxyfsa1xBKdzU/ZuEhvh8aYGgEK7GBMTnnO+c0+Q5qh5fp4ivkMfnM/tgyGtP0a7ISUiHH/UcpaTknEsSrDjgmuq1Iw1vfnKQZ0TMfvDlisT4/OQ7K+CiZ4Dn38BYK6iKmsFzAFIwEElhg7lpFIHalzlXFXsIDORz11h8hA0qWdTW09wR+qFQOIN/fEPVxWqLxqIoOHpB+Prn0xeWVyMWbfzra3aB9jsaB8qN5K1wUhDUwII26vrafcmoPjNNru6G7X6t6EO22QfU9PI6MsMvtQnbFWxDqDNBrdkrZhErCVNRKaaLRJ/vl0IaRVnHP2dKiHZZwm4KZ4Vhy/Y+n/9qUEOWRJjkrri5jorLWKgkb4amUwij7PUORYn9PR6nTacwH/JjnbOdG923CabBhEZTONN0FCiC8p54j2BMoTflZVWYktzr1nBu6Mf32LiOYZWHkcp8DmlxR/kwGtddS7rX4aQ28jUvh2pGzGdqeacITpHMlMCaFY1zqYI4T8oYwnw975WSzt97tIwjzdoNJTIN0/3+jZCzD+TRh/eseH9sl8jZTbypC59z44Ps16Scqpx3SbuJOFWc47z92ftFXUGaKkawJPYfFL/rYJODR2H/UQl1BWrmCn1cKt0GHJN2kIQiLN+uYWnhZ/UrOXuPeWjdSt8tHftyXrfy+YgJZNEHKgjlxs3e4Q7n3jEdl1nKbsr9ph7xdpytFo7dYHZpOYUv3M0XtoLGZnexVqhqyrnX2LTVrdQmX8YWcR2PAmHn8Jvk+XPH0uf7iWvp1f8rvfr/DpnwdBYdrTGngvI+HqM937F1cejLsJ1akAqWyoQVpNr5G+H6Y4vQk0JHCv/vFp98efpCHh/7pXwE4+3xy4bt/ofb/gQamRVYbjZu+Ongj20kdU245RJVCOhMrWhByefYZk9BEvXGJYJXlOi2safZWyK5jp1YrUHQVKlbdefFC+5fWYYlasLSR8S4U6KCI811EcYFFG8C7XB8BPMhgs6Ypoo7dv/fNYH7xO1lHzQAb2INxbMgH3SCouwGg4FxX75r8m4RIl8sGT4AexDMkXaHGfXRuTGfwCgo5uytwiuFLVTkHHVvojW75kLVuOvjQ5x7b0fRwQ4VIuoHBe1NsRm5a9y9J8R4y5XbQ4kexQS08PzzSXhLen09R1rXi9cTxJxOuGHd6Ncng+cCFHco8SBrX/UFLx5IJo/ybSPn5b2/a5EvHyMiydkhagRBsTad35t9e0gbvVEHapb0jeK+hIlwY+5ev1gvI/NlZL6MzJeR+TIyX0bm1/UsI/NlZL6MzJeR+b88Mv8nAAD///5cnQ8=" } diff --git a/filebeat/module/nginx/ingress_controller/_meta/fields.yml b/filebeat/module/nginx/ingress_controller/_meta/fields.yml index 0c9ca13de32..2c467e3856a 100644 --- a/filebeat/module/nginx/ingress_controller/_meta/fields.yml +++ b/filebeat/module/nginx/ingress_controller/_meta/fields.yml @@ -22,11 +22,11 @@ description: > Time elapsed since the first bytes were read from the client - name: upstream.name - type: text + type: keyword description: > The name of the upstream. - name: upstream.alternative_name - type: text + type: keyword description: > The name of the alternative upstream. - name: upstream.response.length @@ -44,7 +44,7 @@ description: > The status code of the response obtained from the upstream server - name: http.request.id - type: text + type: keyword description: > The randomly generated ID of the request - name: upstream.ip diff --git a/filebeat/module/nginx/ingress_controller/ingest/pipeline.json b/filebeat/module/nginx/ingress_controller/ingest/pipeline.json deleted file mode 100644 index e660f22f022..00000000000 --- a/filebeat/module/nginx/ingress_controller/ingest/pipeline.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "description": "Pipeline for parsing Nginx ingress controller access logs. Requires the geoip and user_agent plugins.", - "processors": [ - { - "grok": { - "field": "message", - "patterns": [ - "(%{NGINX_HOST} )?\"?(?:%{NGINX_ADDRESS_LIST:nginx.ingress_controller.remote_ip_list}|%{NOTSPACE:source.address}) - %{DATA:user.name} \\[%{HTTPDATE:nginx.ingress_controller.time}\\] \"%{DATA:nginx.ingress_controller.info}\" %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} \"%{DATA:http.request.referrer}\" \"%{DATA:user_agent.original}\" %{NUMBER:nginx.ingress_controller.http.request.length:long} %{NUMBER:nginx.ingress_controller.http.request.time:double} \\[%{DATA:nginx.ingress_controller.upstream.name}\\] \\[%{DATA:nginx.ingress_controller.upstream.alternative_name}\\] (%{UPSTREAM_ADDRESS}|-) (%{NUMBER:nginx.ingress_controller.upstream.response.length:long}|-) (%{NUMBER:nginx.ingress_controller.upstream.response.time:double}|-) (%{NUMBER:nginx.ingress_controller.upstream.response.status_code:long}|-) %{GREEDYDATA:nginx.ingress_controller.http.request.id}" - ], - "pattern_definitions": { - "NGINX_HOST": "(?:%{IP:destination.ip}|%{NGINX_NOTSEPARATOR:destination.domain})(:%{NUMBER:destination.port})?", - "NGINX_NOTSEPARATOR": "[^\t ,:]+", - "NGINX_ADDRESS_LIST": "(?:%{IP}|%{WORD})(\"?,?\\s*(?:%{IP}|%{WORD}))*", - "UPSTREAM_ADDRESS": "%{IP:nginx.ingress_controller.upstream.ip}(:%{NUMBER:nginx.ingress_controller.upstream.port})?" - }, - "ignore_missing": true - } - }, - { - "grok": { - "field": "nginx.ingress_controller.info", - "patterns": [ - "%{WORD:http.request.method} %{DATA:url.original} HTTP/%{NUMBER:http.version}", - "" - ], - "ignore_missing": true - } - }, - { - "remove": { - "field": "nginx.ingress_controller.info" - } - }, - { - "split": { - "field": "nginx.ingress_controller.remote_ip_list", - "separator": "\"?,?\\s+", - "ignore_missing": true - } - }, - { - "split": { - "field": "nginx.ingress_controller.origin", - "separator": "\"?,?\\s+", - "ignore_missing": true - } - }, - { - "set": { - "field": "source.address", - "if": "ctx.source?.address == null", - "value": "" - } - }, - { - "script": { - "if": "ctx.nginx?.access?.remote_ip_list != null && ctx.nginx.ingress_controller.remote_ip_list.length > 0", - "lang": "painless", - "source": "boolean isPrivate(def dot, def ip) { try { StringTokenizer tok = new StringTokenizer(ip, dot); int firstByte = Integer.parseInt(tok.nextToken()); int secondByte = Integer.parseInt(tok.nextToken()); if (firstByte == 10) { return true; } if (firstByte == 192 && secondByte == 168) { return true; } if (firstByte == 172 && secondByte >= 16 && secondByte <= 31) { return true; } if (firstByte == 127) { return true; } return false; } catch (Exception e) { return false; } } try { ctx.source.address = null; if (ctx.nginx.ingress_controller.remote_ip_list == null) { return; } def found = false; for (def item : ctx.nginx.ingress_controller.remote_ip_list) { if (!isPrivate(params.dot, item)) { ctx.source.address = item; found = true; break; } } if (!found) { ctx.source.address = ctx.nginx.ingress_controller.remote_ip_list[0]; }} catch (Exception e) { ctx.source.address = null; }", - "params": { - "dot": "." - } - } - }, - { - "remove": { - "field": "source.address", - "if": "ctx.source.address == null" - } - }, - { - "grok": { - "field": "source.address", - "patterns": ["^%{IP:source.ip}$"], - "ignore_failure": true - } - }, - { - "remove": { - "field": "message" - } - }, - { - "rename": { - "field": "@timestamp", - "target_field": "event.created" - } - }, - { - "date": { - "field": "nginx.ingress_controller.time", - "target_field": "@timestamp", - "formats": [ - "dd/MMM/yyyy:H:m:s Z" - ], - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, - { - "remove": { - "field": "nginx.ingress_controller.time" - } - }, - { - "user_agent": { - "field": "user_agent.original" - } - }, - { - "geoip": { - "field": "source.ip", - "target_field": "source.geo", - "ignore_missing": true - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "source.ip", - "target_field": "source.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.asn", - "target_field": "source.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.organization_name", - "target_field": "source.as.organization.name", - "ignore_missing": true - } - } - ], - "on_failure": [ - { - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - } - ] -} diff --git a/filebeat/module/nginx/ingress_controller/ingest/pipeline.yml b/filebeat/module/nginx/ingress_controller/ingest/pipeline.yml new file mode 100644 index 00000000000..9721be136e3 --- /dev/null +++ b/filebeat/module/nginx/ingress_controller/ingest/pipeline.yml @@ -0,0 +1,172 @@ +description: Pipeline for parsing Nginx ingress controller access logs. Requires the + geoip and user_agent plugins. +processors: +- grok: + field: message + patterns: + - (%{NGINX_HOST} )?"?(?:%{NGINX_ADDRESS_LIST:nginx.ingress_controller.remote_ip_list}|%{NOTSPACE:source.address}) + - (-|%{DATA:user.name}) \[%{HTTPDATE:nginx.ingress_controller.time}\] "%{DATA:nginx.ingress_controller.info}" + %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} + "(-|%{DATA:http.request.referrer})" "(-|%{DATA:user_agent.original})" %{NUMBER:nginx.ingress_controller.http.request.length:long} + %{NUMBER:nginx.ingress_controller.http.request.time:double} \[%{DATA:nginx.ingress_controller.upstream.name}\] + \[%{DATA:nginx.ingress_controller.upstream.alternative_name}\] (%{UPSTREAM_ADDRESS}|-) + (%{NUMBER:nginx.ingress_controller.upstream.response.length:long}|-) (%{NUMBER:nginx.ingress_controller.upstream.response.time:double}|-) + (%{NUMBER:nginx.ingress_controller.upstream.response.status_code:long}|-) %{GREEDYDATA:nginx.ingress_controller.http.request.id} + pattern_definitions: + NGINX_HOST: (?:%{IP:destination.ip}|%{NGINX_NOTSEPARATOR:destination.domain})(:%{NUMBER:destination.port})? + NGINX_NOTSEPARATOR: "[^\t ,:]+" + NGINX_ADDRESS_LIST: (?:%{IP}|%{WORD})("?,?\s*(?:%{IP}|%{WORD}))* + UPSTREAM_ADDRESS: '%{IP:nginx.ingress_controller.upstream.ip}(:%{NUMBER:nginx.ingress_controller.upstream.port})?' + ignore_missing: true +- grok: + field: nginx.ingress_controller.info + patterns: + - '%{WORD:http.request.method} %{DATA:url.original} HTTP/%{NUMBER:http.version}' + - "" + ignore_missing: true +- remove: + field: nginx.ingress_controller.info +- split: + field: nginx.ingress_controller.remote_ip_list + separator: '"?,?\s+' + ignore_missing: true +- split: + field: nginx.ingress_controller.origin + separator: '"?,?\s+' + ignore_missing: true +- set: + field: source.address + if: ctx.source?.address == null + value: "" +- script: + if: ctx.nginx?.access?.remote_ip_list != null && ctx.nginx.ingress_controller.remote_ip_list.length > 0 + lang: painless + source: >- + boolean isPrivate(def dot, def ip) { + try { + StringTokenizer tok = new StringTokenizer(ip, dot); + int firstByte = Integer.parseInt(tok.nextToken()); + int secondByte = Integer.parseInt(tok.nextToken()); + if (firstByte == 10) { + return true; + } + if (firstByte == 192 && secondByte == 168) { + return true; + } + if (firstByte == 172 && secondByte >= 16 && secondByte <= 31) { + return true; + } + if (firstByte == 127) { + return true; + } + return false; + } + catch (Exception e) { + return false; + } + } + try { + ctx.source.address = null; + if (ctx.nginx.ingress_controller.remote_ip_list == null) { + return; + } + def found = false; + for (def item : ctx.nginx.ingress_controller.remote_ip_list) { + if (!isPrivate(params.dot, item)) { + ctx.source.address = item; + found = true; + break; + } + } + if (!found) { + ctx.source.address = ctx.nginx.ingress_controller.remote_ip_list[0]; + } + } + catch (Exception e) { + ctx.source.address = null; + } + params: + dot: . +- remove: + field: source.address + if: ctx.source.address == null +- grok: + field: source.address + patterns: + - ^%{IP:source.ip}$ + ignore_failure: true +- remove: + field: message +- rename: + field: '@timestamp' + target_field: event.created +- date: + field: nginx.ingress_controller.time + target_field: '@timestamp' + formats: + - dd/MMM/yyyy:H:m:s Z + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- remove: + field: nginx.ingress_controller.time +- user_agent: + field: user_agent.original + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- set: + field: event.kind + value: event +- append: + field: event.category + value: web +- append: + field: event.type + value: info +- set: + field: event.outcome + value: success + if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code < 400" +- set: + field: event.outcome + value: failure + if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code >= 400" +- lowercase: + field: http.request.method + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/nginx/ingress_controller/manifest.yml b/filebeat/module/nginx/ingress_controller/manifest.yml index 0f51e4d5c04..326beb11461 100644 --- a/filebeat/module/nginx/ingress_controller/manifest.yml +++ b/filebeat/module/nginx/ingress_controller/manifest.yml @@ -9,7 +9,7 @@ var: os.windows: - c:/programdata/nginx/logs/*access.log* -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/ingress_controller.yml requires.processors: diff --git a/filebeat/module/nginx/ingress_controller/test/test.log-expected.json b/filebeat/module/nginx/ingress_controller/test/test.log-expected.json index 2dc9d1afbce..a2bf0f6c6e0 100644 --- a/filebeat/module/nginx/ingress_controller/test/test.log-expected.json +++ b/filebeat/module/nginx/ingress_controller/test/test.log-expected.json @@ -1,12 +1,19 @@ [ { "@timestamp": "2020-02-07T11:48:51.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "POST", - "http.request.referrer": "-", + "http.request.method": "post", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -28,7 +35,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "curl", "user_agent.original": "curl/7.54.0", @@ -36,12 +42,19 @@ }, { "@timestamp": "2020-02-07T11:49:15.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -63,7 +76,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "curl", "user_agent.original": "curl/7.54.0", @@ -71,12 +83,19 @@ }, { "@timestamp": "2020-02-07T11:49:30.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "DELETE", - "http.request.referrer": "-", + "http.request.method": "delete", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -98,7 +117,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "curl", "user_agent.original": "curl/7.54.0", @@ -106,12 +124,19 @@ }, { "@timestamp": "2020-02-07T11:49:43.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "PATCH", - "http.request.referrer": "-", + "http.request.method": "patch", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -133,7 +158,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "curl", "user_agent.original": "curl/7.54.0", @@ -141,12 +165,19 @@ }, { "@timestamp": "2020-02-07T11:49:50.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "PATCHp", - "http.request.referrer": "-", + "http.request.method": "patchp", "http.response.body.bytes": 163, "http.response.status_code": 400, "http.version": "1.1", @@ -162,20 +193,23 @@ "nginx.ingress_controller.upstream.name": "", "service.type": "nginx", "source.address": "", - "url.original": "/products/42", - "user.name": "-", - "user_agent.device.name": "Other", - "user_agent.name": "Other", - "user_agent.original": "-" + "url.original": "/products/42" }, { "@timestamp": "2020-02-07T11:50:09.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "failure", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", "http.request.method": "geti", - "http.request.referrer": "-", "http.response.body.bytes": 163, "http.response.status_code": 400, "http.version": "1.1", @@ -191,20 +225,23 @@ "nginx.ingress_controller.upstream.name": "", "service.type": "nginx", "source.address": "", - "url.original": "/products/42", - "user.name": "-", - "user_agent.device.name": "Other", - "user_agent.name": "Other", - "user_agent.original": "-" + "url.original": "/products/42" }, { "@timestamp": "2020-02-07T11:55:05.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -226,7 +263,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Wget", "user_agent.original": "Wget/1.20.3 (darwin18.6.0)", @@ -234,12 +270,19 @@ }, { "@timestamp": "2020-02-07T11:55:57.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -261,7 +304,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", @@ -272,11 +314,19 @@ }, { "@timestamp": "2020-02-07T11:55:57.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", + "http.request.method": "get", "http.request.referrer": "http://hello-world.info/products/42", "http.response.body.bytes": 59, "http.response.status_code": 200, @@ -299,7 +349,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", @@ -310,12 +359,19 @@ }, { "@timestamp": "2020-02-07T11:56:24.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 61, "http.response.status_code": 200, "http.version": "1.1", @@ -337,7 +393,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/v2", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", @@ -348,11 +403,19 @@ }, { "@timestamp": "2020-02-07T11:56:24.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", + "http.request.method": "get", "http.request.referrer": "http://hello-world.info/v2", "http.response.body.bytes": 59, "http.response.status_code": 200, @@ -375,7 +438,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Chrome", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36", @@ -386,12 +448,19 @@ }, { "@timestamp": "2020-02-07T11:56:36.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -413,7 +482,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Safari", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", @@ -424,11 +492,19 @@ }, { "@timestamp": "2020-02-07T11:56:36.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", + "http.request.method": "get", "http.request.referrer": "http://hello-world.info/products/42", "http.response.body.bytes": 59, "http.response.status_code": 200, @@ -451,7 +527,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Safari", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", @@ -462,12 +537,19 @@ }, { "@timestamp": "2020-02-07T11:56:54.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -489,7 +571,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Safari", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", @@ -500,12 +581,19 @@ }, { "@timestamp": "2020-02-07T11:56:54.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -527,7 +615,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Safari", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", @@ -538,11 +625,19 @@ }, { "@timestamp": "2020-02-07T11:56:54.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", + "http.request.method": "get", "http.request.referrer": "http://hello-world.info/", "http.response.body.bytes": 59, "http.response.status_code": 200, @@ -565,7 +660,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Safari", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", @@ -576,12 +670,19 @@ }, { "@timestamp": "2020-02-07T11:56:56.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 61, "http.response.status_code": 200, "http.version": "1.1", @@ -603,7 +704,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/v2", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Safari", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", @@ -614,11 +714,19 @@ }, { "@timestamp": "2020-02-07T11:56:56.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", + "http.request.method": "get", "http.request.referrer": "http://hello-world.info/v2", "http.response.body.bytes": 59, "http.response.status_code": 200, @@ -641,7 +749,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Safari", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15", @@ -652,12 +759,19 @@ }, { "@timestamp": "2020-02-07T12:00:28.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -679,7 +793,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/products/42?address=delhi+technological+university", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Python Requests", "user_agent.original": "python-requests/2.22.0", @@ -687,12 +800,19 @@ }, { "@timestamp": "2020-02-07T12:02:38.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 61, "http.response.status_code": 200, "http.version": "1.1", @@ -714,7 +834,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/v2", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0", @@ -725,12 +844,19 @@ }, { "@timestamp": "2020-02-07T12:02:38.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 59, "http.response.status_code": 200, "http.version": "1.1", @@ -752,7 +878,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/favicon.ico", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0", @@ -763,12 +888,19 @@ }, { "@timestamp": "2020-02-07T12:02:42.000Z", + "event.category": [ + "web" + ], "event.dataset": "nginx.ingress_controller", + "event.kind": "event", "event.module": "nginx", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "info" + ], "fileset.name": "ingress_controller", - "http.request.method": "GET", - "http.request.referrer": "-", + "http.request.method": "get", "http.response.body.bytes": 61, "http.response.status_code": 200, "http.version": "1.1", @@ -790,7 +922,6 @@ "service.type": "nginx", "source.address": "", "url.original": "/v2/some", - "user.name": "-", "user_agent.device.name": "Other", "user_agent.name": "Firefox", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0", diff --git a/filebeat/module/postgresql/log/ingest/pipeline.json b/filebeat/module/postgresql/log/ingest/pipeline.json deleted file mode 100644 index 1bed827739d..00000000000 --- a/filebeat/module/postgresql/log/ingest/pipeline.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "description": "Pipeline for parsing PostgreSQL logs.", - "processors": [ - { - "grok": { - "field": "message", - "ignore_missing": true, - "patterns": [ - "^%{DATETIME:postgresql.log.timestamp} \\[%{NUMBER:process.pid:long}(-%{BASE16FLOAT:postgresql.log.core_id:long})?\\] ((\\[%{USERNAME:user.name}\\]@\\[%{POSTGRESQL_DB_NAME:postgresql.log.database}\\]|%{USERNAME:user.name}@%{POSTGRESQL_DB_NAME:postgresql.log.database}) )?%{WORD:log.level}: (?:%{NUMBER:postgresql.log.error.code:long}|%{SPACE})(duration: %{NUMBER:temp.duration:float} ms %{POSTGRESQL_QUERY_STEP}: %{GREEDYDATA:postgresql.log.query}|: %{GREEDYDATA:message}|%{GREEDYDATA:message})" - ], - "pattern_definitions": { - "DATETIME": "[-0-9]+ %{TIME} %{WORD:event.timezone}", - "GREEDYDATA": "(.|\n|\t)*", - "POSTGRESQL_DB_NAME": "[a-zA-Z0-9_]+[a-zA-Z0-9_\\$]*", - "POSTGRESQL_QUERY_STEP": "%{WORD:postgresql.log.query_step}(?: | %{WORD:postgresql.log.query_name})?" - } - } - }, - { - "date": { - "field": "postgresql.log.timestamp", - "target_field": "@timestamp", - "formats": [ - "yyyy-MM-dd HH:mm:ss.SSS zz", "yyyy-MM-dd HH:mm:ss zz" - ] - } - }, { - "script": { - "lang": "painless", - "source": "ctx.event.duration = Math.round(ctx.temp.duration * params.scale)", - "params": { "scale": 1000000 }, - "if": "ctx.temp?.duration != null" - } - }, { - "remove": { - "field": "temp.duration", - "ignore_missing": true - } - } - ], - "on_failure": [ - { - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - } - ] -} diff --git a/filebeat/module/postgresql/log/ingest/pipeline.yml b/filebeat/module/postgresql/log/ingest/pipeline.yml new file mode 100644 index 00000000000..bd7fbd69e7d --- /dev/null +++ b/filebeat/module/postgresql/log/ingest/pipeline.yml @@ -0,0 +1,57 @@ +description: Pipeline for parsing PostgreSQL logs. +processors: +- grok: + field: message + ignore_missing: true + patterns: + - '^%{DATETIME:postgresql.log.timestamp} \[%{NUMBER:process.pid:long}(-%{BASE16FLOAT:postgresql.log.core_id:long})?\] + ((\[%{USERNAME:user.name}\]@\[%{POSTGRESQL_DB_NAME:postgresql.log.database}\]|%{USERNAME:user.name}@%{POSTGRESQL_DB_NAME:postgresql.log.database}) + )?%{WORD:log.level}: (?:%{NUMBER:postgresql.log.error.code:long}|%{SPACE})(duration: + %{NUMBER:temp.duration:float} ms %{POSTGRESQL_QUERY_STEP}: %{GREEDYDATA:postgresql.log.query}|: + %{GREEDYDATA:message}|%{GREEDYDATA:message})' + pattern_definitions: + DATETIME: '[-0-9]+ %{TIME} %{WORD:event.timezone}' + GREEDYDATA: |- + (.| + | )* + POSTGRESQL_DB_NAME: '[a-zA-Z0-9_]+[a-zA-Z0-9_\$]*' + POSTGRESQL_QUERY_STEP: '%{WORD:postgresql.log.query_step}(?: | %{WORD:postgresql.log.query_name})?' +- date: + field: postgresql.log.timestamp + target_field: '@timestamp' + formats: + - yyyy-MM-dd HH:mm:ss.SSS zz + - yyyy-MM-dd HH:mm:ss zz +- script: + lang: painless + source: ctx.event.duration = Math.round(ctx.temp.duration * params.scale) + params: + scale: 1000000 + if: ctx.temp?.duration != null +- remove: + field: temp.duration + ignore_missing: true +- set: + field: event.kind + value: event +- append: + field: event.category + value: + - database +- append: + field: event.type + value: + - info +- append: + field: event.type + value: + - error + if: "ctx?.postgresql?.log?.error?.code != null && ctx.postgresql.log.error.code >= 02000" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/postgresql/log/manifest.yml b/filebeat/module/postgresql/log/manifest.yml index e5ab4a9a69c..ade6e2899de 100644 --- a/filebeat/module/postgresql/log/manifest.yml +++ b/filebeat/module/postgresql/log/manifest.yml @@ -9,5 +9,5 @@ var: os.windows: - "c:/Program Files/PostgreSQL/*/logs/*.log*" -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/log.yml diff --git a/filebeat/module/postgresql/log/test/postgresql-11.4.log-expected.json b/filebeat/module/postgresql/log/test/postgresql-11.4.log-expected.json index 2c347c87c6a..2d95ce2fd0e 100644 --- a/filebeat/module/postgresql/log/test/postgresql-11.4.log-expected.json +++ b/filebeat/module/postgresql/log/test/postgresql-11.4.log-expected.json @@ -1,9 +1,16 @@ [ { "@timestamp": "2019-07-23T12:06:24.406Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -16,9 +23,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.406Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -30,9 +44,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.478Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -45,9 +66,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.478Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -59,9 +87,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.485Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -74,9 +109,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.485Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -88,9 +130,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.485Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -103,9 +152,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.485Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -117,9 +173,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.485Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -132,9 +195,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.485Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -146,9 +216,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.507Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -161,9 +238,16 @@ }, { "@timestamp": "2019-07-23T12:06:24.507Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -175,9 +259,16 @@ }, { "@timestamp": "2019-07-23T12:06:30.536Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -190,9 +281,16 @@ }, { "@timestamp": "2019-07-23T12:06:30.536Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -204,9 +302,16 @@ }, { "@timestamp": "2019-07-23T12:06:30.537Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -219,9 +324,16 @@ }, { "@timestamp": "2019-07-23T12:06:30.537Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -233,9 +345,16 @@ }, { "@timestamp": "2019-07-23T12:06:33.732Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -248,9 +367,16 @@ }, { "@timestamp": "2019-07-23T12:06:33.732Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -262,9 +388,17 @@ }, { "@timestamp": "2019-07-23T12:06:33.732Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info", + "error" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -277,9 +411,16 @@ }, { "@timestamp": "2019-07-23T12:06:33.732Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -291,9 +432,16 @@ }, { "@timestamp": "2019-07-23T12:06:33.732Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -305,9 +453,16 @@ }, { "@timestamp": "2019-07-23T12:06:34.877Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -320,9 +475,16 @@ }, { "@timestamp": "2019-07-23T12:06:34.877Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -334,9 +496,16 @@ }, { "@timestamp": "2019-07-23T12:06:34.878Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -349,9 +518,16 @@ }, { "@timestamp": "2019-07-23T12:06:34.878Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -363,9 +539,16 @@ }, { "@timestamp": "2019-07-23T12:09:57.563Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -378,9 +561,16 @@ }, { "@timestamp": "2019-07-23T12:09:57.563Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", @@ -392,9 +582,16 @@ }, { "@timestamp": "2019-07-23T12:09:57.565Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -407,9 +604,16 @@ }, { "@timestamp": "2019-07-23T12:09:57.565Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOCATION", diff --git a/filebeat/module/postgresql/log/test/postgresql-9.6-debian-with-slowlog.log-expected.json b/filebeat/module/postgresql/log/test/postgresql-9.6-debian-with-slowlog.log-expected.json index 201c50cb0b7..280547f6b29 100644 --- a/filebeat/module/postgresql/log/test/postgresql-9.6-debian-with-slowlog.log-expected.json +++ b/filebeat/module/postgresql/log/test/postgresql-9.6-debian-with-slowlog.log-expected.json @@ -1,9 +1,16 @@ [ { "@timestamp": "2017-07-31T11:36:42.585Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -15,9 +22,16 @@ }, { "@timestamp": "2017-07-31T11:36:42.605Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -29,9 +43,16 @@ }, { "@timestamp": "2017-07-31T11:36:42.615Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -43,9 +64,16 @@ }, { "@timestamp": "2017-07-31T11:36:42.616Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -57,9 +85,16 @@ }, { "@timestamp": "2017-07-31T11:36:42.956Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -68,15 +103,25 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-07-31 13:36:42.956 CEST", "process.pid": 4980, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-07-31T11:36:43.557Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 37118000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -90,15 +135,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:36:43.557 CEST", "process.pid": 4983, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:36:44.104Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 2895000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -112,15 +167,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:36:44.104 CEST", "process.pid": 4986, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:36:44.642Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 2809000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -134,14 +199,24 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:36:44.642 CEST", "process.pid": 4989, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:39:16.249Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -150,14 +225,24 @@ "postgresql.log.database": "users", "postgresql.log.timestamp": "2017-07-31 13:39:16.249 CEST", "process.pid": 5407, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:39:17.945Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -166,15 +251,25 @@ "postgresql.log.database": "user", "postgresql.log.timestamp": "2017-07-31 13:39:17.945 CEST", "process.pid": 5500, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:39:21.025Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 37598000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -188,15 +283,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:39:21.025 CEST", "process.pid": 5404, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:39:31.619Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 9482000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -207,15 +312,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:39:31.619 CEST", "process.pid": 5502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:39:40.147Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 765000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -226,15 +341,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:39:40.147 CEST", "process.pid": 5502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:40:54.310Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", - "event.duration": 26082001, + "event.duration": 26082000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -248,15 +373,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:40:54.310 CEST", "process.pid": 5502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:43:22.645Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", - "event.duration": 36161999, + "event.duration": 36162000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -267,15 +402,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:43:22.645 CEST", "process.pid": 5502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:46:02.670Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 10540000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -286,15 +431,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:46:02.670 CEST", "process.pid": 5502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:46:23.016Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 5156000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -305,15 +460,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:46:23.016 CEST", "process.pid": 5502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T11:46:55.637Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 25871000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -324,15 +489,25 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-07-31 13:46:55.637 CEST", "process.pid": 5502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2019-05-06T19:00:04.511Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 753000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -346,6 +521,9 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2019-05-06 19:00:04.511 UTC", "process.pid": 913763, + "related.user": [ + "elastic" + ], "service.type": "postgresql", "user.name": "elastic" } diff --git a/filebeat/module/postgresql/log/test/postgresql-9.6-multi-core.log-expected.json b/filebeat/module/postgresql/log/test/postgresql-9.6-multi-core.log-expected.json index dbd1e12dd49..76f1bd2f065 100644 --- a/filebeat/module/postgresql/log/test/postgresql-9.6-multi-core.log-expected.json +++ b/filebeat/module/postgresql/log/test/postgresql-9.6-multi-core.log-expected.json @@ -1,9 +1,16 @@ [ { "@timestamp": "2017-04-03T20:32:14.322Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -13,14 +20,24 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-04-03 22:32:14.322 CEST", "process.pid": 12975, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-04-03T20:32:14.322Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -30,15 +47,25 @@ "postgresql.log.database": "user", "postgresql.log.timestamp": "2017-04-03 22:32:14.322 CEST", "process.pid": 5404, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-03T20:35:22.389Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 37598000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -53,14 +80,24 @@ "postgresql.log.query_step": "statement", "postgresql.log.timestamp": "2017-04-03 22:35:22.389 CEST", "process.pid": 5404, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T17:36:43.557Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -73,9 +110,16 @@ }, { "@timestamp": "2017-07-31T17:36:44.227Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -88,9 +132,16 @@ }, { "@timestamp": "2017-07-31T17:46:02.670Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "HINT", @@ -103,9 +154,16 @@ }, { "@timestamp": "2017-07-31T17:46:23.016Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -115,14 +173,24 @@ "postgresql.log.database": "postgres", "postgresql.log.timestamp": "2017-07-31 13:46:23.016 EST", "process.pid": 768, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T17:46:55.637Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -132,6 +200,9 @@ "postgresql.log.database": "postgres", "postgresql.log.timestamp": "2017-07-31 13:46:55.637 EST", "process.pid": 771, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" } diff --git a/filebeat/module/postgresql/log/test/postgresql-9.6-new-timestamp.log-expected.json b/filebeat/module/postgresql/log/test/postgresql-9.6-new-timestamp.log-expected.json index 9737568df83..9a1d8b1b5fa 100644 --- a/filebeat/module/postgresql/log/test/postgresql-9.6-new-timestamp.log-expected.json +++ b/filebeat/module/postgresql/log/test/postgresql-9.6-new-timestamp.log-expected.json @@ -1,9 +1,16 @@ [ { "@timestamp": "2017-07-31T17:36:43.000Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -16,9 +23,16 @@ }, { "@timestamp": "2017-07-31T17:36:44.000Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -31,9 +45,16 @@ }, { "@timestamp": "2017-07-31T17:46:02.000Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "HINT", @@ -46,9 +67,16 @@ }, { "@timestamp": "2017-07-31T17:46:23.000Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -58,14 +86,24 @@ "postgresql.log.database": "postgres", "postgresql.log.timestamp": "2017-07-31 13:46:23 EST", "process.pid": 768, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-07-31T17:46:55.000Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "EST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -75,6 +113,9 @@ "postgresql.log.database": "postgres", "postgresql.log.timestamp": "2017-07-31 13:46:55 EST", "process.pid": 771, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" } diff --git a/filebeat/module/postgresql/log/test/postgresql-query-steps-slowlog.log-expected.json b/filebeat/module/postgresql/log/test/postgresql-query-steps-slowlog.log-expected.json index 273499e8634..cec040589ab 100644 --- a/filebeat/module/postgresql/log/test/postgresql-query-steps-slowlog.log-expected.json +++ b/filebeat/module/postgresql/log/test/postgresql-query-steps-slowlog.log-expected.json @@ -1,10 +1,17 @@ [ { "@timestamp": "2019-09-04T13:52:38.004Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 12437000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -15,15 +22,25 @@ "postgresql.log.query_step": "parse", "postgresql.log.timestamp": "2019-09-04 15:52:38.004 CEST", "process.pid": 31136, + "related.user": [ + "user" + ], "service.type": "postgresql", "user.name": "user" }, { "@timestamp": "2019-09-04T13:52:38.004Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", "event.duration": 12437000, + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.flags": [ @@ -38,6 +55,9 @@ "postgresql.log.query_step": "execute", "postgresql.log.timestamp": "2019-09-04 15:52:38.004 CEST", "process.pid": 31136, + "related.user": [ + "user" + ], "service.type": "postgresql", "user.name": "user" } diff --git a/filebeat/module/postgresql/log/test/postgresql-ubuntu-9.5.log-expected.json b/filebeat/module/postgresql/log/test/postgresql-ubuntu-9.5.log-expected.json index 0d1b3df95b5..f1248d53e45 100644 --- a/filebeat/module/postgresql/log/test/postgresql-ubuntu-9.5.log-expected.json +++ b/filebeat/module/postgresql/log/test/postgresql-ubuntu-9.5.log-expected.json @@ -1,9 +1,16 @@ [ { "@timestamp": "2017-04-03T20:32:14.322Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -12,14 +19,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-03 22:32:14.322 CEST", "process.pid": 31225, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-03T20:32:14.322Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -28,14 +45,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-03 22:32:14.322 CEST", "process.pid": 31225, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-03T20:35:22.389Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -44,14 +71,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-03 22:35:22.389 CEST", "process.pid": 3474, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-03T20:36:56.464Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -60,14 +97,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-03 22:36:56.464 CEST", "process.pid": 3525, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-03T20:37:12.961Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -76,14 +123,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-03 22:37:12.961 CEST", "process.pid": 3570, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T19:05:28.549Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -92,14 +149,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 21:05:28.549 CEST", "process.pid": 21483, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T19:09:41.345Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -108,14 +175,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 21:09:41.345 CEST", "process.pid": 21597, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T20:45:30.218Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -124,14 +201,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 22:45:30.218 CEST", "process.pid": 22603, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T20:45:30.218Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "HINT", @@ -140,14 +227,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 22:45:30.218 CEST", "process.pid": 22603, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T20:45:30.218Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -156,14 +253,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 22:45:30.218 CEST", "process.pid": 22603, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T20:46:09.751Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -172,14 +279,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 22:46:09.751 CEST", "process.pid": 22608, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T20:46:09.751Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -188,14 +305,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 22:46:09.751 CEST", "process.pid": 22608, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:02:51.199Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -204,14 +331,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:02:51.199 CEST", "process.pid": 24341, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:02:51.199Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -220,14 +357,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:02:51.199 CEST", "process.pid": 24341, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:04:36.087Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -236,14 +383,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:04:36.087 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:04:36.087Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -252,14 +409,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:04:36.087 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:04:51.462Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -268,14 +435,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:04:51.462 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:04:51.462Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -284,14 +461,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:04:51.462 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:05:06.217Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -300,14 +487,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:05:06.217 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:05:06.217Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -316,14 +513,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:05:06.217 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:05:18.295Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -332,14 +539,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:05:18.295 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:05:18.295Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -348,14 +565,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:05:18.295 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:13:47.505Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -364,14 +591,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:13:47.505 CEST", "process.pid": 24489, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-07T21:13:47.505Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -380,14 +617,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-07 23:13:47.505 CEST", "process.pid": 24489, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-08T10:32:51.056Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "ERROR", @@ -396,14 +643,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-08 12:32:51.056 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-08T10:32:51.056Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "DETAIL", @@ -412,14 +669,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-08 12:32:51.056 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-08T10:32:51.056Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "STATEMENT", @@ -428,14 +695,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-08 12:32:51.056 CEST", "process.pid": 20730, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-08T19:54:37.443Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -444,14 +721,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-08 21:54:37.443 CEST", "process.pid": 30630, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-08T19:54:37.468Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -460,14 +747,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-04-08 21:54:37.468 CEST", "process.pid": 30502, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-04-08T19:54:37.618Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -479,9 +776,16 @@ }, { "@timestamp": "2017-04-08T19:54:37.618Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -493,9 +797,16 @@ }, { "@timestamp": "2017-04-08T19:54:37.618Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -507,9 +818,16 @@ }, { "@timestamp": "2017-04-08T19:54:37.622Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -521,9 +839,16 @@ }, { "@timestamp": "2017-04-08T19:54:37.644Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -535,9 +860,16 @@ }, { "@timestamp": "2017-04-08T19:56:02.932Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -549,9 +881,16 @@ }, { "@timestamp": "2017-04-08T19:56:02.944Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -563,9 +902,16 @@ }, { "@timestamp": "2017-04-08T19:56:02.946Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -577,9 +923,16 @@ }, { "@timestamp": "2017-04-08T19:56:02.947Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -591,9 +944,16 @@ }, { "@timestamp": "2017-04-08T19:56:03.362Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -602,14 +962,24 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-04-08 21:56:03.362 CEST", "process.pid": 891, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-05-27T14:07:53.007Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -621,9 +991,16 @@ }, { "@timestamp": "2017-05-27T14:07:53.010Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -635,9 +1012,16 @@ }, { "@timestamp": "2017-05-27T14:07:53.015Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -649,9 +1033,16 @@ }, { "@timestamp": "2017-05-27T14:07:53.016Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -663,9 +1054,16 @@ }, { "@timestamp": "2017-05-27T14:07:53.463Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -674,14 +1072,24 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-05-27 14:07:53.463 UTC", "process.pid": 32573, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-05-27T14:08:13.661Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "FATAL", @@ -690,14 +1098,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-05-27 14:08:13.661 UTC", "process.pid": 1308, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-05-27T14:59:26.553Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -706,14 +1124,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-05-27 14:59:26.553 UTC", "process.pid": 1994, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-05-27T14:59:26.555Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "UTC", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -722,14 +1150,24 @@ "postgresql.log.database": "mydb", "postgresql.log.timestamp": "2017-05-27 14:59:26.555 UTC", "process.pid": 1989, + "related.user": [ + "postgres" + ], "service.type": "postgresql", "user.name": "postgres" }, { "@timestamp": "2017-06-06T05:54:13.753Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -741,9 +1179,16 @@ }, { "@timestamp": "2017-06-06T05:54:13.753Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -755,9 +1200,16 @@ }, { "@timestamp": "2017-06-06T05:54:13.753Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -769,9 +1221,16 @@ }, { "@timestamp": "2017-06-06T05:54:13.755Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -783,9 +1242,16 @@ }, { "@timestamp": "2017-06-06T05:54:13.816Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -797,9 +1263,16 @@ }, { "@timestamp": "2017-06-06T05:55:39.725Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -811,9 +1284,16 @@ }, { "@timestamp": "2017-06-06T05:55:39.736Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -825,9 +1305,16 @@ }, { "@timestamp": "2017-06-06T05:55:39.739Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -839,9 +1326,16 @@ }, { "@timestamp": "2017-06-06T05:55:39.739Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -853,9 +1347,16 @@ }, { "@timestamp": "2017-06-06T05:55:40.155Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -864,14 +1365,24 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-06-06 07:55:40.155 CEST", "process.pid": 12975, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-06-06T05:55:40.156Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -880,14 +1391,24 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-06-06 07:55:40.156 CEST", "process.pid": 12975, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-06-10T17:37:30.681Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -899,9 +1420,16 @@ }, { "@timestamp": "2017-06-10T17:37:30.695Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -913,9 +1441,16 @@ }, { "@timestamp": "2017-06-10T17:37:30.702Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -927,9 +1462,16 @@ }, { "@timestamp": "2017-06-10T17:37:30.702Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -941,9 +1483,16 @@ }, { "@timestamp": "2017-06-10T17:37:31.104Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -952,14 +1501,24 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-06-10 19:37:31.104 CEST", "process.pid": 17404, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-06-10T18:27:55.911Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -971,9 +1530,16 @@ }, { "@timestamp": "2017-06-10T18:27:55.911Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -985,9 +1551,16 @@ }, { "@timestamp": "2017-06-10T18:27:55.911Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -999,9 +1572,16 @@ }, { "@timestamp": "2017-06-10T18:27:55.914Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1013,9 +1593,16 @@ }, { "@timestamp": "2017-06-10T18:27:55.973Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1027,9 +1614,16 @@ }, { "@timestamp": "2017-06-10T18:27:57.022Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1041,9 +1635,16 @@ }, { "@timestamp": "2017-06-10T18:27:57.032Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1055,9 +1656,16 @@ }, { "@timestamp": "2017-06-10T18:27:57.035Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1069,9 +1677,16 @@ }, { "@timestamp": "2017-06-10T18:27:57.035Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1083,9 +1698,16 @@ }, { "@timestamp": "2017-06-10T18:27:57.475Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1094,14 +1716,24 @@ "postgresql.log.database": "unknown", "postgresql.log.timestamp": "2017-06-10 20:27:57.475 CEST", "process.pid": 24496, + "related.user": [ + "unknown" + ], "service.type": "postgresql", "user.name": "unknown" }, { "@timestamp": "2017-06-17T14:58:03.937Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1113,9 +1745,16 @@ }, { "@timestamp": "2017-06-17T14:58:03.937Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1127,9 +1766,16 @@ }, { "@timestamp": "2017-06-17T14:58:03.938Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1141,9 +1787,16 @@ }, { "@timestamp": "2017-06-17T14:58:03.940Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", @@ -1155,9 +1808,16 @@ }, { "@timestamp": "2017-06-17T14:58:04.040Z", + "event.category": [ + "database" + ], "event.dataset": "postgresql.log", + "event.kind": "event", "event.module": "postgresql", "event.timezone": "CEST", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "LOG", diff --git a/filebeat/module/redis/log/ingest/pipeline.json b/filebeat/module/redis/log/ingest/pipeline.json deleted file mode 100644 index c9ec2d3371b..00000000000 --- a/filebeat/module/redis/log/ingest/pipeline.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "description": "Pipeline for parsing redis logs", - "processors": [ - { - "grok": { - "field": "message", - "patterns": [ - "(%{POSINT:process.pid:long}:%{CHAR:redis.log.role} )?(%{REDISTIMESTAMP1:redis.log.timestamp}||%{REDISTIMESTAMP2:redis.log.timestamp}) %{REDISLEVEL:log.level} %{GREEDYDATA:message}", - "%{POSINT:process.pid:long}:signal-handler \\(%{POSINT:redis.log.timestamp}\\) %{GREEDYDATA:message}" - ], - "pattern_definitions": { - "CHAR": "[a-zA-Z]", - "REDISLEVEL": "[.\\-*#]", - "REDISTIMESTAMP1": "%{MONTHDAY} %{MONTH} %{TIME}", - "REDISTIMESTAMP2": "%{MONTHDAY} %{MONTH} %{YEAR} %{TIME}" - } - } - }, - { - "script": { - "lang": "painless", - "source": "if (ctx.log.level == params.dot) {\n ctx.log.level = params.debug;\n } else if (ctx.log.level == params.dash) {\n ctx.log.level = params.verbose;\n } else if (ctx.log.level == params.asterisk) {\n ctx.log.level = params.notice;\n } else if (ctx.log.level == params.hash) {\n ctx.log.level = params.warning;\n }", - "params": { - "dot": ".", - "debug": "debug", - "dash": "-", - "verbose": "verbose", - "asterisk": "*", - "notice": "notice", - "hash": "#", - "warning": "warning" - } - } - }, - { - "script": { - "lang": "painless", - "source": "if (ctx.redis.log.role == params.master_abbrev) {\n ctx.redis.log.role = params.master;\n } else if (ctx.redis.log.role == params.slave_abbrev) {\n ctx.redis.log.role = params.slave;\n } else if (ctx.redis.log.role == params.child_abbrev) {\n ctx.redis.log.role = params.child;\n } else if (ctx.redis.log.role == params.sentinel_abbrev) {\n ctx.redis.log.role = params.sentinel;\n }\n ", - "params": { - "master_abbrev": "M", - "master": "master", - "slave_abbrev": "S", - "slave": "slave", - "child_abbrev": "C", - "child": "child", - "sentinel_abbrev": "X", - "sentinel": "sentinel" - } - } - }, - { - "rename": { - "field": "@timestamp", - "target_field": "event.created" - } - }, - { - "date": { - "field": "redis.log.timestamp", - "target_field": "@timestamp", - "formats": [ - "dd MMM yyyy H:m:s.SSS", - "dd MMM H:m:s.SSS", - "dd MMM H:m:s", - "UNIX" - ], - "ignore_failure": true - } - }, - { - "remove": { - "field": "redis.log.timestamp", - "ignore_failure": true - } - } - ], - "on_failure": [ - { - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - } - ] -} diff --git a/filebeat/module/redis/log/ingest/pipeline.yml b/filebeat/module/redis/log/ingest/pipeline.yml new file mode 100644 index 00000000000..d1c08cab378 --- /dev/null +++ b/filebeat/module/redis/log/ingest/pipeline.yml @@ -0,0 +1,84 @@ +description: Pipeline for parsing redis logs +processors: +- grok: + field: message + patterns: + - (%{POSINT:process.pid:long}:%{CHAR:redis.log.role} )?(%{REDISTIMESTAMP1:redis.log.timestamp}||%{REDISTIMESTAMP2:redis.log.timestamp}) + %{REDISLEVEL:log.level} %{GREEDYDATA:message} + - '%{POSINT:process.pid:long}:signal-handler \(%{POSINT:redis.log.timestamp}\) + %{GREEDYDATA:message}' + pattern_definitions: + CHAR: '[a-zA-Z]' + REDISLEVEL: '[.\-*#]' + REDISTIMESTAMP1: '%{MONTHDAY} %{MONTH} %{TIME}' + REDISTIMESTAMP2: '%{MONTHDAY} %{MONTH} %{YEAR} %{TIME}' +- script: + lang: painless + source: >- + if (ctx.log.level == params.dot) { + ctx.log.level = params.debug; + } else if (ctx.log.level == params.dash) { + ctx.log.level = params.verbose; + } else if (ctx.log.level == params.asterisk) { + ctx.log.level = params.notice; + } else if (ctx.log.level == params.hash) { + ctx.log.level = params.warning; + } + params: + dot: . + debug: debug + dash: '-' + verbose: verbose + asterisk: '*' + notice: notice + hash: '#' + warning: warning +- script: + lang: painless + source: >- + if (ctx.redis.log.role == params.master_abbrev) { + ctx.redis.log.role = params.master; + } else if (ctx.redis.log.role == params.slave_abbrev) { + ctx.redis.log.role = params.slave; + } else if (ctx.redis.log.role == params.child_abbrev) { + ctx.redis.log.role = params.child; + } else if (ctx.redis.log.role == params.sentinel_abbrev) { + ctx.redis.log.role = params.sentinel; + } + params: + master_abbrev: M + master: master + slave_abbrev: S + slave: slave + child_abbrev: C + child: child + sentinel_abbrev: X + sentinel: sentinel +- rename: + field: '@timestamp' + target_field: event.created +- date: + field: redis.log.timestamp + target_field: '@timestamp' + formats: + - dd MMM yyyy H:m:s.SSS + - dd MMM H:m:s.SSS + - dd MMM H:m:s + - UNIX + ignore_failure: true +- remove: + field: redis.log.timestamp + ignore_failure: true +- set: + field: event.kind + value: event +- append: + field: event.category + value: database +- append: + field: event.type + value: info +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/redis/log/manifest.yml b/filebeat/module/redis/log/manifest.yml index 3c63a894c28..728e098d4c2 100644 --- a/filebeat/module/redis/log/manifest.yml +++ b/filebeat/module/redis/log/manifest.yml @@ -10,5 +10,5 @@ var: os.windows: - "c:/program files/Redis/logs/redis.log*" -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/log.yml diff --git a/filebeat/module/redis/log/test/redis-5.0.3.log-expected.json b/filebeat/module/redis/log/test/redis-5.0.3.log-expected.json index 71d76c30a96..d3efc715fe3 100644 --- a/filebeat/module/redis/log/test/redis-5.0.3.log-expected.json +++ b/filebeat/module/redis/log/test/redis-5.0.3.log-expected.json @@ -1,7 +1,14 @@ [ { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", diff --git a/filebeat/module/redis/log/test/redis-darwin-3.0.2.log-expected.json b/filebeat/module/redis/log/test/redis-darwin-3.0.2.log-expected.json index ff533b577ac..365ced2400b 100644 --- a/filebeat/module/redis/log/test/redis-darwin-3.0.2.log-expected.json +++ b/filebeat/module/redis/log/test/redis-darwin-3.0.2.log-expected.json @@ -1,7 +1,14 @@ [ { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -12,8 +19,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "warning", @@ -24,8 +38,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -36,8 +57,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -48,8 +76,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.offset": 1478, @@ -58,8 +93,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "warning", @@ -70,8 +112,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -82,8 +131,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -94,8 +150,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "warning", @@ -106,8 +169,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -118,8 +188,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "warning", @@ -130,8 +207,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -142,8 +226,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -154,8 +245,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.offset": 3273, @@ -164,8 +262,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "warning", @@ -176,8 +281,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -188,8 +300,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -200,8 +319,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "warning", diff --git a/filebeat/module/redis/log/test/redis-debian-1.2.6.log-expected.json b/filebeat/module/redis/log/test/redis-debian-1.2.6.log-expected.json index ff13e461ef4..a8f9d71736e 100644 --- a/filebeat/module/redis/log/test/redis-debian-1.2.6.log-expected.json +++ b/filebeat/module/redis/log/test/redis-debian-1.2.6.log-expected.json @@ -1,7 +1,14 @@ [ { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -10,8 +17,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -20,8 +34,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -30,8 +51,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -40,8 +68,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -50,8 +85,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -60,8 +102,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -70,8 +119,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -80,8 +136,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -90,8 +153,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -100,8 +170,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -110,8 +187,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -120,8 +204,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -130,8 +221,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -140,8 +238,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -150,8 +255,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -160,8 +272,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -170,8 +289,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -180,8 +306,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -190,8 +323,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -200,8 +340,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -210,8 +357,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -220,8 +374,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -230,8 +391,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -240,8 +408,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -250,8 +425,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -260,8 +442,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -270,8 +459,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -280,8 +476,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -290,8 +493,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -300,8 +510,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -310,8 +527,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -320,8 +544,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -330,8 +561,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -340,8 +578,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -350,8 +595,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -360,8 +612,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -370,8 +629,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -380,8 +646,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -390,8 +663,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -400,8 +680,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -410,8 +697,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -420,8 +714,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -430,8 +731,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -440,8 +748,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -450,8 +765,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -460,8 +782,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -470,8 +799,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -480,8 +816,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -490,8 +833,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -500,8 +850,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -510,8 +867,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -520,8 +884,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -530,8 +901,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -540,8 +918,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -550,8 +935,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -560,8 +952,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -570,8 +969,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -580,8 +986,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -590,8 +1003,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -600,8 +1020,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -610,8 +1037,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -620,8 +1054,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -630,8 +1071,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -640,8 +1088,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -650,8 +1105,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -660,8 +1122,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -670,8 +1139,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -680,8 +1156,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -690,8 +1173,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -700,8 +1190,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -710,8 +1207,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -720,8 +1224,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -730,8 +1241,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -740,8 +1258,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -750,8 +1275,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -760,8 +1292,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -770,8 +1309,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -780,8 +1326,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -790,8 +1343,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -800,8 +1360,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -810,8 +1377,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -820,8 +1394,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -830,8 +1411,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -840,8 +1428,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -850,8 +1445,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -860,8 +1462,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -870,8 +1479,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -880,8 +1496,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -890,8 +1513,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -900,8 +1530,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -910,8 +1547,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -920,8 +1564,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -930,8 +1581,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -940,8 +1598,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -950,8 +1615,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -960,8 +1632,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -970,8 +1649,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -980,8 +1666,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -990,8 +1683,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", diff --git a/filebeat/module/redis/log/test/redis-windows-2.4.6.log-expected.json b/filebeat/module/redis/log/test/redis-windows-2.4.6.log-expected.json index 4fb3b4e92b0..dbafda2b3df 100644 --- a/filebeat/module/redis/log/test/redis-windows-2.4.6.log-expected.json +++ b/filebeat/module/redis/log/test/redis-windows-2.4.6.log-expected.json @@ -1,7 +1,14 @@ [ { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -10,8 +17,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "warning", @@ -20,8 +34,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -30,8 +51,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -40,8 +68,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -50,8 +85,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -60,8 +102,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -70,8 +119,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -80,8 +136,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -90,8 +153,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -100,8 +170,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -110,8 +187,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -120,8 +204,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -130,8 +221,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -140,8 +238,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -150,8 +255,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -160,8 +272,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -170,8 +289,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -180,8 +306,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -190,8 +323,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -200,8 +340,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -210,8 +357,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -220,8 +374,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -230,8 +391,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -240,8 +408,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -250,8 +425,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -260,8 +442,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -270,8 +459,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -280,8 +476,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -290,8 +493,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -300,8 +510,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -310,8 +527,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -320,8 +544,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", @@ -330,8 +561,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "verbose", diff --git a/filebeat/module/redis/log/test/test.log-expected.json b/filebeat/module/redis/log/test/test.log-expected.json index b74b64a93ed..cee22b55c3b 100644 --- a/filebeat/module/redis/log/test/test.log-expected.json +++ b/filebeat/module/redis/log/test/test.log-expected.json @@ -1,7 +1,14 @@ [ { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -12,8 +19,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "debug", @@ -22,8 +36,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.level": "notice", @@ -32,8 +53,15 @@ "service.type": "redis" }, { + "event.category": [ + "database" + ], "event.dataset": "redis.log", + "event.kind": "event", "event.module": "redis", + "event.type": [ + "info" + ], "fileset.name": "log", "input.type": "log", "log.offset": 250, diff --git a/filebeat/module/santa/_meta/fields.yml b/filebeat/module/santa/_meta/fields.yml index fea0b03a78c..57255dd76c8 100644 --- a/filebeat/module/santa/_meta/fields.yml +++ b/filebeat/module/santa/_meta/fields.yml @@ -56,10 +56,10 @@ - name: mount description: The disk volume path. - - name: certificate.common_name - type: keyword - description: Common name from code signing certificate. + - name: certificate.common_name + type: keyword + description: Common name from code signing certificate. - - name: certificate.sha256 - type: keyword - description: SHA256 hash of code signing certificate. + - name: certificate.sha256 + type: keyword + description: SHA256 hash of code signing certificate. diff --git a/filebeat/module/santa/fields.go b/filebeat/module/santa/fields.go index 06b53e41d84..cd3f44d3647 100644 --- a/filebeat/module/santa/fields.go +++ b/filebeat/module/santa/fields.go @@ -32,5 +32,5 @@ func init() { // AssetSanta returns asset data. // This is the base64 encoded gzipped contents of module/santa. func AssetSanta() string { - return "eJyUk82O2jAQgO88xWhP7WFRl4o95FAphfRHBS0iK7W3yhtPiJXEE9lOW96+spMFk8QtcCJj+/s8npl7KPEYgWbSsBmAEabCCO4+Ex0qhNSG72YAHHWmRGMEyQg+zACgW4Mt8bbCGUAusOI6ckv3IFmNZ6r9mWODERwUtU0fmWCeMf3nmcUyu/EUfgWWePxNintx/MPqxiaR/EhWXvxCF3e0kYVjJvSNnnizefoeEq17IJiCme5BOBiicj6WK2T6NvUq2T+HzHtHg5wUmAJtZtrdZEJcE8dbtNuQ86lBxYyQB4cEyrsumVByocuR0u+OEfuT6wyXz/pr+i3e7ZJ437eFnnun/E4can9R1dZ4sTTQPBfY73JHvKv7nJdW/w9iM7T7oFFkKKMqgNKoBKuuonVbQbb1C6rQzTS3fwa8U+ks50G/v0r3MV3/6w1siYf3Pg/FbrdJIE3XkG7fLR8Wm6uMDhnQ5cMXP7lYM1qbxvd1LYXk8CYXFeqjNli77nsbzLKV5hZ6w0zRs14ZGSojcpExg/OM6prkT69GU+N2YVi5I44FuaIaMjtcWhyknTQfHtbqgi2Wj9ca0y/xYvkIBdOFHeKw728AAAD//5mnu+4=" + return "eJyUk82O2jAQgO88xWhP7WFRl4o9cKiUQvqjghaRldpb5Y0nxErsiWynLW9f2UkhJPGWcCJj+/tm7Jl7KPC0AsOUZTMAK2yJK7j7THQsERIXvpsBcDSpFpUVpFbwYQYAzRrsiNclzgAygSU3K790D4pJvFDdz54qXMFRU121kRHmBdN+XlgsdRvP4X/AAk+/SfNOHP8wWbki4h/xuhO/0kUNbWDhmAoz0RNtt0/fQ6JNCwSbM9tcCAdLVMyHco3MTFOv48NzyHzwNMhIg83RVWZ8JiNiSRynaHch51OFmlmhjh4JlDVdMqLkwhQDZbc7BuxPvjN8PZuvybdov4+jQ9sWZt451e3EvvYXlbXEq6We5jnHdpc/0km9y3mpzf8grkK3DypNllIqAyiDWrDyJlqzFVQtX1CHMjPc/enxzk/nOA/m/U26j8nmtTtwT9zP+zIU+/02hiTZQLJ7t3xYbG8yemRAl/Vv/Oxi1WBtHN++ayEUhzeZKNGcjEXpu+9tsMpa2Sn0itl8pOFT1FZkImUW5ylJSepn751CY3dlW/ujngmZJgmpGzQjjspNXVfyegomZ4vl41R78iVaLB8hZyZ3wx12/w0AAP//xDi+7g==" } diff --git a/filebeat/module/santa/log/ingest/pipeline.json b/filebeat/module/santa/log/ingest/pipeline.json deleted file mode 100644 index 4eaddc753a6..00000000000 --- a/filebeat/module/santa/log/ingest/pipeline.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "description": "Pipeline for parsing Google Santa logs.", - "processors": [ - { - "grok": { - "field": "message", - "patterns": [ - "\\[%{TIMESTAMP_ISO8601:process.start}\\] I santad: action=%{NOT_SEPARATOR:santa.action}\\|decision=%{NOT_SEPARATOR:santa.decision}\\|reason=%{NOT_SEPARATOR:santa.reason}\\|sha256=%{NOT_SEPARATOR:hash.sha256}\\|path=%{NOT_SEPARATOR:process.executable}(\\|args=%{NOT_SEPARATOR:process.args})?(\\|cert_sha256=%{NOT_SEPARATOR:certificate.sha256})?(\\|cert_cn=%{NOT_SEPARATOR:certificate.common_name})?\\|pid=%{NUMBER:process.pid:long}\\|ppid=%{NUMBER:process.ppid:long}\\|uid=%{NUMBER:user.id}\\|user=%{NOT_SEPARATOR:user.name}\\|gid=%{NUMBER:group.id}\\|group=%{NOT_SEPARATOR:group.name}\\|mode=%{WORD:santa.mode}", - "\\[%{TIMESTAMP_ISO8601:timestamp}\\] I santad: action=%{NOT_SEPARATOR:santa.action}\\|mount=%{NOT_SEPARATOR:santa.disk.mount}\\|volume=%{NOT_SEPARATOR:santa.disk.volume}\\|bsdname=%{NOT_SEPARATOR:santa.disk.bsdname}\\|fs=%{NOT_SEPARATOR:santa.disk.fs}\\|model=%{NOT_SEPARATOR:santa.disk.model}\\|serial=%{NOT_SEPARATOR:santa.disk.serial}\\|bus=%{NOT_SEPARATOR:santa.disk.bus}\\|dmgpath=%{NOT_SEPARATOR:santa.disk.dmgpath}?" - ], - "pattern_definitions": { - "NOT_SEPARATOR": "[^\\|]+" - } - } - }, - { - "rename": { - "field": "message", - "target_field": "log.original" - } - }, - { - "date": { - "field": "process.start", - "target_field": "process.start", - "formats": [ - "ISO8601" - ], - "ignore_failure": true - } - }, - { - "set": { - "field": "@timestamp", - "value": "{{ process.start }}", - "ignore_failure": true - } - }, - { - "split": { - "field": "process.args", - "separator": " ", - "ignore_failure": true - } - }, - { - "date": { - "field": "timestamp", - "target_field": "@timestamp", - "formats": [ - "ISO8601" - ], - "ignore_failure": true - } - }, - { - "remove": { - "field": "timestamp", - "ignore_missing": true - } - } - ], - "on_failure": [ - { - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - } - ] -} diff --git a/filebeat/module/santa/log/ingest/pipeline.yml b/filebeat/module/santa/log/ingest/pipeline.yml new file mode 100644 index 00000000000..11ad4cead6c --- /dev/null +++ b/filebeat/module/santa/log/ingest/pipeline.yml @@ -0,0 +1,91 @@ +description: Pipeline for parsing Google Santa logs. +processors: +- grok: + field: message + patterns: + - '\[%{TIMESTAMP_ISO8601:process.start}\] %{NOT_SEPARATOR:log.level} santad: action=%{NOT_SEPARATOR:santa.action}\|decision=%{NOT_SEPARATOR:santa.decision}\|reason=%{NOT_SEPARATOR:santa.reason}\|sha256=%{NOT_SEPARATOR:process.hash.sha256}\|path=%{NOT_SEPARATOR:process.executable}(\|args=%{NOT_SEPARATOR:santa.args})?(\|cert_sha256=%{NOT_SEPARATOR:santa.certificate.sha256})?(\|cert_cn=%{NOT_SEPARATOR:santa.certificate.common_name})?\|pid=%{NUMBER:process.pid:long}\|ppid=%{NUMBER:process.ppid:long}\|uid=%{NUMBER:user.id}\|user=%{NOT_SEPARATOR:user.name}\|gid=%{NUMBER:group.id}\|group=%{NOT_SEPARATOR:group.name}\|mode=%{WORD:santa.mode}' + - '\[%{TIMESTAMP_ISO8601:timestamp}\] %{NOT_SEPARATOR:log.level} santad: action=%{NOT_SEPARATOR:santa.action}\|mount=%{NOT_SEPARATOR:santa.disk.mount}\|volume=%{NOT_SEPARATOR:santa.disk.volume}\|bsdname=%{NOT_SEPARATOR:santa.disk.bsdname}\|fs=%{NOT_SEPARATOR:santa.disk.fs}\|model=%{NOT_SEPARATOR:santa.disk.model}\|serial=%{NOT_SEPARATOR:santa.disk.serial}\|bus=%{NOT_SEPARATOR:santa.disk.bus}\|dmgpath=%{NOT_SEPARATOR:santa.disk.dmgpath}?' + pattern_definitions: + NOT_SEPARATOR: '[^\|]+' +- rename: + field: message + target_field: log.original +- date: + field: process.start + target_field: process.start + formats: + - ISO8601 + ignore_failure: true +- set: + field: '@timestamp' + value: '{{ process.start }}' + ignore_failure: true +- split: + field: santa.args + separator: ' ' + ignore_failure: true +- date: + field: timestamp + target_field: '@timestamp' + formats: + - ISO8601 + ignore_failure: true +- remove: + field: timestamp + ignore_missing: true +- append: + field: process.args + value: "{{process.executable}}" + if: "ctx?.process?.executable != null" +- foreach: + field: santa.args + processor: + append: + field: process.args + value: "{{_ingest._value}}" + ignore_missing: true +- remove: + field: santa.args + ignore_missing: true +- set: + field: event.kind + value: event +- append: + field: event.category + value: process + if: "ctx?.santa?.action == 'EXEC'" +- append: + field: event.type + value: start + if: "ctx?.santa?.action == 'EXEC'" +- set: + field: event.outcome + value: success + if: "ctx?.santa?.decision == 'ALLOW'" +- set: + field: event.outcome + value: failure + if: "ctx?.santa?.decision == 'DENY'" +- set: + field: event.action + value: "{{santa.action}}" + if: "ctx?.santa?.action != null" +- lowercase: + field: event.action + ignore_missing: true +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- append: + field: related.hash + value: "{{santa.certificate.sha256}}" + if: "ctx?.santa?.certificate?.sha256 != null" +- append: + field: related.hash + value: "{{process.hash.sha256}}" + if: "ctx?.process?.hash != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/santa/log/manifest.yml b/filebeat/module/santa/log/manifest.yml index d0369930490..43cad6e1934 100644 --- a/filebeat/module/santa/log/manifest.yml +++ b/filebeat/module/santa/log/manifest.yml @@ -4,8 +4,9 @@ var: - name: paths default: - /var/log/santa.log + - /var/db/santa/santa.log - name: input default: file -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/{{.input}}.yml diff --git a/filebeat/module/santa/log/test/santa.log-expected.json b/filebeat/module/santa/log/test/santa.log-expected.json index ab94261c13a..6c1fbe81184 100644 --- a/filebeat/module/santa/log/test/santa.log-expected.json +++ b/filebeat/module/santa/log/test/santa.log-expected.json @@ -1,25 +1,43 @@ [ { "@timestamp": "2018-12-10T06:45:16.802Z", - "certificate.common_name": "Software Signing", - "certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "0", "group.name": "wheel", - "hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "input.type": "log", + "log.level": "I", "log.offset": 0, "log.original": "[2018-12-10T06:45:16.802Z] I santad: action=EXEC|decision=ALLOW|reason=CERT|sha256=c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4|path=/usr/libexec/xpcproxy|args=/usr/sbin/newsyslog|cert_sha256=2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32|cert_cn=Software Signing|pid=29678|ppid=1|uid=0|user=root|gid=0|group=wheel|mode=M", "process.args": [ + "/usr/libexec/xpcproxy", "/usr/sbin/newsyslog" ], "process.executable": "/usr/libexec/xpcproxy", + "process.hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "process.pid": 29678, "process.ppid": 1, "process.start": "2018-12-10T06:45:16.802Z", + "related.hash": [ + "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4" + ], + "related.user": [ + "root" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Software Signing", + "santa.certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "CERT", @@ -29,26 +47,44 @@ }, { "@timestamp": "2018-12-10T06:45:16.802Z", - "certificate.common_name": "Software Signing", - "certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "0", "group.name": "wheel", - "hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "input.type": "log", + "log.level": "I", "log.offset": 360, "log.original": "[2018-12-10T06:45:16.802Z] I santad: action=EXEC|decision=ALLOW|reason=CERT|sha256=c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4|path=/usr/libexec/xpcproxy|args=xpcproxy com.apple.systemstats.daily|cert_sha256=2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32|cert_cn=Software Signing|pid=29679|ppid=1|uid=0|user=root|gid=0|group=wheel|mode=M", "process.args": [ + "/usr/libexec/xpcproxy", "xpcproxy", "com.apple.systemstats.daily" ], "process.executable": "/usr/libexec/xpcproxy", + "process.hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "process.pid": 29679, "process.ppid": 1, "process.start": "2018-12-10T06:45:16.802Z", + "related.hash": [ + "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4" + ], + "related.user": [ + "root" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Software Signing", + "santa.certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "CERT", @@ -58,25 +94,43 @@ }, { "@timestamp": "2018-12-10T06:45:16.851Z", - "certificate.common_name": "Software Signing", - "certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "0", "group.name": "wheel", - "hash.sha256": "746f0dbafb7e675d5ce67131e5544772ee612b894e8ab51d3ce2d21f7cb7332d", "input.type": "log", + "log.level": "I", "log.offset": 737, "log.original": "[2018-12-10T06:45:16.851Z] I santad: action=EXEC|decision=ALLOW|reason=CERT|sha256=746f0dbafb7e675d5ce67131e5544772ee612b894e8ab51d3ce2d21f7cb7332d|path=/usr/sbin/newsyslog|args=/usr/sbin/newsyslog|cert_sha256=2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32|cert_cn=Software Signing|pid=29678|ppid=1|uid=0|user=root|gid=0|group=wheel|mode=M", "process.args": [ + "/usr/sbin/newsyslog", "/usr/sbin/newsyslog" ], "process.executable": "/usr/sbin/newsyslog", + "process.hash.sha256": "746f0dbafb7e675d5ce67131e5544772ee612b894e8ab51d3ce2d21f7cb7332d", "process.pid": 29678, "process.ppid": 1, "process.start": "2018-12-10T06:45:16.851Z", + "related.hash": [ + "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "746f0dbafb7e675d5ce67131e5544772ee612b894e8ab51d3ce2d21f7cb7332d" + ], + "related.user": [ + "root" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Software Signing", + "santa.certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "CERT", @@ -86,26 +140,44 @@ }, { "@timestamp": "2018-12-10T06:45:16.859Z", - "certificate.common_name": "Software Signing", - "certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "0", "group.name": "wheel", - "hash.sha256": "d6be9bfbd777ac5dcd30488014acc787a2df5ce840f1fe4d5742d323ee00392f", "input.type": "log", + "log.level": "I", "log.offset": 1095, "log.original": "[2018-12-10T06:45:16.859Z] I santad: action=EXEC|decision=ALLOW|reason=CERT|sha256=d6be9bfbd777ac5dcd30488014acc787a2df5ce840f1fe4d5742d323ee00392f|path=/usr/sbin/systemstats|args=/usr/sbin/systemstats --daily|cert_sha256=2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32|cert_cn=Software Signing|pid=29679|ppid=1|uid=0|user=root|gid=0|group=wheel|mode=M", "process.args": [ + "/usr/sbin/systemstats", "/usr/sbin/systemstats", "--daily" ], "process.executable": "/usr/sbin/systemstats", + "process.hash.sha256": "d6be9bfbd777ac5dcd30488014acc787a2df5ce840f1fe4d5742d323ee00392f", "process.pid": 29679, "process.ppid": 1, "process.start": "2018-12-10T06:45:16.859Z", + "related.hash": [ + "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "d6be9bfbd777ac5dcd30488014acc787a2df5ce840f1fe4d5742d323ee00392f" + ], + "related.user": [ + "root" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Software Signing", + "santa.certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "CERT", @@ -115,25 +187,43 @@ }, { "@timestamp": "2018-12-10T08:45:27.810Z", - "certificate.common_name": "Software Signing", - "certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "0", "group.name": "wheel", - "hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "input.type": "log", + "log.level": "I", "log.offset": 1465, "log.original": "[2018-12-10T08:45:27.810Z] I santad: action=EXEC|decision=ALLOW|reason=CERT|sha256=c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4|path=/usr/libexec/xpcproxy|args=/usr/sbin/newsyslog|cert_sha256=2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32|cert_cn=Software Signing|pid=29681|ppid=1|uid=0|user=root|gid=0|group=wheel|mode=M", "process.args": [ + "/usr/libexec/xpcproxy", "/usr/sbin/newsyslog" ], "process.executable": "/usr/libexec/xpcproxy", + "process.hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "process.pid": 29681, "process.ppid": 1, "process.start": "2018-12-10T08:45:27.810Z", + "related.hash": [ + "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4" + ], + "related.user": [ + "root" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Software Signing", + "santa.certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "CERT", @@ -143,26 +233,44 @@ }, { "@timestamp": "2018-12-10T08:45:27.810Z", - "certificate.common_name": "Software Signing", - "certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "0", "group.name": "wheel", - "hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "input.type": "log", + "log.level": "I", "log.offset": 1825, "log.original": "[2018-12-10T08:45:27.810Z] I santad: action=EXEC|decision=ALLOW|reason=CERT|sha256=c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4|path=/usr/libexec/xpcproxy|args=xpcproxy com.adobe.AAM.Scheduler-1.0|cert_sha256=2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32|cert_cn=Software Signing|pid=29680|ppid=1|uid=0|user=root|gid=0|group=wheel|mode=M", "process.args": [ + "/usr/libexec/xpcproxy", "xpcproxy", "com.adobe.AAM.Scheduler-1.0" ], "process.executable": "/usr/libexec/xpcproxy", + "process.hash.sha256": "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4", "process.pid": 29680, "process.ppid": 1, "process.start": "2018-12-10T08:45:27.810Z", + "related.hash": [ + "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "c4bc09fd2f248534552f517acf3edb9a635aba2b02e46f49df683ea9b778e5b4" + ], + "related.user": [ + "root" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Software Signing", + "santa.certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "CERT", @@ -172,24 +280,41 @@ }, { "@timestamp": "2018-12-10T21:37:27.247Z", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "0", "group.name": "wheel", - "hash.sha256": "08bd61582657cd6d78c9e071d34d79a32bb59e7210077a44919d2c5477e988a1", "input.type": "log", + "log.level": "I", "log.offset": 2202, "log.original": "[2018-12-10T21:37:27.247Z] I santad: action=EXEC|decision=ALLOW|reason=UNKNOWN|sha256=08bd61582657cd6d78c9e071d34d79a32bb59e7210077a44919d2c5477e988a1|path=/usr/local/Cellar/osquery/3.3.0_1/bin/osqueryd|args=/usr/local/bin/osqueryd --flagfile=/private/var/osquery/osquery.flags --logger_min_stderr=1|pid=45084|ppid=1|uid=0|user=root|gid=0|group=wheel|mode=M", "process.args": [ + "/usr/local/Cellar/osquery/3.3.0_1/bin/osqueryd", "/usr/local/bin/osqueryd", "--flagfile=/private/var/osquery/osquery.flags", "--logger_min_stderr=1" ], "process.executable": "/usr/local/Cellar/osquery/3.3.0_1/bin/osqueryd", + "process.hash.sha256": "08bd61582657cd6d78c9e071d34d79a32bb59e7210077a44919d2c5477e988a1", "process.pid": 45084, "process.ppid": 1, "process.start": "2018-12-10T21:37:27.247Z", + "related.hash": [ + "08bd61582657cd6d78c9e071d34d79a32bb59e7210077a44919d2c5477e988a1" + ], + "related.user": [ + "root" + ], "santa.action": "EXEC", "santa.decision": "ALLOW", "santa.mode": "M", @@ -200,22 +325,42 @@ }, { "@timestamp": "2018-12-10T16:24:43.992Z", - "certificate.common_name": "Software Signing", - "certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "20", "group.name": "staff", - "hash.sha256": "63b6a54848d7b4adf726d68f11409a4ac05b43926cb0f2792f7d41dc0221c106", "input.type": "log", + "log.level": "I", "log.offset": 2560, "log.original": "[2018-12-10T16:24:43.992Z] I santad: action=EXEC|decision=ALLOW|reason=CERT|sha256=63b6a54848d7b4adf726d68f11409a4ac05b43926cb0f2792f7d41dc0221c106|path=/usr/bin/basename|cert_sha256=2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32|cert_cn=Software Signing|pid=40757|ppid=40756|uid=501|user=akroh|gid=20|group=staff|mode=M", + "process.args": [ + "/usr/bin/basename" + ], "process.executable": "/usr/bin/basename", + "process.hash.sha256": "63b6a54848d7b4adf726d68f11409a4ac05b43926cb0f2792f7d41dc0221c106", "process.pid": 40757, "process.ppid": 40756, "process.start": "2018-12-10T16:24:43.992Z", + "related.hash": [ + "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", + "63b6a54848d7b4adf726d68f11409a4ac05b43926cb0f2792f7d41dc0221c106" + ], + "related.user": [ + "akroh" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Software Signing", + "santa.certificate.sha256": "2aa4b9973b7ba07add447ee4da8b5337c3ee2c3a991911e80e7282e8a751fc32", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "CERT", @@ -225,18 +370,26 @@ }, { "@timestamp": "2018-12-14T05:35:38.313Z", - "certificate.common_name": "Developer ID Application: Google, Inc. (EQHXZ8M8AV)", - "certificate.sha256": "345a8e098bd04794aaeefda8c9ef56a0bf3d3706d67d35bc0e23f11bb3bffce5", + "event.action": "exec", + "event.category": [ + "process" + ], "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", + "event.outcome": "success", + "event.type": [ + "start" + ], "fileset.name": "log", "group.id": "20", "group.name": "staff", - "hash.sha256": "a8defc1b24c45f6dabeb8298af5f8e1daf39e1504e16f878345f15ac94ae96d7", "input.type": "log", + "log.level": "I", "log.offset": 2899, "log.original": "[2018-12-14T05:35:38.313Z] I santad: action=EXEC|decision=ALLOW|reason=UNKNOWN|sha256=a8defc1b24c45f6dabeb8298af5f8e1daf39e1504e16f878345f15ac94ae96d7|path=/Applications/Google Chrome.app/Contents/Versions/70.0.3538.110/Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper|args=/Applications/Google Chrome.app/Contents/Versions/70.0.3538.110/Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper --type=utility --field-trial-handle=120122713615061869,9401617251746517350,131072 --lang=en-US --service-sandbox-type=utility --service-request-channel-token=10458143409865682077 --seatbelt-client=262|cert_sha256=345a8e098bd04794aaeefda8c9ef56a0bf3d3706d67d35bc0e23f11bb3bffce5|cert_cn=Developer ID Application: Google, Inc. (EQHXZ8M8AV)|pid=89238|ppid=704|uid=501|user=akroh|gid=20|group=staff|mode=M", "process.args": [ + "/Applications/Google Chrome.app/Contents/Versions/70.0.3538.110/Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper", "/Applications/Google", "Chrome.app/Contents/Versions/70.0.3538.110/Google", "Chrome", @@ -251,10 +404,20 @@ "--seatbelt-client=262" ], "process.executable": "/Applications/Google Chrome.app/Contents/Versions/70.0.3538.110/Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper", + "process.hash.sha256": "a8defc1b24c45f6dabeb8298af5f8e1daf39e1504e16f878345f15ac94ae96d7", "process.pid": 89238, "process.ppid": 704, "process.start": "2018-12-14T05:35:38.313Z", + "related.hash": [ + "345a8e098bd04794aaeefda8c9ef56a0bf3d3706d67d35bc0e23f11bb3bffce5", + "a8defc1b24c45f6dabeb8298af5f8e1daf39e1504e16f878345f15ac94ae96d7" + ], + "related.user": [ + "akroh" + ], "santa.action": "EXEC", + "santa.certificate.common_name": "Developer ID Application: Google, Inc. (EQHXZ8M8AV)", + "santa.certificate.sha256": "345a8e098bd04794aaeefda8c9ef56a0bf3d3706d67d35bc0e23f11bb3bffce5", "santa.decision": "ALLOW", "santa.mode": "M", "santa.reason": "UNKNOWN", @@ -264,10 +427,13 @@ }, { "@timestamp": "2018-12-17T03:03:52.337Z", + "event.action": "diskappear", "event.dataset": "santa.log", + "event.kind": "event", "event.module": "santa", "fileset.name": "log", "input.type": "log", + "log.level": "I", "log.offset": 3712, "log.original": "[2018-12-17T03:03:52.337Z] I santad: action=DISKAPPEAR|mount=/Volumes/Recovery|volume=Recovery|bsdname=disk1s3|fs=apfs|model=APPLE SSD SM0512L|serial=C026495006UHCHH1Q|bus=PCI-Express|dmgpath=", "santa.action": "DISKAPPEAR", diff --git a/filebeat/module/system/auth/ingest/pipeline.json b/filebeat/module/system/auth/ingest/pipeline.json deleted file mode 100644 index 8df0a77e582..00000000000 --- a/filebeat/module/system/auth/ingest/pipeline.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "description": "Pipeline for parsing system authorisation/secure logs", - "processors": [ - { - "grok": { - "field": "message", - "ignore_missing": true, - "pattern_definitions" : { - "GREEDYMULTILINE" : "(.|\n)*", - "TIMESTAMP": "(?:%{TIMESTAMP_ISO8601}|%{SYSLOGTIMESTAMP})" - }, - "patterns": [ - "%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: %{DATA:system.auth.ssh.event} %{DATA:system.auth.ssh.method} for (invalid user )?%{DATA:user.name} from %{IPORHOST:source.ip} port %{NUMBER:source.port:long} ssh2(: %{GREEDYDATA:system.auth.ssh.signature})?", - "%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: %{DATA:system.auth.ssh.event} user %{DATA:user.name} from %{IPORHOST:source.ip}", - "%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: Did not receive identification string from %{IPORHOST:system.auth.ssh.dropped_ip}", - "%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: \\s*%{DATA:user.name} :( %{DATA:system.auth.sudo.error} ;)? TTY=%{DATA:system.auth.sudo.tty} ; PWD=%{DATA:system.auth.sudo.pwd} ; USER=%{DATA:system.auth.sudo.user} ; COMMAND=%{GREEDYDATA:system.auth.sudo.command}", - "%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: new group: name=%{DATA:group.name}, GID=%{NUMBER:group.id}", - "%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: new user: name=%{DATA:user.name}, UID=%{NUMBER:user.id}, GID=%{NUMBER:group.id}, home=%{DATA:system.auth.useradd.home}, shell=%{DATA:system.auth.useradd.shell}$", - "%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname}? %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: %{GREEDYMULTILINE:system.auth.message}" - ] - } - }, - { - "remove": { - "field": "message" - } - }, - { - "rename": { - "field": "system.auth.message", - "target_field": "message", - "ignore_missing": true - } - }, - { - "set": { - "field": "source.ip", - "value": "{{system.auth.ssh.dropped_ip}}", - "if": "ctx.containsKey('system') && ctx.system.containsKey('auth') && ctx.system.auth.containsKey('ssh') && ctx.system.auth.ssh.containsKey('dropped_ip')" - } - }, - { - "date": { - "if": "ctx.event.timezone == null", - "field": "system.auth.timestamp", - "target_field": "@timestamp", - "formats": [ - "MMM d HH:mm:ss", - "MMM dd HH:mm:ss", - "ISO8601" - ], - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, - { - "date": { - "if": "ctx.event.timezone != null", - "field": "system.auth.timestamp", - "target_field": "@timestamp", - "formats": [ - "MMM d HH:mm:ss", - "MMM dd HH:mm:ss", - "ISO8601" - ], - "timezone": "{{ event.timezone }}", - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, - { - "remove": { - "field": "system.auth.timestamp" - } - }, - { - "geoip": { - "field": "source.ip", - "target_field": "source.geo", - "ignore_failure": true - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "source.ip", - "target_field": "source.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.asn", - "target_field": "source.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.organization_name", - "target_field": "source.as.organization.name", - "ignore_missing": true - } - }, - { - "script": { - "lang": "painless", - "ignore_failure": true, - "source": "if (ctx.system.auth.ssh.event == \"Accepted\") { if (!ctx.containsKey(\"event\")) { ctx.event = [:]; } ctx.event.type = \"authentication_success\"; ctx.event.category = \"authentication\"; ctx.event.action = \"ssh_login\"; ctx.event.outcome = \"success\"; } else if (ctx.system.auth.ssh.event == \"Invalid\" || ctx.system.auth.ssh.event == \"Failed\") { if (!ctx.containsKey(\"event\")) { ctx.event = [:]; } ctx.event.type = \"authentication_failure\"; ctx.event.category = \"authentication\"; ctx.event.action = \"ssh_login\"; ctx.event.outcome = \"failure\"; }" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/filebeat/module/system/auth/ingest/pipeline.yml b/filebeat/module/system/auth/ingest/pipeline.yml new file mode 100644 index 00000000000..2cdd507f8cc --- /dev/null +++ b/filebeat/module/system/auth/ingest/pipeline.yml @@ -0,0 +1,145 @@ +description: Pipeline for parsing system authorisation/secure logs +processors: +- grok: + field: message + ignore_missing: true + pattern_definitions: + GREEDYMULTILINE: |- + (.| + )* + TIMESTAMP: (?:%{TIMESTAMP_ISO8601}|%{SYSLOGTIMESTAMP}) + patterns: + - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + %{DATA:system.auth.ssh.event} %{DATA:system.auth.ssh.method} for (invalid user + )?%{DATA:user.name} from %{IPORHOST:source.ip} port %{NUMBER:source.port:long} + ssh2(: %{GREEDYDATA:system.auth.ssh.signature})?' + - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + %{DATA:system.auth.ssh.event} user %{DATA:user.name} from %{IPORHOST:source.ip}' + - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + Did not receive identification string from %{IPORHOST:system.auth.ssh.dropped_ip}' + - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + \s*%{DATA:user.name} :( %{DATA:system.auth.sudo.error} ;)? TTY=%{DATA:system.auth.sudo.tty} + ; PWD=%{DATA:system.auth.sudo.pwd} ; USER=%{DATA:system.auth.sudo.user} ; COMMAND=%{GREEDYDATA:system.auth.sudo.command}' + - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + new group: name=%{DATA:group.name}, GID=%{NUMBER:group.id}' + - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + new user: name=%{DATA:user.name}, UID=%{NUMBER:user.id}, GID=%{NUMBER:group.id}, + home=%{DATA:system.auth.useradd.home}, shell=%{DATA:system.auth.useradd.shell}$' + - '%{TIMESTAMP:system.auth.timestamp} %{SYSLOGHOST:host.hostname}? %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + %{GREEDYMULTILINE:system.auth.message}' +- remove: + field: message +- rename: + field: system.auth.message + target_field: message + ignore_missing: true +- set: + field: source.ip + value: '{{system.auth.ssh.dropped_ip}}' + if: "ctx?.system?.auth?.ssh?.dropped_ip != null" +- date: + if: ctx.event.timezone == null + field: system.auth.timestamp + target_field: '@timestamp' + formats: + - MMM d HH:mm:ss + - MMM dd HH:mm:ss + - ISO8601 + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- date: + if: ctx.event.timezone != null + field: system.auth.timestamp + target_field: '@timestamp' + formats: + - MMM d HH:mm:ss + - MMM dd HH:mm:ss + - ISO8601 + timezone: '{{ event.timezone }}' + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- remove: + field: system.auth.timestamp +- geoip: + field: source.ip + target_field: source.geo + ignore_failure: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- set: + field: event.kind + value: event +- script: + lang: painless + ignore_failure: true + source: >- + if (ctx.system.auth.ssh.event == "Accepted") { + ctx.event.type = ["authentication_success", "info"]; + ctx.event.category = ["authentication"]; + ctx.event.action = "ssh_login"; + ctx.event.outcome = "success"; + } else if (ctx.system.auth.ssh.event == "Invalid" || ctx.system.auth.ssh.event == "Failed") { + ctx.event.type = ["authentication_failure", "info"]; + ctx.event.category = ["authentication"]; + ctx.event.action = "ssh_login"; + ctx.event.outcome = "failure"; + } + +- append: + field: event.category + value: iam + if: "ctx?.process?.name != null && ['groupadd', 'groupdel', 'groupmod', 'useradd', 'userdel', 'usermod'].contains(ctx.process.name)" +- set: + field: event.outcome + value: success + if: "ctx?.process?.name != null && ['groupadd', 'groupdel', 'groupmod', 'useradd', 'userdel', 'usermod'].contains(ctx.process.name)" +- append: + field: event.type + value: user + if: "ctx?.process?.name != null && ['useradd', 'userdel', 'usermod'].contains(ctx.process.name)" +- append: + field: event.type + value: group + if: "ctx?.process?.name != null && ['groupadd', 'groupdel', 'groupmod'].contains(ctx.process.name)" +- append: + field: event.type + value: creation + if: "ctx?.process?.name != null && ['useradd', 'groupadd'].contains(ctx.process.name)" +- append: + field: event.type + value: deletion + if: "ctx?.process?.name != null && ['userdel', 'groupdel'].contains(ctx.process.name)" +- append: + field: event.type + value: change + if: "ctx?.process?.name != null && ['usermod', 'groupmod'].contains(ctx.process.name)" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/system/auth/manifest.yml b/filebeat/module/system/auth/manifest.yml index ade9e03a69a..dd16ddafd65 100644 --- a/filebeat/module/system/auth/manifest.yml +++ b/filebeat/module/system/auth/manifest.yml @@ -11,5 +11,5 @@ var: - /var/log/secure.log* os.windows: [] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/auth.yml diff --git a/filebeat/module/system/auth/test/auth-ubuntu1204.log-expected.json b/filebeat/module/system/auth/test/auth-ubuntu1204.log-expected.json index a7a3cee04e6..74654cb6dc1 100644 --- a/filebeat/module/system/auth/test/auth-ubuntu1204.log-expected.json +++ b/filebeat/module/system/auth/test/auth-ubuntu1204.log-expected.json @@ -1,6 +1,7 @@ [ { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -14,6 +15,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -21,6 +23,9 @@ "input.type": "log", "log.offset": 81, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-lhspyyxxlfzpytwsebjoegenjxyjombo; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675177.72-26828938879074/get_url; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675177.72-26828938879074/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -30,6 +35,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -42,6 +48,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -54,6 +61,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -67,6 +75,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -74,6 +83,9 @@ "input.type": "log", "log.offset": 736, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-xspkubktopzqiwiofvdhqaglconkrgwp; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675181.24-158548606882799/get_url; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675181.24-158548606882799/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -83,6 +95,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -95,6 +108,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -107,6 +121,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -120,6 +135,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -127,6 +143,9 @@ "input.type": "log", "log.offset": 1393, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-vxcrqvczsrjrrsjcokculalhrgfsxqzl; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675202.4-199750250589919/command; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675202.4-199750250589919/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -136,6 +155,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -148,6 +168,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -160,6 +181,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -173,6 +195,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -180,6 +203,9 @@ "input.type": "log", "log.offset": 2048, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-gruorqbeefuuhfprfoqzsftalatgwwvf; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675203.3-59927285912173/file; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675203.3-59927285912173/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -189,6 +215,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -201,6 +228,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -213,6 +241,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -226,6 +255,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -233,6 +263,9 @@ "input.type": "log", "log.offset": 2698, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-fnthqelgspkbnpnxlsknzcbyxbqqxpmt; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675204.07-135388534337396/command; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675204.07-135388534337396/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -242,6 +275,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -254,6 +288,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -266,6 +301,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -279,6 +315,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -291,6 +328,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -298,6 +336,9 @@ "input.type": "log", "log.offset": 3414, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-wagdvfiuqxtryvmyrqlfcwoxeqqrxejt; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675206.28-198308747142204/async_wrapper 321853834469 45 /home/vagrant/.ansible/tmp/ansible-tmp-1486675206.28-198308747142204/command /home/vagrant/.ansible/tmp/ansible-tmp-1486675206.28-198308747142204/arguments; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675206.28-198308747142204/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -307,6 +348,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -319,6 +361,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -331,6 +374,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -344,6 +388,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -351,6 +396,9 @@ "input.type": "log", "log.offset": 4249, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-lkgydmrwiywdfvxfoxmgntufiumtzpmq; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675212.66-81790186240643/command; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675212.66-81790186240643/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -360,6 +408,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -372,6 +421,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -384,6 +434,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -397,6 +448,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -404,6 +456,9 @@ "input.type": "log", "log.offset": 4904, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-mjsapklbglujaoktlsyytirwygexdily; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675218.96-234174787135180/command; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675218.96-234174787135180/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -413,6 +468,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -425,6 +481,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -437,6 +494,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -450,6 +508,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -457,6 +516,9 @@ "input.type": "log", "log.offset": 5561, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-kvmafqtdnnvnyfyqlnoovickcavkqwdy; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675219.83-99205535237718/setup; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675219.83-99205535237718/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -466,6 +528,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -478,6 +541,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -490,6 +554,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -503,6 +568,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -510,6 +576,9 @@ "input.type": "log", "log.offset": 6214, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-nhrnwbdpypmsmvcstuihfqfbcvpxrmys; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675224.58-12467498973476/get_url; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675224.58-12467498973476/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -519,6 +588,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -531,6 +601,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -543,6 +614,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -556,6 +628,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -563,6 +636,9 @@ "input.type": "log", "log.offset": 6869, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-buzartmsbrirxgcoibjpsqjkldihhexh; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675228.25-195852789001210/get_url; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675228.25-195852789001210/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -572,6 +648,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -584,6 +661,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -596,6 +674,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -609,6 +688,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -616,6 +696,9 @@ "input.type": "log", "log.offset": 7526, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-swwkpvmnxhcuduxerfbgclhsmgbhwzie; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675247.78-128146395950020/command; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675247.78-128146395950020/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -625,6 +708,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -637,6 +721,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -649,6 +734,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -662,6 +748,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -669,6 +756,9 @@ "input.type": "log", "log.offset": 8183, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-raffykohamlcbnpxzipksbvfpjbfpagy; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675250.82-190689706060358/apt; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675250.82-190689706060358/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -678,6 +768,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -690,6 +781,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -702,6 +794,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -715,6 +808,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -722,6 +816,9 @@ "input.type": "log", "log.offset": 8836, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-dfoxiractbmtavfiwfnhzfkftipjumph; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675251.6-137767038423665/apt; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675251.6-137767038423665/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -731,6 +828,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -743,6 +841,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -755,6 +854,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -768,6 +868,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -775,6 +876,9 @@ "input.type": "log", "log.offset": 9487, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-jveaoynmhsmeodakzfhhaodihyroxobu; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675261.29-208287411335817/file; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675261.29-208287411335817/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -784,6 +888,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -796,6 +901,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -808,6 +914,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -815,6 +922,9 @@ "input.type": "log", "log.offset": 10060, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-lwzhcvorajmjyxsrqydafzapoeescwaf; rc=flag; [ -r /etc/metricbeat/metricbeat.yml ] || rc=2; [ -f /etc/metricbeat/metricbeat.yml ] || rc=1; [ -d /etc/metricbeat/metricbeat.yml ] && rc=3; python -V 2>/dev/null || rc=4; [ x\"$rc\" != \"xflag\" ] && echo \"${rc} \"/etc/metricbeat/metricbeat.yml && exit 0; (python -c 'import hashlib; BLOCKSIZE = 65536; hasher = hashlib.sha1();#012afile = open(\"'/etc/metricbeat/metricbeat.yml'\", \"rb\")#012buf = afile.read(BLOCKSIZE)#012while len(buf) > 0:#012#011hasher.update(buf)#012#011buf = afile.read(BLOCKSIZE)#012afile.close()#012print(hasher.hexdigest())' 2>/dev/null) || (python -c 'import sha; BLOCKSIZE = 65536; hasher = sha.sha();#012afile = open(\"'/etc/metricbeat/metricbeat.yml'\", \"rb\")#012buf = afile.read(BLOCKSIZE)#012while len(buf) > 0:#012#011hasher.update(buf)#012#011buf = afile.read(BLOCKSIZE)#012afile.close()#012print(hasher.hexdigest())' 2>/dev/null) || (echo '0 ", "system.auth.sudo.pwd": "/home/vagrant", @@ -824,6 +934,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -836,6 +947,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -848,6 +960,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -860,6 +973,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -873,6 +987,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -886,6 +1001,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -893,6 +1009,9 @@ "input.type": "log", "log.offset": 11548, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-yesyhegdrhiolusidthffdemrxphqdfm; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675262.15-83340738940485/copy; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675262.15-83340738940485/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -902,6 +1021,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -914,6 +1034,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -926,6 +1047,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -939,6 +1061,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -946,6 +1069,9 @@ "input.type": "log", "log.offset": 12200, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-vqbyiylfjufyxlwvxcwusklrtmiekpia; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675263.16-15325827909434/service; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675263.16-15325827909434/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -955,6 +1081,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -967,6 +1094,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -979,6 +1107,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -992,6 +1121,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -999,6 +1129,9 @@ "input.type": "log", "log.offset": 12855, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-osrbplljwskuafamtjuanhwfxqdxmfbj; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675264.47-179299683847940/wait_for; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675264.47-179299683847940/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -1008,6 +1141,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1020,6 +1154,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1032,6 +1167,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1045,6 +1181,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1052,6 +1189,9 @@ "input.type": "log", "log.offset": 13513, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-xqypdfdxashhaekghbfnpdlcgsmfarmy; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675265.39-273766954542007/service; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675265.39-273766954542007/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -1061,6 +1201,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1073,6 +1214,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1085,6 +1227,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1098,6 +1241,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1105,6 +1249,9 @@ "input.type": "log", "log.offset": 14170, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-ktkmpxhjivossxngupfgrqfobhopruzp; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675266.58-47565152594552/apt; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675266.58-47565152594552/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -1114,6 +1261,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1126,6 +1274,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1138,6 +1287,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1151,6 +1301,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1158,6 +1309,9 @@ "input.type": "log", "log.offset": 14821, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-erpqyqrmifxazcclvbqytjwxgdplhtpy; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675275.74-155140815824587/file; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675275.74-155140815824587/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -1167,6 +1321,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1179,6 +1334,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1191,6 +1347,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1204,6 +1361,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1211,6 +1369,9 @@ "input.type": "log", "log.offset": 15475, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-cfqjebskszjdqpksprlbjpbttastwzyp; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675276.62-248748589735433/get_url; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675276.62-248748589735433/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -1220,6 +1381,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1232,6 +1394,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1244,6 +1407,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1257,6 +1421,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1264,6 +1429,9 @@ "input.type": "log", "log.offset": 16132, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-oxbowrzvfhsebemuiblilqwvdxvnwztv; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675280.28-272460786101534/get_url; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675280.28-272460786101534/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", @@ -1273,6 +1441,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1285,6 +1454,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1297,6 +1467,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1310,6 +1481,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1317,6 +1489,9 @@ "input.type": "log", "log.offset": 16789, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/sh -c echo BECOME-SUCCESS-ohlhhhazvtawqawluadjlxglowwenmyc; LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python /home/vagrant/.ansible/tmp/ansible-tmp-1486675302.51-201837201796085/command; rm -rf /home/vagrant/.ansible/tmp/ansible-tmp-1486675302.51-201837201796085/ >/dev/null 2>&1", "system.auth.sudo.pwd": "/home/vagrant", diff --git a/filebeat/module/system/auth/test/secure-rhel7.log-expected.json b/filebeat/module/system/auth/test/secure-rhel7.log-expected.json index 331294ad81d..5242ff398d9 100644 --- a/filebeat/module/system/auth/test/secure-rhel7.log-expected.json +++ b/filebeat/module/system/auth/test/secure-rhel7.log-expected.json @@ -1,18 +1,30 @@ [ { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 0, "process.name": "sshd", "process.pid": 2738, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -30,6 +42,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -43,18 +56,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 209, "process.name": "sshd", "process.pid": 2738, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -72,6 +97,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -85,18 +111,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 418, "process.name": "sshd", "process.pid": 2738, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -114,6 +152,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -127,6 +166,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -140,6 +180,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -153,6 +194,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -166,6 +208,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -179,18 +222,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 1105, "process.name": "sshd", "process.pid": 2742, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -208,6 +263,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -221,18 +277,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 1314, "process.name": "sshd", "process.pid": 2742, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -250,6 +318,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -263,18 +332,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 1523, "process.name": "sshd", "process.pid": 2742, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -292,6 +373,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -305,18 +387,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 1732, "process.name": "sshd", "process.pid": 2742, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -334,6 +428,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -347,18 +442,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 1941, "process.name": "sshd", "process.pid": 2742, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -376,6 +483,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -389,6 +497,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -402,6 +511,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -415,6 +525,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -428,6 +539,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -441,6 +553,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -454,6 +567,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -467,18 +581,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 2889, "process.name": "sshd", "process.pid": 2754, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -496,6 +622,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -509,18 +636,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 3098, "process.name": "sshd", "process.pid": 2758, + "related.ip": [ + "116.31.116.27" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 134764, "source.as.organization.name": "CHINANET Guangdong province network", @@ -538,6 +677,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -551,18 +691,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 3306, "process.name": "sshd", "process.pid": 2754, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -580,6 +732,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -593,18 +746,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 3515, "process.name": "sshd", "process.pid": 2758, + "related.ip": [ + "116.31.116.27" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 134764, "source.as.organization.name": "CHINANET Guangdong province network", @@ -622,6 +787,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -635,18 +801,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 3723, "process.name": "sshd", "process.pid": 2754, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -664,6 +842,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -677,18 +856,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 3932, "process.name": "sshd", "process.pid": 2758, + "related.ip": [ + "116.31.116.27" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 134764, "source.as.organization.name": "CHINANET Guangdong province network", @@ -706,6 +897,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -719,6 +911,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -732,18 +925,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 4259, "process.name": "sshd", "process.pid": 2754, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -761,6 +966,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -774,18 +980,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 4468, "process.name": "sshd", "process.pid": 2754, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -803,6 +1021,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -816,6 +1035,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -829,6 +1049,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -842,6 +1063,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -855,6 +1077,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -868,18 +1091,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 5155, "process.name": "sshd", "process.pid": 2762, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -897,6 +1132,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -910,18 +1146,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 5364, "process.name": "sshd", "process.pid": 2762, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -939,6 +1187,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -952,18 +1201,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 5573, "process.name": "sshd", "process.pid": 2762, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -981,6 +1242,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -994,18 +1256,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 5782, "process.name": "sshd", "process.pid": 2762, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1023,6 +1297,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1036,18 +1311,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 5991, "process.name": "sshd", "process.pid": 2762, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1065,6 +1352,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1078,6 +1366,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1091,6 +1380,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1104,6 +1394,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1117,6 +1408,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1130,18 +1422,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 6678, "process.name": "sshd", "process.pid": 2766, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1159,6 +1463,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1172,18 +1477,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 6887, "process.name": "sshd", "process.pid": 2766, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1201,6 +1518,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1214,18 +1532,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 7096, "process.name": "sshd", "process.pid": 2766, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1243,6 +1573,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1256,18 +1587,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 7305, "process.name": "sshd", "process.pid": 2766, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1285,6 +1628,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1298,18 +1642,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 7514, "process.name": "sshd", "process.pid": 2766, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1327,6 +1683,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1340,6 +1697,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1353,6 +1711,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1366,6 +1725,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1379,6 +1739,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1392,18 +1753,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 8199, "process.name": "sshd", "process.pid": 2778, + "related.ip": [ + "116.31.116.27" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 134764, "source.as.organization.name": "CHINANET Guangdong province network", @@ -1421,6 +1794,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1434,18 +1808,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 8407, "process.name": "sshd", "process.pid": 2778, + "related.ip": [ + "116.31.116.27" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 134764, "source.as.organization.name": "CHINANET Guangdong province network", @@ -1463,6 +1849,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1476,18 +1863,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 8615, "process.name": "sshd", "process.pid": 2778, + "related.ip": [ + "116.31.116.27" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 134764, "source.as.organization.name": "CHINANET Guangdong province network", @@ -1505,6 +1904,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1518,6 +1918,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1531,6 +1932,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1544,6 +1946,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1557,18 +1960,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 9205, "process.name": "sshd", "process.pid": 2785, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1586,6 +2001,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1599,18 +2015,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 9414, "process.name": "sshd", "process.pid": 2785, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1628,6 +2056,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1641,18 +2070,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 9623, "process.name": "sshd", "process.pid": 2785, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1670,6 +2111,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1683,18 +2125,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 9832, "process.name": "sshd", "process.pid": 2785, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1712,6 +2166,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1725,18 +2180,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 10041, "process.name": "sshd", "process.pid": 2785, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1754,6 +2221,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1767,6 +2235,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1780,6 +2249,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1793,6 +2263,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1806,6 +2277,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -1819,18 +2291,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 10728, "process.name": "sshd", "process.pid": 2797, + "related.ip": [ + "202.109.143.106" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 4134, "source.as.organization.name": "No.31,Jin-rong Street", @@ -1848,6 +2332,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", diff --git a/filebeat/module/system/auth/test/test.log-expected.json b/filebeat/module/system/auth/test/test.log-expected.json index 5a2cf8fa0a2..0203b1a1f3b 100644 --- a/filebeat/module/system/auth/test/test.log-expected.json +++ b/filebeat/module/system/auth/test/test.log-expected.json @@ -1,18 +1,30 @@ [ { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "success", "event.timezone": "-02:00", - "event.type": "authentication_success", + "event.type": [ + "authentication_success", + "info" + ], "fileset.name": "auth", "host.hostname": "localhost", "input.type": "log", "log.offset": 0, "process.name": "sshd", "process.pid": 3402, + "related.ip": [ + "10.0.2.2" + ], + "related.user": [ + "vagrant" + ], "service.type": "system", "source.ip": "10.0.2.2", "source.port": 63673, @@ -23,18 +35,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "success", "event.timezone": "-02:00", - "event.type": "authentication_success", + "event.type": [ + "authentication_success", + "info" + ], "fileset.name": "auth", "host.hostname": "localhost", "input.type": "log", "log.offset": 152, "process.name": "sshd", "process.pid": 7483, + "related.ip": [ + "192.168.33.1" + ], + "related.user": [ + "vagrant" + ], "service.type": "system", "source.ip": "192.168.33.1", "source.port": 58803, @@ -44,18 +68,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "localhost", "input.type": "log", "log.offset": 254, "process.name": "sshd", "process.pid": 3430, + "related.ip": [ + "10.0.2.2" + ], + "related.user": [ + "test" + ], "service.type": "system", "source.ip": "10.0.2.2", "system.auth.ssh.event": "Invalid", @@ -63,18 +99,30 @@ }, { "event.action": "ssh_login", - "event.category": "authentication", + "event.category": [ + "authentication" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.outcome": "failure", "event.timezone": "-02:00", - "event.type": "authentication_failure", + "event.type": [ + "authentication_failure", + "info" + ], "fileset.name": "auth", "host.hostname": "slave22", "input.type": "log", "log.offset": 324, "process.name": "sshd", "process.pid": 5774, + "related.ip": [ + "116.31.116.24" + ], + "related.user": [ + "root" + ], "service.type": "system", "source.as.number": 134764, "source.as.organization.name": "CHINANET Guangdong province network", @@ -92,6 +140,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -99,6 +148,9 @@ "input.type": "log", "log.offset": 420, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/ls", "system.auth.sudo.pwd": "/home/vagrant", @@ -108,6 +160,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -116,6 +169,9 @@ "log.offset": 522, "process.name": "sshd", "process.pid": 18406, + "related.ip": [ + "123.57.245.163" + ], "service.type": "system", "source.as.number": 37963, "source.as.organization.name": "Hangzhou Alibaba Advertising Co.,Ltd.", @@ -131,6 +187,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -138,6 +195,9 @@ "input.type": "log", "log.offset": 617, "process.name": "sudo", + "related.user": [ + "vagrant" + ], "service.type": "system", "system.auth.sudo.command": "/bin/cat /var/log/secure", "system.auth.sudo.pwd": "/home/vagrant", @@ -147,6 +207,7 @@ }, { "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -154,6 +215,9 @@ "input.type": "log", "log.offset": 736, "process.name": "sudo", + "related.user": [ + "tsg" + ], "service.type": "system", "system.auth.sudo.command": "/bin/ls", "system.auth.sudo.error": "user NOT in sudoers", @@ -163,9 +227,18 @@ "user.name": "tsg" }, { + "event.category": [ + "iam" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "group", + "creation" + ], "fileset.name": "auth", "group.id": "48", "group.name": "apache", @@ -177,9 +250,18 @@ "service.type": "system" }, { + "event.category": [ + "iam" + ], "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", + "event.outcome": "success", "event.timezone": "-02:00", + "event.type": [ + "user", + "creation" + ], "fileset.name": "auth", "group.id": "48", "host.hostname": "localhost", @@ -187,6 +269,9 @@ "log.offset": 934, "process.name": "useradd", "process.pid": 6995, + "related.user": [ + "apache" + ], "service.type": "system", "system.auth.useradd.home": "/usr/share/httpd", "system.auth.useradd.shell": "/sbin/nologin", diff --git a/filebeat/module/system/auth/test/timestamp.log-expected.json b/filebeat/module/system/auth/test/timestamp.log-expected.json index 80c07d4e9a8..8903b63e89e 100644 --- a/filebeat/module/system/auth/test/timestamp.log-expected.json +++ b/filebeat/module/system/auth/test/timestamp.log-expected.json @@ -2,6 +2,7 @@ { "@timestamp": "2019-06-14T10:40:20.912-02:00", "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", @@ -16,6 +17,7 @@ { "@timestamp": "2019-06-14T09:31:15.412-02:00", "event.dataset": "system.auth", + "event.kind": "event", "event.module": "system", "event.timezone": "-02:00", "fileset.name": "auth", diff --git a/filebeat/module/system/syslog/ingest/pipeline.json b/filebeat/module/system/syslog/ingest/pipeline.json deleted file mode 100644 index 0c614b8a957..00000000000 --- a/filebeat/module/system/syslog/ingest/pipeline.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "description": "Pipeline for parsing Syslog messages.", - "processors": [ - { - "grok": { - "field": "message", - "patterns": [ - "%{SYSLOGTIMESTAMP:system.syslog.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: %{GREEDYMULTILINE:system.syslog.message}", - "%{SYSLOGTIMESTAMP:system.syslog.timestamp} %{GREEDYMULTILINE:system.syslog.message}", - "%{TIMESTAMP_ISO8601:system.syslog.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\\[%{POSINT:process.pid:long}\\])?: %{GREEDYMULTILINE:system.syslog.message}" - ], - "pattern_definitions" : { - "GREEDYMULTILINE" : "(.|\n)*" - }, - "ignore_missing": true - } - }, - { - "remove": { - "field": "message" - } - }, - { - "rename": { - "field": "system.syslog.message", - "target_field": "message", - "ignore_missing": true - } - }, - { - "date": { - "if": "ctx.event.timezone == null", - "field": "system.syslog.timestamp", - "target_field": "@timestamp", - "formats": [ - "MMM d HH:mm:ss", - "MMM dd HH:mm:ss", - "MMM d HH:mm:ss", - "ISO8601" - ], - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, - { - "date": { - "if": "ctx.event.timezone != null", - "field": "system.syslog.timestamp", - "target_field": "@timestamp", - "formats": [ - "MMM d HH:mm:ss", - "MMM dd HH:mm:ss", - "MMM d HH:mm:ss", - "ISO8601" - ], - "timezone": "{{ event.timezone }}", - "on_failure": [{"append": {"field": "error.message", "value": "{{ _ingest.on_failure_message }}"}}] - } - }, - { - "remove": { - "field": "system.syslog.timestamp" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/filebeat/module/system/syslog/ingest/pipeline.yml b/filebeat/module/system/syslog/ingest/pipeline.yml new file mode 100644 index 00000000000..e0c80b9aad6 --- /dev/null +++ b/filebeat/module/system/syslog/ingest/pipeline.yml @@ -0,0 +1,57 @@ +description: Pipeline for parsing Syslog messages. +processors: +- grok: + field: message + patterns: + - '%{SYSLOGTIMESTAMP:system.syslog.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + %{GREEDYMULTILINE:system.syslog.message}' + - '%{SYSLOGTIMESTAMP:system.syslog.timestamp} %{GREEDYMULTILINE:system.syslog.message}' + - '%{TIMESTAMP_ISO8601:system.syslog.timestamp} %{SYSLOGHOST:host.hostname} %{DATA:process.name}(?:\[%{POSINT:process.pid:long}\])?: + %{GREEDYMULTILINE:system.syslog.message}' + pattern_definitions: + GREEDYMULTILINE: |- + (.| + )* + ignore_missing: true +- remove: + field: message +- rename: + field: system.syslog.message + target_field: message + ignore_missing: true +- date: + if: ctx.event.timezone == null + field: system.syslog.timestamp + target_field: '@timestamp' + formats: + - MMM d HH:mm:ss + - MMM dd HH:mm:ss + - MMM d HH:mm:ss + - ISO8601 + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- date: + if: ctx.event.timezone != null + field: system.syslog.timestamp + target_field: '@timestamp' + formats: + - MMM d HH:mm:ss + - MMM dd HH:mm:ss + - MMM d HH:mm:ss + - ISO8601 + timezone: '{{ event.timezone }}' + on_failure: + - append: + field: error.message + value: '{{ _ingest.on_failure_message }}' +- remove: + field: system.syslog.timestamp +- set: + field: event.type + value: event +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/filebeat/module/system/syslog/manifest.yml b/filebeat/module/system/syslog/manifest.yml index fa0ec049135..39a34e56ca3 100644 --- a/filebeat/module/system/syslog/manifest.yml +++ b/filebeat/module/system/syslog/manifest.yml @@ -9,5 +9,5 @@ var: - /var/log/system.log* os.windows: [] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/syslog.yml diff --git a/filebeat/module/system/syslog/test/darwin-syslog-sample.log-expected.json b/filebeat/module/system/syslog/test/darwin-syslog-sample.log-expected.json index 5b1165078bc..5a164aef94f 100644 --- a/filebeat/module/system/syslog/test/darwin-syslog-sample.log-expected.json +++ b/filebeat/module/system/syslog/test/darwin-syslog-sample.log-expected.json @@ -3,6 +3,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -19,6 +20,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -32,6 +34,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "input.type": "log", "log.offset": 1176, diff --git a/filebeat/module/system/syslog/test/darwin-syslog.log-expected.json b/filebeat/module/system/syslog/test/darwin-syslog.log-expected.json index fc057403a39..45d44816cd1 100644 --- a/filebeat/module/system/syslog/test/darwin-syslog.log-expected.json +++ b/filebeat/module/system/syslog/test/darwin-syslog.log-expected.json @@ -3,6 +3,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -16,6 +17,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -32,6 +34,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -45,6 +48,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -58,6 +62,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -74,6 +79,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -87,6 +93,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -100,6 +107,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -116,6 +124,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -129,6 +138,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -142,6 +152,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -155,6 +166,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -168,6 +180,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -181,6 +194,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -194,6 +208,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -207,6 +222,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -220,6 +236,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -233,6 +250,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -246,6 +264,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -259,6 +278,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -272,6 +292,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -285,6 +306,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -298,6 +320,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -314,6 +337,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -327,6 +351,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -343,6 +368,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -356,6 +382,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -369,6 +396,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -382,6 +410,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -395,6 +424,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -408,6 +438,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -421,6 +452,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -434,6 +466,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -450,6 +483,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -463,6 +497,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -479,6 +514,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -492,6 +528,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -505,6 +542,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -518,6 +556,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -531,6 +570,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -543,6 +583,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -556,6 +597,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -569,6 +611,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -582,6 +625,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -594,6 +638,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -606,6 +651,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -619,6 +665,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -631,6 +678,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -644,6 +692,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -657,6 +706,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -669,6 +719,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -682,6 +733,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -695,6 +747,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -708,6 +761,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -720,6 +774,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -733,6 +788,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -745,6 +801,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -758,6 +815,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -770,6 +828,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -783,6 +842,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -796,6 +856,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -809,6 +870,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -821,6 +883,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -834,6 +897,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -846,6 +910,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -859,6 +924,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -872,6 +938,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -885,6 +952,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -897,6 +965,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -910,6 +979,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -923,6 +993,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -935,6 +1006,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -948,6 +1020,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -961,6 +1034,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -973,6 +1047,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -986,6 +1061,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -999,6 +1075,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1011,6 +1088,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1024,6 +1102,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1036,6 +1115,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1049,6 +1129,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1061,6 +1142,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1074,6 +1156,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1087,6 +1170,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1100,6 +1184,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1113,6 +1198,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1125,6 +1211,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1138,6 +1225,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1151,6 +1239,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1163,6 +1252,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1176,6 +1266,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1189,6 +1280,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1201,6 +1293,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1214,6 +1307,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1226,6 +1320,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1239,6 +1334,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1252,6 +1348,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1265,6 +1362,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1277,6 +1375,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", @@ -1290,6 +1389,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "a-mac-with-esc-key", "input.type": "log", diff --git a/filebeat/module/system/syslog/test/suse-syslog.log-expected.json b/filebeat/module/system/syslog/test/suse-syslog.log-expected.json index 0230189feaf..f517557a26e 100644 --- a/filebeat/module/system/syslog/test/suse-syslog.log-expected.json +++ b/filebeat/module/system/syslog/test/suse-syslog.log-expected.json @@ -3,6 +3,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "linux-sqrz", "input.type": "log", @@ -16,6 +17,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "linux-sqrz", "input.type": "log", diff --git a/filebeat/module/system/syslog/test/tz-offset.log-expected.json b/filebeat/module/system/syslog/test/tz-offset.log-expected.json index 154e256b2ba..f2e167a1fd7 100644 --- a/filebeat/module/system/syslog/test/tz-offset.log-expected.json +++ b/filebeat/module/system/syslog/test/tz-offset.log-expected.json @@ -4,6 +4,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "rmbkmonitor04", "input.type": "log", @@ -19,6 +20,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "rmbkmonitor04", "input.type": "log", @@ -33,6 +35,7 @@ "event.dataset": "system.syslog", "event.module": "system", "event.timezone": "-02:00", + "event.type": "event", "fileset.name": "syslog", "host.hostname": "localhost", "input.type": "log", diff --git a/filebeat/scripts/mage/config.go b/filebeat/scripts/mage/config.go index 7585c62b634..0893fbc0743 100644 --- a/filebeat/scripts/mage/config.go +++ b/filebeat/scripts/mage/config.go @@ -18,38 +18,25 @@ package mage import ( + "github.com/magefile/mage/mg" + devtools "github.com/elastic/beats/v7/dev-tools/mage" ) -const modulesConfigYml = "build/config.modules.yml" +const modulesConfigYml = "build/config.modules.yml.tmpl" func configFileParams(moduleDirs ...string) devtools.ConfigFileParams { collectModuleConfig := func() error { return devtools.GenerateModuleReferenceConfig(modulesConfigYml, moduleDirs...) } + mg.Deps(collectModuleConfig) - return devtools.ConfigFileParams{ - ShortParts: []string{ - devtools.OSSBeatDir("_meta/common.p1.yml"), - devtools.OSSBeatDir("_meta/common.p2.yml"), - devtools.LibbeatDir("_meta/config.yml.tmpl"), - }, - ReferenceDeps: []interface{}{collectModuleConfig}, - ReferenceParts: []string{ - devtools.OSSBeatDir("_meta/common.reference.p1.yml"), - modulesConfigYml, - devtools.OSSBeatDir("_meta/common.reference.inputs.yml"), - devtools.OSSBeatDir("_meta/common.reference.p2.yml"), - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - }, - DockerParts: []string{ - devtools.OSSBeatDir("_meta/beat.docker.yml"), - devtools.LibbeatDir("_meta/config.docker.yml"), - }, - ExtraVars: map[string]interface{}{ - "UseKubernetesMetadataProcessor": true, - }, + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, devtools.OSSBeatDir("_meta/config/*.tmpl"), modulesConfigYml) + p.ExtraVars = map[string]interface{}{ + "UseKubernetesMetadataProcessor": true, } + return p } // OSSConfigFileParams returns the default ConfigFileParams for generating @@ -62,13 +49,6 @@ func OSSConfigFileParams(moduleDirs ...string) devtools.ConfigFileParams { // filebeat*.yml files. func XPackConfigFileParams() devtools.ConfigFileParams { args := configFileParams(devtools.OSSBeatDir("module"), "module") - args.ReferenceParts = []string{ - devtools.OSSBeatDir("_meta/common.reference.p1.yml"), - modulesConfigYml, - devtools.OSSBeatDir("_meta/common.reference.inputs.yml"), - "_meta/common.reference.inputs.yml", // Added only to X-Pack. - devtools.OSSBeatDir("_meta/common.reference.p2.yml"), - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - } + args.Templates = append(args.Templates, "_meta/config/*.tmpl") return args } diff --git a/filebeat/scripts/tester/main.go b/filebeat/scripts/tester/main.go index 2ae9de44388..6da063e6204 100644 --- a/filebeat/scripts/tester/main.go +++ b/filebeat/scripts/tester/main.go @@ -108,6 +108,11 @@ func main() { } for _, path := range paths { + // TODO: Add support for testing YAML pipelines. + if filepath.Ext(path) == ".yml" { + fmt.Fprintf(os.Stderr, "YAML pipelines are not supported by this tool. Cannot process %q.", path) + os.Exit(3) + } err = testPipeline(*esURL, path, logs, *verbose, *simulateVerbose) if err != nil { os.Stderr.WriteString(err.Error()) @@ -185,8 +190,14 @@ func getPipelinePath(path, modulesPath string) ([]string, error) { module := parts[0] fileset := parts[1] - pathToPipeline := filepath.Join(modulesPath, module, fileset, "ingest", "pipeline.json") - _, err := os.Stat(pathToPipeline) + var pathToPipeline string + for _, ext := range []string{".json", ".yml"} { + pathToPipeline = filepath.Join(modulesPath, module, fileset, "ingest", "pipeline"+ext) + _, err = os.Stat(pathToPipeline) + if err == nil { + break + } + } if err != nil { return nil, fmt.Errorf("Cannot find pipeline in %s: %v %v\n", path, err, pathToPipeline) } @@ -199,8 +210,7 @@ func getPipelinePath(path, modulesPath string) ([]string, error) { return nil, err } for _, f := range files { - isPipelineFile := strings.HasSuffix(f.Name(), ".json") - if isPipelineFile { + if isPipelineFileExtension(f.Name()) { fullPath := filepath.Join(path, f.Name()) paths = append(paths, fullPath) } @@ -211,8 +221,7 @@ func getPipelinePath(path, modulesPath string) ([]string, error) { return paths, nil } - isPipelineFile := strings.HasSuffix(path, ".json") - if isPipelineFile { + if isPipelineFileExtension(path) { return []string{path}, nil } @@ -220,6 +229,15 @@ func getPipelinePath(path, modulesPath string) ([]string, error) { } +func isPipelineFileExtension(path string) bool { + ext := filepath.Ext(path) + switch strings.ToLower(ext) { + case ".yml", ".json": + return true + } + return false +} + func testPipeline(esURL, path string, logs []string, verbose, simulateVerbose bool) error { pipeline, err := readPipeline(path) if err != nil { diff --git a/filebeat/scripts/tester/main_test.go b/filebeat/scripts/tester/main_test.go index 79f53186a1b..6284f6f2e5e 100644 --- a/filebeat/scripts/tester/main_test.go +++ b/filebeat/scripts/tester/main_test.go @@ -29,7 +29,7 @@ func TestGetPipelinePath(t *testing.T) { count int }{ { - pipelinePath: "../../module/postgresql/log/ingest/pipeline.json", + pipelinePath: "../../module/postgresql/log/ingest/pipeline.yml", count: 1, }, { diff --git a/filebeat/tests/system/config/filebeat.yml.j2 b/filebeat/tests/system/config/filebeat.yml.j2 index 0c48b34ebc2..2275d761bca 100644 --- a/filebeat/tests/system/config/filebeat.yml.j2 +++ b/filebeat/tests/system/config/filebeat.yml.j2 @@ -52,6 +52,11 @@ filebeat.{{input_config | default("inputs")}}: {{k}}: {{v}} {% endfor %} {% endif %} + {%- if publisher_pipeline %} + {%- for name, value in publisher_pipeline.items() %} + publisher_pipeline.{{name}}: {{value | tojson}} + {%- endfor %} + {% endif %} fields_under_root: {{"true" if fieldsUnderRoot else "false"}} diff --git a/filebeat/tests/system/test_fields.py b/filebeat/tests/system/test_fields.py index be1e76fb52a..2e05ca8a6db 100644 --- a/filebeat/tests/system/test_fields.py +++ b/filebeat/tests/system/test_fields.py @@ -63,7 +63,7 @@ def test_custom_fields_under_root(self): def test_beat_fields(self): """ Checks that it's possible to set a custom shipper name. Also - tests that beat.hostname has values. + tests that agent.hostname has values. """ self.render_config_template( path=os.path.abspath(self.working_dir) + "/test.log", @@ -80,5 +80,6 @@ def test_beat_fields(self): output = self.read_output() doc = output[0] assert doc["host.name"] == "testShipperName" + assert doc["agent.name"] == "testShipperName" assert doc["agent.hostname"] == socket.gethostname() assert "fields" not in doc diff --git a/filebeat/tests/system/test_input.py b/filebeat/tests/system/test_input.py index 0075329205b..684f4f852af 100644 --- a/filebeat/tests/system/test_input.py +++ b/filebeat/tests/system/test_input.py @@ -662,3 +662,23 @@ def test_disable_recursive_glob(self): "recursive glob disabled"), max_timeout=10) filebeat.check_kill_and_wait() + + def test_input_processing_pipeline_disable_host(self): + """ + Check processing_pipeline.disable_host in input config. + """ + self.render_config_template( + path=os.path.abspath(self.working_dir) + "/test.log", + publisher_pipeline={ + "disable_host": True, + }, + ) + with open(self.working_dir + "/test.log", "w") as f: + f.write("test message\n") + + filebeat = self.start_beat() + self.wait_until(lambda: self.output_has(lines=1)) + filebeat.check_kill_and_wait() + + output = self.read_output() + assert "host.name" not in output[0] diff --git a/filebeat/tests/system/test_modules.py b/filebeat/tests/system/test_modules.py index 37e5e63537a..23b517c0391 100644 --- a/filebeat/tests/system/test_modules.py +++ b/filebeat/tests/system/test_modules.py @@ -214,7 +214,7 @@ def _test_expected_events(self, test_file, objects): def clean_keys(obj): # These keys are host dependent - host_keys = ["host.name", "agent.hostname", "agent.type", "agent.ephemeral_id", "agent.id"] + host_keys = ["host.name", "agent.name", "agent.hostname", "agent.type", "agent.ephemeral_id", "agent.id"] # The create timestamps area always new time_keys = ["event.created"] # source path and agent.version can be different for each run diff --git a/filebeat/tests/system/test_registrar.py b/filebeat/tests/system/test_registrar.py index 449f22da937..ed2e04d2c07 100644 --- a/filebeat/tests/system/test_registrar.py +++ b/filebeat/tests/system/test_registrar.py @@ -819,6 +819,7 @@ def test_clean_inactive(self): # Make sure the last file in the registry is the correct one and has the correct offset assert data[0]["offset"] == self.input_logs.size(file3) + @unittest.skipIf(os.name == 'nt', 'flaky test https://github.com/elastic/beats/issues/7690') def test_clean_removed(self): """ Checks that files which were removed, the state is removed @@ -868,6 +869,7 @@ def test_clean_removed(self): # Make sure the last file in the registry is the correct one and has the correct offset assert data[0]["offset"] == self.input_logs.size(file2) + @unittest.skipIf(os.name == 'nt', 'flaky test https://github.com/elastic/beats/issues/10606') def test_clean_removed_with_clean_inactive(self): """ Checks that files which were removed, the state is removed diff --git a/generator/_templates/beat/{beat}/_meta/beat.docker.yml b/generator/_templates/beat/{beat}/_meta/config/beat.docker.yml.tmpl similarity index 100% rename from generator/_templates/beat/{beat}/_meta/beat.docker.yml rename to generator/_templates/beat/{beat}/_meta/config/beat.docker.yml.tmpl diff --git a/generator/_templates/beat/{beat}/_meta/beat.reference.yml b/generator/_templates/beat/{beat}/_meta/config/beat.reference.yml.tmpl similarity index 100% rename from generator/_templates/beat/{beat}/_meta/beat.reference.yml rename to generator/_templates/beat/{beat}/_meta/config/beat.reference.yml.tmpl diff --git a/generator/_templates/beat/{beat}/_meta/beat.yml b/generator/_templates/beat/{beat}/_meta/config/beat.yml.tmpl similarity index 100% rename from generator/_templates/beat/{beat}/_meta/beat.yml rename to generator/_templates/beat/{beat}/_meta/config/beat.yml.tmpl diff --git a/generator/_templates/beat/{beat}/magefile.go b/generator/_templates/beat/{beat}/magefile.go index d924f3c7946..28638df0f0a 100644 --- a/generator/_templates/beat/{beat}/magefile.go +++ b/generator/_templates/beat/{beat}/magefile.go @@ -56,7 +56,9 @@ func Fields() error { // Config generates both the short/reference/docker configs. func Config() error { - return devtools.Config(devtools.AllConfigTypes, devtools.ConfigFileParams{}, ".") + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, "_meta/config/*.tmpl") + return devtools.Config(devtools.AllConfigTypes, p, ".") } // Clean cleans all generated files and build artifacts. diff --git a/generator/_templates/metricbeat/{beat}/_meta/docker.yml b/generator/_templates/metricbeat/{beat}/_meta/config/beat.docker.yml.tmpl similarity index 100% rename from generator/_templates/metricbeat/{beat}/_meta/docker.yml rename to generator/_templates/metricbeat/{beat}/_meta/config/beat.docker.yml.tmpl diff --git a/generator/_templates/metricbeat/{beat}/_meta/reference.yml b/generator/_templates/metricbeat/{beat}/_meta/config/beat.reference.yml.tmpl similarity index 100% rename from generator/_templates/metricbeat/{beat}/_meta/reference.yml rename to generator/_templates/metricbeat/{beat}/_meta/config/beat.reference.yml.tmpl diff --git a/generator/_templates/metricbeat/{beat}/_meta/short.yml b/generator/_templates/metricbeat/{beat}/_meta/config/beat.yml.tmpl similarity index 100% rename from generator/_templates/metricbeat/{beat}/_meta/short.yml rename to generator/_templates/metricbeat/{beat}/_meta/config/beat.yml.tmpl diff --git a/generator/_templates/metricbeat/{beat}/magefile.go b/generator/_templates/metricbeat/{beat}/magefile.go index 22b3dcfcc76..8805fc36a32 100644 --- a/generator/_templates/metricbeat/{beat}/magefile.go +++ b/generator/_templates/metricbeat/{beat}/magefile.go @@ -14,9 +14,11 @@ import ( "github.com/elastic/beats/v7/dev-tools/mage/target/common" "github.com/elastic/beats/v7/dev-tools/mage/target/pkg" "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" - "github.com/elastic/beats/v7/dev-tools/mage/target/update" "github.com/elastic/beats/v7/generator/common/beatgen" metricbeat "github.com/elastic/beats/v7/metricbeat/scripts/mage" + + // mage:import + _ "github.com/elastic/beats/v7/metricbeat/scripts/mage/target/metricset" ) func init() { @@ -44,8 +46,9 @@ func Package() { defer func() { fmt.Println("package ran for", time.Since(start)) }() devtools.UseCommunityBeatPackaging() + devtools.PackageKibanaDashboardsFromBuildDir() - mg.Deps(update.Update) + mg.Deps(Update) mg.Deps(build.CrossBuild, build.CrossBuildGoDaemon) mg.SerialDeps(devtools.Package, pkg.PackageTest) } @@ -74,13 +77,9 @@ func Config() { } func configYML() error { - customDeps := devtools.ConfigFileParams{ - ShortParts: []string{"_meta/short.yml", devtools.LibbeatDir("_meta/config.yml.tmpl")}, - ReferenceParts: []string{"_meta/reference.yml", devtools.LibbeatDir("_meta/config.reference.yml.tmpl")}, - DockerParts: []string{"_meta/docker.yml", devtools.LibbeatDir("_meta/config.docker.yml")}, - ExtraVars: map[string]interface{}{"BeatName": devtools.BeatName}, - } - return devtools.Config(devtools.AllConfigTypes, customDeps, ".") + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, "_meta/config/*.tmpl") + return devtools.Config(devtools.AllConfigTypes, p, ".") } // Clean cleans all generated files and build artifacts. @@ -99,9 +98,14 @@ func Fmt() { common.Fmt() } -// Update updates the generated files (aka make update). -func Update() error { - return update.Update() +// Update is an alias for running fields, dashboards, config. +func Update() { + mg.SerialDeps(Fields, Dashboards, Config, Imports) +} + +// Dashboards collects all the dashboards and generates index patterns. +func Dashboards() error { + return devtools.KibanaDashboards("module") } // Imports generates an include/list.go file containing diff --git a/generator/common/Makefile b/generator/common/Makefile index 0a7b3608dae..8d82a444e84 100644 --- a/generator/common/Makefile +++ b/generator/common/Makefile @@ -5,7 +5,7 @@ BEAT_PATH=${GOPATH}/src/${BEAT_NAME} ES_BEATS=${GOPATH}/src/github.com/elastic/beats PREPARE_COMMAND?= --include ${ES_BEATS}/dev-tools/make/mage.mk +-include ${ES_BEATS}/dev-tools/make/mage-install.mk # Runs test build for mock beat .PHONY: test @@ -16,7 +16,7 @@ test: prepare-test git config user.name "beats-jenkins" || exit 1 ; \ $(MAKE) check CHECK_HEADERS_DISABLED=y || exit 1 ; \ $(MAKE) || exit 1 ; \ - $(MAKE) unit + mage test .PHONY: test-package test-package: test diff --git a/go.mod b/go.mod index 92e086c40ad..60769774140 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/coreos/go-systemd/v22 v22.0.0 github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 // indirect - github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f + github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 // indirect github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible // indirect github.com/digitalocean/go-libvirt v0.0.0-20180301200012-6075ea3c39a1 @@ -143,6 +143,9 @@ require ( github.com/vmware/govmomi v0.0.0-20170802214208-2cad15190b41 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c github.com/yuin/gopher-lua v0.0.0-20170403160031-b402f3114ec7 // indirect + go.elastic.co/apm v1.7.2 + go.elastic.co/apm/module/apmelasticsearch v1.7.2 + go.elastic.co/apm/module/apmhttp v1.7.2 go.uber.org/atomic v1.3.1 go.uber.org/multierr v1.1.1-0.20170829224307-fb7d312c2c04 go.uber.org/zap v1.7.1 @@ -159,7 +162,7 @@ require ( google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb google.golang.org/grpc v1.27.1 gopkg.in/inf.v0 v0.9.0 - gopkg.in/jcmturner/gokrb5.v7 v7.3.0 // indirect + gopkg.in/jcmturner/gokrb5.v7 v7.3.0 gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528 gopkg.in/yaml.v2 v2.2.8 howett.net/plist v0.0.0-20181124034731-591f970eefbb diff --git a/go.sum b/go.sum index 1d13f1a6c16..a7cdb11f48a 100644 --- a/go.sum +++ b/go.sum @@ -113,6 +113,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antlr/antlr4 v0.0.0-20200225173536-225249fdaef5 h1:nkZ9axP+MvUFCu8JRN/MCY+DmTfs6lY7hE0QnJbxSdI= github.com/antlr/antlr4 v0.0.0-20200225173536-225249fdaef5/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-lambda-go v1.6.0 h1:T+u/g79zPKw1oJM7xYhvpq7i4Sjc0iVsXZUaqRVVSOg= @@ -173,14 +175,16 @@ github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+ github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= +github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 h1:qg9VbHo1TlL0KDM0vYvBG9EY0X0Yku5WYIPoFWt8f6o= github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE= -github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f h1:WH0w/R4Yoey+04HhFxqZ6VX6I0d7RMyw5aXQ9UTvQPs= -github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= +github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e h1:LzwWXEScfcTu7vUZNlDDWDARoSGEtvlDKK2BYHowNeE= +github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 h1:6+hM8KeYKV0Z9EIINNqIEDyyIRAcNc2FW+/TUYNmWyw= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= @@ -204,6 +208,7 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dop251/goja_nodejs v0.0.0-20171011081505-adff31b136e6 h1:RrkoB0pT3gnjXhL/t10BSP1mcr/0Ldea2uMyuBr2SWk= github.com/dop251/goja_nodejs v0.0.0-20171011081505-adff31b136e6/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= @@ -239,6 +244,7 @@ github.com/elastic/go-seccomp-bpf v1.1.0 h1:jUzzDc6LyCtdolZdvL/26dad6rZ9vsc7xZ2e github.com/elastic/go-seccomp-bpf v1.1.0/go.mod h1:l+89Vy5BzjVcaX8USZRMOwmwwDScE+vxCFzzvQwN7T8= github.com/elastic/go-structform v0.0.6 h1:wqeK4LwD2NNDOoRGTImE24S6pkCDVr8+oUSIkmChzLk= github.com/elastic/go-structform v0.0.6/go.mod h1:QrMyP3oM9Sjk92EVGLgRaL2lKt0Qx7ZNDRWDxB6khVs= +github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= github.com/elastic/go-sysinfo v1.3.0 h1:eb2XFGTMlSwG/yyU9Y8jVAYLIzU2sFzWXwo2gmetyrE= github.com/elastic/go-sysinfo v1.3.0/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= github.com/elastic/go-txfile v0.0.7 h1:Yn28gclW7X0Qy09nSMSsx0uOAvAGMsp6XHydbiLVe2s= @@ -254,12 +260,14 @@ github.com/elastic/gosigar v0.10.5 h1:GzPQ+78RaAb4J63unidA/JavQRKrB6s8IOzN6Ib59j github.com/elastic/gosigar v0.10.5/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= github.com/elastic/sarama v0.0.0-20191122160421-355d120d0970 h1:rSo6gsz4zOanqtJ5fmZYQJvEJnA5YsVOB25casIwqUw= github.com/elastic/sarama v0.0.0-20191122160421-355d120d0970/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.5.0 h1:vBh+kQp8lg9XPr56u1CPrWjFXtdphMoGWVHr9/1c+A0= github.com/fatih/color v1.5.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -315,6 +323,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8= @@ -595,6 +605,8 @@ github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b/go.mod h1:8458kAa github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522/go.mod h1:tQTYKOQgxoH3v6dEmdHiz4JG+nbxWwM5fgPQUpSZqVQ= github.com/sanathkr/yaml v1.0.1-0.20170819201035-0056894fa522 h1:39BJIaZIhIBmXATIhdlTBlTQpAiGXHnz17CrO7vF2Ss= github.com/sanathkr/yaml v1.0.1-0.20170819201035-0056894fa522/go.mod h1:tQTYKOQgxoH3v6dEmdHiz4JG+nbxWwM5fgPQUpSZqVQ= +github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= +github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v2.19.11+incompatible h1:lJHR0foqAjI4exXqWsU3DbH7bX1xvdhGdnXTIARA9W4= github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -668,6 +680,14 @@ github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56 h1:yhqBHs09Sm github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/yuin/gopher-lua v0.0.0-20170403160031-b402f3114ec7 h1:0gYLpmzecnaDCoeWxSfEJ7J1b6B/67+NV++4HKQXx+Y= github.com/yuin/gopher-lua v0.0.0-20170403160031-b402f3114ec7/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= +go.elastic.co/apm v1.7.2 h1:0nwzVIPp4PDBXSYYtN19+1W5V+sj+C25UjqxDVoKcA8= +go.elastic.co/apm v1.7.2/go.mod h1:tCw6CkOJgkWnzEthFN9HUP1uL3Gjc/Ur6m7gRPLaoH0= +go.elastic.co/apm/module/apmelasticsearch v1.7.2 h1:5STGHLZLSeAzxordMc+dFVKiyVtMmxADOV+TgRaXXJg= +go.elastic.co/apm/module/apmelasticsearch v1.7.2/go.mod h1:ZyNFuyWdt42GBZkz0SogoLzDBrBGj4orxpiUuxYeYq8= +go.elastic.co/apm/module/apmhttp v1.7.2 h1:2mRh7SwBuEVLmJlX+hsMdcSg9xaielCLElaPn/+i34w= +go.elastic.co/apm/module/apmhttp v1.7.2/go.mod h1:sTFWiWejnhSdZv6+dMgxGec2Nxe/ZKfHfz/xtRM+cRY= +go.elastic.co/fastjson v1.0.0 h1:ooXV/ABvf+tBul26jcVViPT3sBir0PvXgibYB1IQQzg= +go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -684,6 +704,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -924,7 +945,9 @@ k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.4-0.20190719014911-6a023d6d0e09 h1:w2hB+DoJsxpuO4hxMXfs44k1riAXX5kaV40564cWMUc= k8s.io/klog v0.3.4-0.20190719014911-6a023d6d0e09/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058 h1:di3XCwddOR9cWBNpfgXaskhh6cgJuwcK54rvtwUaC10= k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= +k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= k8s.io/utils v0.0.0-20190712204705-3dccf664f023 h1:1H4Jyzb0z2X0GfBMTwRjnt5ejffRHrGftUgJcV/ZfDc= diff --git a/heartbeat/_meta/beat.docker.yml b/heartbeat/_meta/config/beat.docker.yml.tmpl similarity index 99% rename from heartbeat/_meta/beat.docker.yml rename to heartbeat/_meta/config/beat.docker.yml.tmpl index f845c4a47a1..2d5c7b43afd 100644 --- a/heartbeat/_meta/beat.docker.yml +++ b/heartbeat/_meta/config/beat.docker.yml.tmpl @@ -21,4 +21,3 @@ heartbeat.monitors: hosts: - elasticsearch - kibana - diff --git a/heartbeat/_meta/beat.reference.yml b/heartbeat/_meta/config/beat.reference.yml.tmpl similarity index 99% rename from heartbeat/_meta/beat.reference.yml rename to heartbeat/_meta/config/beat.reference.yml.tmpl index e84bd9e99c4..efc94b31ed9 100644 --- a/heartbeat/_meta/beat.reference.yml +++ b/heartbeat/_meta/config/beat.reference.yml.tmpl @@ -266,5 +266,5 @@ heartbeat.scheduler: # disabled if set to 0. The default is 0. #limit: 0 - # Set the scheduler it's timezone + # Set the scheduler it's time zone #location: '' diff --git a/heartbeat/_meta/beat.yml b/heartbeat/_meta/config/beat.yml.tmpl similarity index 94% rename from heartbeat/_meta/beat.yml rename to heartbeat/_meta/config/beat.yml.tmpl index 5459f28f989..04c9b71f7a1 100644 --- a/heartbeat/_meta/beat.yml +++ b/heartbeat/_meta/config/beat.yml.tmpl @@ -33,7 +33,7 @@ heartbeat.monitors: # Total test connection and data exchange timeout #timeout: 16s -#==================== Elasticsearch template setting ========================== +{{header "Elasticsearch template setting"}} setup.template.settings: index.number_of_shards: 1 diff --git a/heartbeat/_meta/fields.common.yml b/heartbeat/_meta/fields.common.yml index 28a721494f9..8db94d8a56c 100644 --- a/heartbeat/_meta/fields.common.yml +++ b/heartbeat/_meta/fields.common.yml @@ -17,11 +17,19 @@ type: keyword description: > The monitors configured name + multi_fields: + - name: text + type: text + analyzer: simple - name: id type: keyword description: > The monitors full job ID as used by heartbeat. + multi_fields: + - name: text + type: text + analyzer: simple - name: duration type: group diff --git a/heartbeat/autodiscover/builder/hints/monitors.go b/heartbeat/autodiscover/builder/hints/monitors.go index ba89d81b456..836b5a9326c 100644 --- a/heartbeat/autodiscover/builder/hints/monitors.go +++ b/heartbeat/autodiscover/builder/hints/monitors.go @@ -91,7 +91,7 @@ func (hb *heartbeatHints) CreateConfig(event bus.Event) []*common.Config { } hb.logger.Debugf("generated config %+v", configs) // Apply information in event to the template to generate the final config - return template.ApplyConfigTemplate(event, configs) + return template.ApplyConfigTemplate(event, configs, false) } tempCfg := common.MapStr{} @@ -121,7 +121,7 @@ func (hb *heartbeatHints) CreateConfig(event bus.Event) []*common.Config { } // Apply information in event to the template to generate the final config - return template.ApplyConfigTemplate(event, configs) + return template.ApplyConfigTemplate(event, configs, false) } func (hb *heartbeatHints) getType(hints common.MapStr) common.MapStr { diff --git a/heartbeat/beater/heartbeat.go b/heartbeat/beater/heartbeat.go index dbdfe1dd740..1ad682fc496 100644 --- a/heartbeat/beater/heartbeat.go +++ b/heartbeat/beater/heartbeat.go @@ -156,7 +156,18 @@ func (bt *Heartbeat) RunReloadableMonitors(b *beat.Beat) (err error) { // makeAutodiscover creates an autodiscover object ready to be started. func (bt *Heartbeat) makeAutodiscover(b *beat.Beat) (*autodiscover.Autodiscover, error) { - return autodiscover.NewAutodiscover("heartbeat", b.Publisher, bt.dynamicFactory, autodiscover.QueryConfig(), bt.config.Autodiscover) + autodiscover, err := autodiscover.NewAutodiscover( + "heartbeat", + b.Publisher, + bt.dynamicFactory, + autodiscover.QueryConfig(), + bt.config.Autodiscover, + b.Keystore, + ) + if err != nil { + return nil, err + } + return autodiscover, nil } // Stop stops the beat. diff --git a/heartbeat/cmd/root.go b/heartbeat/cmd/root.go index 8e24b28976b..a2b253a535e 100644 --- a/heartbeat/cmd/root.go +++ b/heartbeat/cmd/root.go @@ -41,7 +41,7 @@ var RootCmd *cmd.BeatsRootCmd func init() { settings := instance.Settings{ Name: Name, - Processing: processing.MakeDefaultSupport(true, processing.WithECS, processing.WithBeatMeta("agent")), + Processing: processing.MakeDefaultSupport(true, processing.WithECS, processing.WithAgentMeta()), HasDashboards: false, } RootCmd = cmd.GenRootCmdWithSettings(beater.New, settings) diff --git a/heartbeat/docs/configuring-howto.asciidoc b/heartbeat/docs/configuring-howto.asciidoc index 7f2b6547d7e..525fc15baf3 100644 --- a/heartbeat/docs/configuring-howto.asciidoc +++ b/heartbeat/docs/configuring-howto.asciidoc @@ -26,6 +26,7 @@ _Beats Platform Reference_ for more about the structure of the config file. The following topics describe how to configure Heartbeat: * <> +* <> * <> * <> * <> @@ -44,6 +45,8 @@ The following topics describe how to configure Heartbeat: include::./heartbeat-options.asciidoc[] +include::./heartbeat-scheduler.asciidoc[] + include::./heartbeat-general-options.asciidoc[] include::{libbeat-dir}/shared-path-config.asciidoc[] diff --git a/heartbeat/docs/fields.asciidoc b/heartbeat/docs/fields.asciidoc index b288eec1788..1ce19dc9375 100644 --- a/heartbeat/docs/fields.asciidoc +++ b/heartbeat/docs/fields.asciidoc @@ -40,7 +40,8 @@ Contains common beat fields available in all event types. *`agent.hostname`*:: + -- -Hostname of the agent. +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. + type: keyword @@ -215,6 +216,13 @@ type: keyword -- +*`monitor.name.text`*:: ++ +-- +type: text + +-- + *`monitor.id`*:: + -- @@ -225,6 +233,13 @@ type: keyword -- +*`monitor.id.text`*:: ++ +-- +type: text + +-- + [float] === duration @@ -7824,7 +7839,10 @@ TLS layer related fields. *`tls.certificate_not_valid_before`*:: + -- -Earliest time at which the connection's certificates are valid. + +deprecated:[7.8.0] + +Deprecated in favor of `tls.server.x509.not_before`. Earliest time at which the connection's certificates are valid. type: date @@ -7833,7 +7851,10 @@ type: date *`tls.certificate_not_valid_after`*:: + -- -Latest time at which the connection's certificates are valid. + +deprecated:[7.8.0] + +Deprecated in favor of `tls.server.x509.not_after`. Latest time at which the connection's certificates are valid. type: date @@ -7862,3 +7883,180 @@ type: long -- +[float] +=== server + +Detailed x509 certificate metadata + + + +*`tls.server.x509.alternative_names`*:: ++ +-- +List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses. + +type: keyword + +example: *.elastic.co + +-- + + +*`tls.server.x509.issuer.common_name`*:: ++ +-- +List of common name (CN) of issuing certificate authority. + +type: keyword + +example: DigiCert SHA2 High Assurance Server CA + +-- + +*`tls.server.x509.issuer.common_name.text`*:: ++ +-- +type: text + +-- + +*`tls.server.x509.issuer.distinguished_name`*:: ++ +-- +Distinguished name (DN) of issuing certificate authority. + +type: keyword + +example: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA + +-- + +*`tls.server.x509.not_after`*:: ++ +-- +Time at which the certificate is no longer considered valid. + +type: date + +example: 2020-07-16 03:15:39 + +-- + +*`tls.server.x509.not_before`*:: ++ +-- +Time at which the certificate is first considered valid. + +type: date + +example: 2019-08-16 01:40:25 + +-- + +*`tls.server.x509.public_key_algorithm`*:: ++ +-- +Algorithm used to generate the public key. + +type: keyword + +example: RSA + +-- + +*`tls.server.x509.public_key_curve`*:: ++ +-- +The curve used by the elliptic curve public key algorithm. This is algorithm specific. + +type: keyword + +example: nistp521 + +-- + +*`tls.server.x509.public_key_exponent`*:: ++ +-- +Exponent used to derive the public key. This is algorithm specific. + +type: long + +example: 65537 + +-- + +*`tls.server.x509.public_key_size`*:: ++ +-- +The size of the public key space in bits. + +type: long + +example: 2048 + +-- + +*`tls.server.x509.serial_number`*:: ++ +-- +Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters. + +type: keyword + +example: 55FBB9C7DEBF09809D12CCAA + +-- + +*`tls.server.x509.signature_algorithm`*:: ++ +-- +Identifier for certificate signature algorithm. Recommend using names found in Go Lang Crypto library (See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353). + +type: keyword + +example: SHA256-RSA + +-- + + +*`tls.server.x509.subject.common_name`*:: ++ +-- +List of common names (CN) of subject. + +type: keyword + +example: r2.shared.global.fastly.net + +-- + +*`tls.server.x509.subject.common_name.text`*:: ++ +-- +type: text + +-- + +*`tls.server.x509.subject.distinguished_name`*:: ++ +-- +Distinguished name (DN) of the certificate subject entity. + +type: keyword + +example: C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net + +-- + +*`tls.server.x509.version_number`*:: ++ +-- +Version of x509 format. + +type: keyword + +example: 3 + +-- + diff --git a/heartbeat/docs/heartbeat-options.asciidoc b/heartbeat/docs/heartbeat-options.asciidoc index 4e166610a53..23f33c26a3d 100644 --- a/heartbeat/docs/heartbeat-options.asciidoc +++ b/heartbeat/docs/heartbeat-options.asciidoc @@ -81,661 +81,26 @@ monitor definitions only, e.g. what is normally under the `heartbeat.monitors` s ---------------------------------------------------------------------- [float] -[[monitor-options]] -=== Monitor options +[[monitor-types]] +=== Monitor types -You can specify the following options when defining a {beatname_uc} monitor in any location. -These options are the same for all monitors. Each monitor type has additional configuration -options that are specific to that monitor type. +You can configure {beatname_uc} to use the following monitor types: -[float] -[[monitor-type]] -==== `type` - -The type of monitor to run. One of: - -* `icmp`: Uses an ICMP (v4 and v6) Echo Request to ping the configured hosts. -Requires special permissions or root access. See <>. -* `tcp`: Connects via TCP and optionally verifies the endpoint by sending and/or -receiving a custom payload. See <>. -* `http`: Connects via HTTP and optionally verifies that the host returns the -expected response. See <>. Will use `Elastic-Heartbeat` as the user agent product. +*<>*:: Uses an ICMP (v4 and v6) Echo Request to ping the configured hosts. +Requires special permissions or root access. +*<>*:: Connects via TCP and optionally verifies the endpoint by sending and/or +receiving a custom payload. +*<>*:: Connects via HTTP and optionally verifies that the host returns the +expected response. Will use `Elastic-Heartbeat` as +the user agent product. The `tcp` and `http` monitor types both support SSL/TLS and some proxy settings. -[float] -[[monitor-id]] -==== `id` - -A unique identifier for this configuration. This should not change with edits to the monitor configuration -regardless of changes to any config fields. Examples: `uploader-service`, `http://example.net`, `us-west-loadbalancer`. Note that this uniqueness is only within a given beat instance. If you want to monitor the same endpoint from multiple locations it is recommended that those heartbeat instances use the same IDs so that their results can be correlated. You can use the `host.geo.name` property to disambiguate them. - -When querying against indexed monitor data this is the field you will be aggregating with. Appears in the -<> as `monitor.id`. - -If you do not set this explicitly the monitor's config will be hashed and a generated value used. This value will -change with any options change to this monitor making aggregations over time between changes impossible. For this reason -it is recommended that you set this manually. - -[float] -[[monitor-name]] -==== `name` - -Optional human readable name for this monitor. This value appears in the <> -as `monitor.name`. - -[float] -[[monitor-enabled]] -==== `enabled` - -A Boolean value that specifies whether the module is enabled. If the `enabled` -option is missing from the configuration block, the module is enabled by -default. - -[float] -[[monitor-schedule]] -==== `schedule` - -A cron-like expression that specifies the task schedule. For example: - -* `*/5 * * * * * *` runs the task every 5 seconds (for example, at 10:00:00, -10:00:05, and so on). -* `@every 5s` runs the task every 5 seconds from the time when {beatname_uc} was -started. - -The `schedule` option uses a cron-like syntax based on https://github.com/gorhill/cronexpr#implementation[this `cronexpr` implementation], -but adds the `@every` keyword. - -For stats on the execution of scheduled tasks you can enable the HTTP stats server with `http.enabled: true` in heartbeat.yml, then run `curl http://localhost:5066/stats | jq .heartbeat.scheduler` to view the scheduler's stats. Stats are provided for both jobs and tasks. Each time a monitor is scheduled is considered to be a single job, while portions of the work a job does, like DNS lookups and executing network requests are defined as tasks. The stats provided are: - -* **jobs.active:** The number of actively running jobs/monitors. -* **jobs.missed_deadline:** The number of jobs that executed after their scheduled time. This can be caused either by overlong long timeouts from the previous job or high load preventing heartbeat from keeping up with work. -* **tasks.active:** The number of tasks currently running. -* **tasks.waiting:** If the global `schedule.limit` option is set, this number will reflect the number of tasks that are ready to execute, but have not been started in order to prevent exceeding `schedule.limit`. - -[float] -[[monitor-ipv4]] -==== `ipv4` - -A Boolean value that specifies whether to ping using the ipv4 protocol if -hostnames are configured. The default is `true`. - -[float] -[[monitor-ipv6]] -==== `ipv6` - -A Boolean value that specifies whether to ping using the ipv6 protocol -if hostnames are configured. The default is `true`. - -[float] -[[monitor-mode]] -==== `mode` - -If `mode` is `any`, the monitor pings only one IP address for a hostname. If -`mode` is `all`, the monitor pings all resolvable IPs for a hostname. The -`mode: all` setting is useful if you are using a DNS-load balancer and want to -ping every IP address for the specified hostname. The default is `any`. - -[float] -[[monitor-timeout]] -==== `timeout` - -The total running time for each ping test. This is the total time allowed for -testing the connection and exchanging data. The default is 16 seconds (16s). - -If the timeout is exceeded, {beatname_uc} publishes a `service-down` event. If the -value specified for `timeout` is greater than `schedule`, intermediate checks -will not be executed by the scheduler. - -[float] -[[monitor-fields]] -==== `fields` - -Optional fields that you can specify to add additional information to the -output. For example, you might add fields that you can use for filtering log -data. Fields can be scalar values, arrays, dictionaries, or any nested -combination of these. By default, the fields that you specify here will be -grouped under a `fields` sub-dictionary in the output document. To store the -custom fields as top-level fields, set the `fields_under_root` option to true. -If a duplicate field is declared in the general configuration, then its value -will be overwritten by the value declared here. - -[float] -[[monitor-fields-under-root]] -==== `fields_under_root` - -If this option is set to true, the custom <> -are stored as top-level fields in the output document instead of being grouped -under a `fields` sub-dictionary. If the custom field names conflict with other -field names added by {beatname_uc}, then the custom fields overwrite the other -fields. - -[float] -[[monitor-tags]] -==== `tags` - -A list of tags that will be sent with the monitor event. This setting is optional. - -[float] -[[monitor-processors]] -==== `processors` - -A list of processors to apply to the data generated by the monitor. - -See <> for information about specifying -processors in your config. - -[float] -[[monitor-keep-null]] -==== `keep_null` - -If this option is set to true, fields with `null` values will be published in -the output document. By default, `keep_null` is set to `false`. - -[float] -[[monitor-icmp-options]] -=== ICMP options - -These options configure {beatname_uc} to use ICMP (v4 and v6) Echo Requests to check -the configured hosts. These options are valid when the <> is -`icmp`. Please note that on most platforms you must execute Heartbeat with elevated permissions -to perform ICMP pings. - -On Linux, regular users may perform pings if the right file capabilities are set. Run -`sudo setcap cap_net_raw+eip /path/to/heartbeat` to grant {beatname_uc} ping capabilities on Linux. -Alternatively, one may grant ping permissions to the user {beatname_uc} runs as. To grant ping permissions -in this way, run `sudo sysctl -w net.ipv4.ping_group_range='myuserid myuserid'`. - -Other platforms may require {beatname_uc} to run as root or administrator to execute pings. - -[float] -[[monitor-icmp-hosts]] -==== `hosts` - -A list of hosts to ping. - -[float] -[[monitor-icmp-wait]] -==== `wait` - -The duration to wait before emitting another ICMP Echo Request. The default is 1 -second (1s). - -[float] -[[monitor-tcp-options]] -=== TCP options - -These options configure {beatname_uc} to connect via TCP and optionally verify the -endpoint by sending and/or receiving a custom payload. These options are valid when -the <> is `tcp`. - -[float] -[[monitor-tcp-hosts]] -==== `hosts` - -A list of hosts to ping. The entries in the list can be: - -* A plain host name, such as `localhost`, or an IP address. If you specify this -option, you must also specify a value for <>. If the -monitor is <>, {beatname_uc} establishes an -SSL/TLS-based connection. Otherwise, it establishes a plain TCP connection. -* A hostname and port, such as `localhost:12345`. {beatname_uc} connects -to the port on the specified host. If the monitor is -<>, {beatname_uc} establishes an -SSL/TLS-based connection. Otherwise, it establishes a TCP connection. -* A full URL using the syntax `scheme://:[port]`, where: -** `scheme` is one of `tcp`, `plain`, `ssl` or `tls`. If `tcp` or `plain` is -specified, {beatname_uc} establishes a TCP connection even if the monitor is -configured to use SSL. If `tls` or `ssl` is specified, {beatname_uc} establishes -an SSL connection. However, if the monitor is not configured to use SSL, the -system defaults are used (currently not supported on Windows). -** `host` is the hostname. -** `port` is the port number. If `port` is missing in the URL, the -<> setting is required. - -[float] -[[monitor-tcp-ports]] -==== `ports` - -A list of ports to ping if the host specified in <> -does not contain a port number. It is generally preferable to use a single value here, -since each port will be monitored using a separate `id`, with the given `id` value, -used as a prefix in the Heartbeat data, and the configured `name` shared across events -sent via this check. - -Example configuration: - -[source,yaml] -------------------------------------------------------------------------------- -- type: tcp - id: my-host-services - name: My Host Services - hosts: ["myhost"] - ports: [80, 9200, 5044] - schedule: '@every 5s' -------------------------------------------------------------------------------- - -[float] -[[monitor-tcp-check]] -==== `check` - -An optional payload string to send to the remote host and the expected answer. -If no payload is specified, the endpoint is assumed to be available if the -connection attempt was successful. If `send` is specified without `receive`, -any response is accepted as OK. If `receive` is specified without `send`, no -payload is sent, but the client expects to receive a payload in the form of a -"hello message" or "banner" on connect. - -Example configuration: - -[source,yaml] -------------------------------------------------------------------------------- -- type: tcp - id: echo-service - name: Echo Service - hosts: ["myhost"] - ports: [7] - check.send: 'Hello World' - check.receive: 'Hello World' - schedule: '@every 5s' -------------------------------------------------------------------------------- - - -[float] -[[monitor-tcp-proxy-url]] -==== `proxy_url` - -The URL of the SOCKS5 proxy to use when connecting to the server. The value -must be a URL with a scheme of socks5://. - -If the SOCKS5 proxy server requires client authentication, then a username and -password can be embedded in the URL as shown in the example. - -[source,yaml] -------------------------------------------------------------------------------- - proxy_url: socks5://user:password@socks5-proxy:2233 -------------------------------------------------------------------------------- - -When using a proxy, hostnames are resolved on the proxy server instead of on -the client. You can change this behavior by setting the -`proxy_use_local_resolver` option. - -[float] -[[monitor-tcp-proxy-use-local-resolver]] -==== `proxy_use_local_resolver` - -A Boolean value that determines whether hostnames are resolved locally instead -of being resolved on the proxy server. The default value is false, which means -that name resolution occurs on the proxy server. - -[float] -[[monitor-tcp-tls-ssl]] -==== `ssl` - -The TLS/SSL connection settings. If the monitor is -<>, it will attempt an SSL -handshake. If `check` is not configured, the monitor will only check to see if -it can establish an SSL/TLS connection. This check can fail either at TCP level -or during certificate validation. - -Example configuration: - -[source,yaml] -------------------------------------------------------------------------------- -- type: tcp - id: tls-mail - name: TLS Mail - hosts: ["mail.example.net"] - ports: [465] - schedule: '@every 5s' - ssl: - certificate_authorities: ['/etc/ca.crt'] - supported_protocols: ["TLSv1.0", "TLSv1.1", "TLSv1.2"] -------------------------------------------------------------------------------- - - -Also see <> for a full description of the `ssl` options. - -[float] -[[monitor-http-options]] -=== HTTP options - -These options configure {beatname_uc} to connect via HTTP and optionally verify that -the host returns the expected response. These options are valid when the -<> is `http`. - -[float] -[[monitor-http-urls]] -==== `hosts` - -A list of URLs to ping. - -Example configuration: - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: myhost - name: My HTTP Host - schedule: '@every 5s' - hosts: ["http://myhost:80"] -------------------------------------------------------------------------------- - -[float] -[[monitor-http-max-redirects]] -==== `max_redirects` - -The total number of redirections Heartbeat will follow. Defaults to 0, meaning heartbeat will not follow redirects, -but will report the status of the redirect. If set to a number greater than 0 heartbeat will follow that number of redirects. - -When this option is set to a value greater than zero the `monitor.ip` field will no longer be reported, as multiple -DNS requests across multiple IPs may return multiple IPs. Fine grained network timing data will also not be recorded, as with redirects -that data will span multiple requests. Specifically the fields `http.rtt.content.us`, `http.rtt.response_header.us`, -`http.rtt.total.us`, `http.rtt.validate.us`, `http.rtt.write_request.us` and `dns.rtt.us` will be omitted. - -[float] -[[monitor-http-proxy-url]] -==== `proxy_url` - -The HTTP proxy URL. This setting is optional. Example `http://proxy.mydomain.com:3128` - -[float] -[[monitor-http-username]] -==== `username` - -The username for authenticating with the server. The credentials are passed -with the request. This setting is optional. - -You need to specify credentials when your `check.response` settings require it. -For example, you can check for a 403 response (`check.response.status: [403]`) -without setting credentials. - -[float] -[[monitor-http-password]] -==== `password` - -The password for authenticating with the server. This setting is optional. - -[float] -[[monitor-http-tls-ssl]] -==== `ssl` - -The TLS/SSL connection settings for use with the HTTPS endpoint. If you don't -specify settings, the system defaults are used. - - -Example configuration: - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: my-http-service - name: My HTTP Service - hosts: ["https://myhost:443"] - schedule: '@every 5s' - ssl: - certificate_authorities: ['/etc/ca.crt'] - supported_protocols: ["TLSv1.0", "TLSv1.1", "TLSv1.2"] -------------------------------------------------------------------------------- - -Also see <> for a full description of the `ssl` options. - -[float] -[[monitor-http-response]] -=== `response` - -Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field. - -Set `response.include_body` to one of the options listed below. - -*`on_error`*:: Include the body if an error is encountered during the check. This is the default. -*`never`*:: Never include the body. -*`always`*:: Always include the body with checks. - -Set `response.include_body_max_bytes` to control the maximum size of the stored body contents. Defaults to 1024 bytes. - -[float] -[[monitor-http-check]] -==== `check` - -An optional `request` to send to the remote host and the expected `response`. - -Example configuration: - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: my-http-host - name: My HTTP Service - hosts: ["http://myhost:80"] - check.request.method: HEAD - check.response.status: [200] - schedule: '@every 5s' -------------------------------------------------------------------------------- - - -Under `check.request`, specify these options: - -*`method`*:: The HTTP method to use. Valid values are `"HEAD"`, `"GET"` and -`"POST"`. -*`headers`*:: A dictionary of additional HTTP headers to send. By default heartbeat -will set the 'User-Agent' header to identify itself. -*`body`*:: Optional request body content. - -Example configuration: -This monitor POSTs an `x-www-form-urlencoded` string -to the endpoint `/demo/add` - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: demo-service - name: Demo Service - schedule: '@every 5s' - urls: ["http://localhost:8080/demo/add"] - check.request: - method: POST - headers: - 'Content-Type': 'application/x-www-form-urlencoded' - # urlencode the body: - body: "name=first&email=someemail%40someemailprovider.com" - check.response: - status: [200] - body: - - Saved - - saved -------------------------------------------------------------------------------- - -Under `check.response`, specify these options: - -*`status`*:: A list of expected status codes. 4xx and 5xx codes are considered `down` by default. Other codes are considered `up`. -*`headers`*:: The required response headers. -*`body`*:: A list of regular expressions to match the the body output. Only a single expression needs to match. HTTP response -bodies of up to 100MiB are supported. - -Example configuration: -This monitor examines the -response body for the strings `saved` or `Saved` and expects 200 or 201 status codes - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: demo-service - name: Demo Service - schedule: '@every 5s' - urls: ["http://localhost:8080/demo/add"] - check.request: - method: POST - headers: - 'Content-Type': 'application/x-www-form-urlencoded' - # urlencode the body: - body: "name=first&email=someemail%40someemailprovider.com" - check.response: - status: [200, 201] - body: - - Saved - - saved -------------------------------------------------------------------------------- - -*`json`*:: A list of <> expressions executed against the body when parsed as JSON. Body sizes -must be less than or equal to 100 MiB. - -The following configuration shows how to check the response when the body -contains JSON: - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: demo-service - name: Demo Service - schedule: '@every 5s' - hosts: ["https://myhost:80"] - check.request: - method: GET - headers: - 'X-API-Key': '12345-mykey-67890' - check.response: - status: [200] - json: - - description: check status - condition: - equals: - status: ok -------------------------------------------------------------------------------- - -The following configuration shows how to check the response for multiple regex -patterns: - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: demo-service - name: Demo Service - schedule: '@every 5s' - hosts: ["https://myhost:80"] - check.request: - method: GET - headers: - 'X-API-Key': '12345-mykey-67890' - check.response: - status: [200] - body: - - hello - - world -------------------------------------------------------------------------------- - -The following configuration shows how to check the response with a multiline -regex: - -[source,yaml] -------------------------------------------------------------------------------- -- type: http - id: demo-service - name: Demo Service - schedule: '@every 5s' - hosts: ["https://myhost:80"] - check.request: - method: GET - headers: - 'X-API-Key': '12345-mykey-67890' - check.response: - status: [200] - body: '(?s)first.*second.*third' -------------------------------------------------------------------------------- - - -[float] -[[monitors-scheduler]] -=== Scheduler options - -You specify options under `heartbeat.scheduler` to control the behavior of the task -scheduler. - -Example configuration: - -[source,yaml] -------------------------------------------------------------------------------- -heartbeat.scheduler: - limit: 10 - location: 'UTC-08:00' -------------------------------------------------------------------------------- - -In the example, setting `limit` to 10 guarantees that only 10 concurrent -I/O tasks will be active. An I/O task can be the actual check or resolving an -address via DNS. - -[float] -[[heartbeat-scheduler-limit]] -==== `limit` - -The number of concurrent I/O tasks that {beatname_uc} is allowed to execute. If set -to 0, there is no limit. The default is 0. - -Most operating systems set a file descriptor limit of 1024. For {beatname_uc} to -operate correctly and not accidentally block libbeat output, the value that you -specify for `limit` should be below the configured ulimit. - - -[float] -[[heartbeat-scheduler-location]] -==== `location` - -The timezone for the scheduler. By default the scheduler uses localtime. - -[float] -[[monitor-watch-poll-file]] -==== `watch.poll_file` - -deprecated:[6.5.0,Replaced by using dynamic reloading via the `heartbeat.config.monitors` option.] - -The JSON file to watch for additional monitor configurations. The JSON file can -contain multiple objects, each of which specifies a different monitor config. -{beatname_uc} checks this file periodically and starts a new monitor instance for -each new JSON object added to the file. For example, imagine that you add -10 new entries to the JSON file, each for a different hostname. When {beatname_uc} -picks up the changes in the file, it merges the original config -(`heartbeat.yml`) plus the JSON objects, and starts a monitor for each new host -that you've configured. If you delete an object from the JSON file and it -doesn't exist in the main config, {beatname_uc} stops the monitor instance running -for that object. - -Each monitor has a unique ID that's based on parameters like protocol, host, -and port. If two monitors have the same ID, {beatname_uc} uses the settings that -are defined in the last JSON object of the merged config. This means that -you can specify settings in the JSON file that overwrite the settings in -the main config. In this way, the configuration that you specify for the -monitor in the main {beatname_uc} config file acts like a default config that you -can live-reconfigure by specifying additional configurations in the external -JSON file. - -Example configuration: - -[source, yaml] -------------------------------------------------------------------------------- -heartbeat.monitors: -- type: tcp - id: demo-service - name: Demo Service - schedule: '*/5 * * * * * *' - hosts: ["myhost"] - watch.poll_file: - path: {path.config}/monitors/dynamic.json - interval: 5s -------------------------------------------------------------------------------- +include::monitors/monitor-common-options.asciidoc[] -*`path`*:: Specifies the path to the JSON file to check for updates. -*`interval`*:: Specifies how often {beatname_uc} checks the file for changes. +include::monitors/monitor-icmp.asciidoc[] -To reconfigure the settings specified in the example config, you could define -the following JSON objects in `dynamic.json`: +include::monitors/monitor-tcp.asciidoc[] -[source, json] -------------------------------------------------------------------------------- -{"hosts": ["myhost:1234"], "schedule": "*/15 * * * * * *"} <1> -{"hosts": ["tls://otherhost:479"], "ssl.certificate_authorities": ["path/to/ca/file.pem"]} <2> -------------------------------------------------------------------------------- -<1> Upon detecting the changes, {beatname_uc} stops the old monitor and then -restarts it with a schedule of 15 seconds between checks. -<2> {beatname_uc} starts a new monitor that uses a TLS-based connection with a -custom CA certificate. +include::monitors/monitor-http.asciidoc[] diff --git a/heartbeat/docs/heartbeat-scheduler.asciidoc b/heartbeat/docs/heartbeat-scheduler.asciidoc new file mode 100644 index 00000000000..363928f1e58 --- /dev/null +++ b/heartbeat/docs/heartbeat-scheduler.asciidoc @@ -0,0 +1,41 @@ +[[monitors-scheduler]] +== Configure the task scheduler + +++++ +Task scheduler +++++ + +You specify options under `heartbeat.scheduler` to control the behavior of the task +scheduler. + +Example configuration: + +[source,yaml] +------------------------------------------------------------------------------- +heartbeat.scheduler: + limit: 10 + location: 'UTC-08:00' +------------------------------------------------------------------------------- + +In the example, setting `limit` to 10 guarantees that only 10 concurrent +I/O tasks will be active. An I/O task can be the actual check or resolving an +address via DNS. + +[float] +[[heartbeat-scheduler-limit]] +==== `limit` + +The number of concurrent I/O tasks that {beatname_uc} is allowed to execute. If set +to 0, there is no limit. The default is 0. + +Most operating systems set a file descriptor limit of 1024. For {beatname_uc} to +operate correctly and not accidentally block libbeat output, the value that you +specify for `limit` should be below the configured ulimit. + + +[float] +[[heartbeat-scheduler-location]] +==== `location` + +The time zone for the scheduler. By default the scheduler uses localtime. + diff --git a/heartbeat/docs/monitors/monitor-common-options.asciidoc b/heartbeat/docs/monitors/monitor-common-options.asciidoc new file mode 100644 index 00000000000..68194b28119 --- /dev/null +++ b/heartbeat/docs/monitors/monitor-common-options.asciidoc @@ -0,0 +1,143 @@ +[[monitor-options]] +=== Common monitor options + +You can specify the following options when defining a {beatname_uc} monitor in any location. +These options are the same for all monitors. Each monitor type has additional configuration +options that are specific to that monitor type. + +[float] +[[monitor-type]] +==== `type` + +The type of monitor to run. See <>. + +[float] +[[monitor-id]] +==== `id` + +A unique identifier for this configuration. This should not change with edits to the monitor configuration +regardless of changes to any config fields. Examples: `uploader-service`, `http://example.net`, `us-west-loadbalancer`. Note that this uniqueness is only within a given beat instance. If you want to monitor the same endpoint from multiple locations it is recommended that those heartbeat instances use the same IDs so that their results can be correlated. You can use the `host.geo.name` property to disambiguate them. + +When querying against indexed monitor data this is the field you will be aggregating with. Appears in the +<> as `monitor.id`. + +If you do not set this explicitly the monitor's config will be hashed and a generated value used. This value will +change with any options change to this monitor making aggregations over time between changes impossible. For this reason +it is recommended that you set this manually. + +[float] +[[monitor-name]] +==== `name` + +Optional human readable name for this monitor. This value appears in the <> +as `monitor.name`. + +[float] +[[monitor-enabled]] +==== `enabled` + +A Boolean value that specifies whether the module is enabled. If the `enabled` +option is missing from the configuration block, the module is enabled by +default. + +[float] +[[monitor-schedule]] +==== `schedule` + +A cron-like expression that specifies the task schedule. For example: + +* `*/5 * * * * * *` runs the task every 5 seconds (for example, at 10:00:00, +10:00:05, and so on). +* `@every 5s` runs the task every 5 seconds from the time when {beatname_uc} was +started. + +The `schedule` option uses a cron-like syntax based on https://github.com/gorhill/cronexpr#implementation[this `cronexpr` implementation], +but adds the `@every` keyword. + +For stats on the execution of scheduled tasks you can enable the HTTP stats server with `http.enabled: true` in heartbeat.yml, then run `curl http://localhost:5066/stats | jq .heartbeat.scheduler` to view the scheduler's stats. Stats are provided for both jobs and tasks. Each time a monitor is scheduled is considered to be a single job, while portions of the work a job does, like DNS lookups and executing network requests are defined as tasks. The stats provided are: + +* **jobs.active:** The number of actively running jobs/monitors. +* **jobs.missed_deadline:** The number of jobs that executed after their scheduled time. This can be caused either by overlong long timeouts from the previous job or high load preventing heartbeat from keeping up with work. +* **tasks.active:** The number of tasks currently running. +* **tasks.waiting:** If the global `schedule.limit` option is set, this number will reflect the number of tasks that are ready to execute, but have not been started in order to prevent exceeding `schedule.limit`. + +Also see the <> settings. + +[float] +[[monitor-ipv4]] +==== `ipv4` + +A Boolean value that specifies whether to ping using the ipv4 protocol if +hostnames are configured. The default is `true`. + +[float] +[[monitor-ipv6]] +==== `ipv6` + +A Boolean value that specifies whether to ping using the ipv6 protocol +if hostnames are configured. The default is `true`. + +[float] +[[monitor-mode]] +==== `mode` + +If `mode` is `any`, the monitor pings only one IP address for a hostname. If +`mode` is `all`, the monitor pings all resolvable IPs for a hostname. The +`mode: all` setting is useful if you are using a DNS-load balancer and want to +ping every IP address for the specified hostname. The default is `any`. + +[float] +[[monitor-timeout]] +==== `timeout` + +The total running time for each ping test. This is the total time allowed for +testing the connection and exchanging data. The default is 16 seconds (16s). + +If the timeout is exceeded, {beatname_uc} publishes a `service-down` event. If the +value specified for `timeout` is greater than `schedule`, intermediate checks +will not be executed by the scheduler. + +[float] +[[monitor-fields]] +==== `fields` + +Optional fields that you can specify to add additional information to the +output. For example, you might add fields that you can use for filtering log +data. Fields can be scalar values, arrays, dictionaries, or any nested +combination of these. By default, the fields that you specify here will be +grouped under a `fields` sub-dictionary in the output document. To store the +custom fields as top-level fields, set the `fields_under_root` option to true. +If a duplicate field is declared in the general configuration, then its value +will be overwritten by the value declared here. + +[float] +[[monitor-fields-under-root]] +==== `fields_under_root` + +If this option is set to true, the custom <> +are stored as top-level fields in the output document instead of being grouped +under a `fields` sub-dictionary. If the custom field names conflict with other +field names added by {beatname_uc}, then the custom fields overwrite the other +fields. + +[float] +[[monitor-tags]] +==== `tags` + +A list of tags that will be sent with the monitor event. This setting is optional. + +[float] +[[monitor-processors]] +==== `processors` + +A list of processors to apply to the data generated by the monitor. + +See <> for information about specifying +processors in your config. + +[float] +[[monitor-keep-null]] +==== `keep_null` + +If this option is set to true, fields with `null` values will be published in +the output document. By default, `keep_null` is set to `false`. diff --git a/heartbeat/docs/monitors/monitor-http.asciidoc b/heartbeat/docs/monitors/monitor-http.asciidoc new file mode 100644 index 00000000000..1cea32f662f --- /dev/null +++ b/heartbeat/docs/monitors/monitor-http.asciidoc @@ -0,0 +1,246 @@ +[[monitor-http-options]] +=== HTTP options + +Also see <>. + +The options described here configure {beatname_uc} to connect via HTTP and +optionally verify that the host returns the expected response. + +Example configuration: + +[source,yaml] +---- +- type: http + id: myhost + name: My HTTP Host + schedule: '@every 5s' + hosts: ["http://myhost:80"] +---- + +[float] +[[monitor-http-urls]] +==== `hosts` + +A list of URLs to ping. + +[float] +[[monitor-http-max-redirects]] +==== `max_redirects` + +The total number of redirections Heartbeat will follow. Defaults to 0, meaning heartbeat will not follow redirects, +but will report the status of the redirect. If set to a number greater than 0 heartbeat will follow that number of redirects. + +When this option is set to a value greater than zero the `monitor.ip` field will no longer be reported, as multiple +DNS requests across multiple IPs may return multiple IPs. Fine grained network timing data will also not be recorded, as with redirects +that data will span multiple requests. Specifically the fields `http.rtt.content.us`, `http.rtt.response_header.us`, +`http.rtt.total.us`, `http.rtt.validate.us`, `http.rtt.write_request.us` and `dns.rtt.us` will be omitted. + +[float] +[[monitor-http-proxy-url]] +==== `proxy_url` + +The HTTP proxy URL. This setting is optional. Example `http://proxy.mydomain.com:3128` + +[float] +[[monitor-http-username]] +==== `username` + +The username for authenticating with the server. The credentials are passed +with the request. This setting is optional. + +You need to specify credentials when your `check.response` settings require it. +For example, you can check for a 403 response (`check.response.status: [403]`) +without setting credentials. + +[float] +[[monitor-http-password]] +==== `password` + +The password for authenticating with the server. This setting is optional. + +[float] +[[monitor-http-tls-ssl]] +==== `ssl` + +The TLS/SSL connection settings for use with the HTTPS endpoint. If you don't +specify settings, the system defaults are used. + + +Example configuration: + +[source,yaml] +------------------------------------------------------------------------------- +- type: http + id: my-http-service + name: My HTTP Service + hosts: ["https://myhost:443"] + schedule: '@every 5s' + ssl: + certificate_authorities: ['/etc/ca.crt'] + supported_protocols: ["TLSv1.0", "TLSv1.1", "TLSv1.2"] +------------------------------------------------------------------------------- + +Also see <> for a full description of the `ssl` options. + +[float] +[[monitor-http-response]] +=== `response` + +Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field. + +Set `response.include_body` to one of the options listed below. + +*`on_error`*:: Include the body if an error is encountered during the check. This is the default. +*`never`*:: Never include the body. +*`always`*:: Always include the body with checks. + +Set `response.include_body_max_bytes` to control the maximum size of the stored body contents. Defaults to 1024 bytes. + +[float] +[[monitor-http-check]] +==== `check` + +An optional `request` to send to the remote host and the expected `response`. + +Example configuration: + +[source,yaml] +------------------------------------------------------------------------------- +- type: http + id: my-http-host + name: My HTTP Service + hosts: ["http://myhost:80"] + check.request.method: HEAD + check.response.status: [200] + schedule: '@every 5s' +------------------------------------------------------------------------------- + + +Under `check.request`, specify these options: + +*`method`*:: The HTTP method to use. Valid values are `"HEAD"`, `"GET"` and +`"POST"`. +*`headers`*:: A dictionary of additional HTTP headers to send. By default heartbeat +will set the 'User-Agent' header to identify itself. +*`body`*:: Optional request body content. + +Example configuration: +This monitor POSTs an `x-www-form-urlencoded` string +to the endpoint `/demo/add` + +[source,yaml] +------------------------------------------------------------------------------- +- type: http + id: demo-service + name: Demo Service + schedule: '@every 5s' + urls: ["http://localhost:8080/demo/add"] + check.request: + method: POST + headers: + 'Content-Type': 'application/x-www-form-urlencoded' + # urlencode the body: + body: "name=first&email=someemail%40someemailprovider.com" + check.response: + status: [200] + body: + - Saved + - saved +------------------------------------------------------------------------------- + +Under `check.response`, specify these options: + +*`status`*:: A list of expected status codes. 4xx and 5xx codes are considered `down` by default. Other codes are considered `up`. +*`headers`*:: The required response headers. +*`body`*:: A list of regular expressions to match the the body output. Only a single expression needs to match. HTTP response +bodies of up to 100MiB are supported. + +Example configuration: +This monitor examines the +response body for the strings `saved` or `Saved` and expects 200 or 201 status codes + +[source,yaml] +------------------------------------------------------------------------------- +- type: http + id: demo-service + name: Demo Service + schedule: '@every 5s' + urls: ["http://localhost:8080/demo/add"] + check.request: + method: POST + headers: + 'Content-Type': 'application/x-www-form-urlencoded' + # urlencode the body: + body: "name=first&email=someemail%40someemailprovider.com" + check.response: + status: [200, 201] + body: + - Saved + - saved +------------------------------------------------------------------------------- + +*`json`*:: A list of <> expressions executed against the body when parsed as JSON. Body sizes +must be less than or equal to 100 MiB. + +The following configuration shows how to check the response when the body +contains JSON: + +[source,yaml] +------------------------------------------------------------------------------- +- type: http + id: demo-service + name: Demo Service + schedule: '@every 5s' + hosts: ["https://myhost:80"] + check.request: + method: GET + headers: + 'X-API-Key': '12345-mykey-67890' + check.response: + status: [200] + json: + - description: check status + condition: + equals: + status: ok +------------------------------------------------------------------------------- + +The following configuration shows how to check the response for multiple regex +patterns: + +[source,yaml] +------------------------------------------------------------------------------- +- type: http + id: demo-service + name: Demo Service + schedule: '@every 5s' + hosts: ["https://myhost:80"] + check.request: + method: GET + headers: + 'X-API-Key': '12345-mykey-67890' + check.response: + status: [200] + body: + - hello + - world +------------------------------------------------------------------------------- + +The following configuration shows how to check the response with a multiline +regex: + +[source,yaml] +------------------------------------------------------------------------------- +- type: http + id: demo-service + name: Demo Service + schedule: '@every 5s' + hosts: ["https://myhost:80"] + check.request: + method: GET + headers: + 'X-API-Key': '12345-mykey-67890' + check.response: + status: [200] + body: '(?s)first.*second.*third' +------------------------------------------------------------------------------- diff --git a/heartbeat/docs/monitors/monitor-icmp.asciidoc b/heartbeat/docs/monitors/monitor-icmp.asciidoc new file mode 100644 index 00000000000..ccd0ba5f397 --- /dev/null +++ b/heartbeat/docs/monitors/monitor-icmp.asciidoc @@ -0,0 +1,39 @@ +[[monitor-icmp-options]] +=== ICMP options + +Also see <>. + +The options described here configure {beatname_uc} to use ICMP (v4 and v6) Echo +Requests to check the configured hosts. Please note that on most platforms you +must execute Heartbeat with elevated permissions to perform ICMP pings. + +On Linux, regular users may perform pings if the right file capabilities are set. Run +`sudo setcap cap_net_raw+eip /path/to/heartbeat` to grant {beatname_uc} ping capabilities on Linux. +Alternatively, one may grant ping permissions to the user {beatname_uc} runs as. To grant ping permissions +in this way, run `sudo sysctl -w net.ipv4.ping_group_range='myuserid myuserid'`. + +Other platforms may require {beatname_uc} to run as root or administrator to execute pings. + +Example configuration: + +[source,yaml] +---- +- type: icmp + id: ping-myhost + name: My Host Ping + hosts: ["myhost"] + schedule: '*/5 * * * * * *' +---- + +[float] +[[monitor-icmp-hosts]] +==== `hosts` + +A list of hosts to ping. + +[float] +[[monitor-icmp-wait]] +==== `wait` + +The duration to wait before emitting another ICMP Echo Request. The default is 1 +second (1s). diff --git a/heartbeat/docs/monitors/monitor-tcp.asciidoc b/heartbeat/docs/monitors/monitor-tcp.asciidoc new file mode 100644 index 00000000000..10d39b06302 --- /dev/null +++ b/heartbeat/docs/monitors/monitor-tcp.asciidoc @@ -0,0 +1,134 @@ +[[monitor-tcp-options]] +=== TCP options + +Also see <>. + +The options described here configure {beatname_uc} to connect via TCP and +optionally verify the endpoint by sending and/or receiving a custom payload. + +Example configuration: + +[source,yaml] +---- +- type: tcp + id: my-host-services + name: My Host Services + hosts: ["myhost"] + ports: [80, 9200, 5044] + schedule: '@every 5s' +---- + +[float] +[[monitor-tcp-hosts]] +==== `hosts` + +A list of hosts to ping. The entries in the list can be: + +* A plain host name, such as `localhost`, or an IP address. If you specify this +option, you must also specify a value for <>. If the +monitor is <>, {beatname_uc} establishes an +SSL/TLS-based connection. Otherwise, it establishes a plain TCP connection. +* A hostname and port, such as `localhost:12345`. {beatname_uc} connects +to the port on the specified host. If the monitor is +<>, {beatname_uc} establishes an +SSL/TLS-based connection. Otherwise, it establishes a TCP connection. +* A full URL using the syntax `scheme://:[port]`, where: +** `scheme` is one of `tcp`, `plain`, `ssl` or `tls`. If `tcp` or `plain` is +specified, {beatname_uc} establishes a TCP connection even if the monitor is +configured to use SSL. If `tls` or `ssl` is specified, {beatname_uc} establishes +an SSL connection. However, if the monitor is not configured to use SSL, the +system defaults are used (currently not supported on Windows). +** `host` is the hostname. +** `port` is the port number. If `port` is missing in the URL, the +<> setting is required. + +[float] +[[monitor-tcp-ports]] +==== `ports` + +A list of ports to ping if the host specified in <> +does not contain a port number. It is generally preferable to use a single value here, +since each port will be monitored using a separate `id`, with the given `id` value, +used as a prefix in the Heartbeat data, and the configured `name` shared across events +sent via this check. + +[float] +[[monitor-tcp-check]] +==== `check` + +An optional payload string to send to the remote host and the expected answer. +If no payload is specified, the endpoint is assumed to be available if the +connection attempt was successful. If `send` is specified without `receive`, +any response is accepted as OK. If `receive` is specified without `send`, no +payload is sent, but the client expects to receive a payload in the form of a +"hello message" or "banner" on connect. + +Example configuration: + +[source,yaml] +------------------------------------------------------------------------------- +- type: tcp + id: echo-service + name: Echo Service + hosts: ["myhost"] + ports: [7] + check.send: 'Hello World' + check.receive: 'Hello World' + schedule: '@every 5s' +------------------------------------------------------------------------------- + + +[float] +[[monitor-tcp-proxy-url]] +==== `proxy_url` + +The URL of the SOCKS5 proxy to use when connecting to the server. The value +must be a URL with a scheme of socks5://. + +If the SOCKS5 proxy server requires client authentication, then a username and +password can be embedded in the URL as shown in the example. + +[source,yaml] +------------------------------------------------------------------------------- + proxy_url: socks5://user:password@socks5-proxy:2233 +------------------------------------------------------------------------------- + +When using a proxy, hostnames are resolved on the proxy server instead of on +the client. You can change this behavior by setting the +`proxy_use_local_resolver` option. + +[float] +[[monitor-tcp-proxy-use-local-resolver]] +==== `proxy_use_local_resolver` + +A Boolean value that determines whether hostnames are resolved locally instead +of being resolved on the proxy server. The default value is false, which means +that name resolution occurs on the proxy server. + +[float] +[[monitor-tcp-tls-ssl]] +==== `ssl` + +The TLS/SSL connection settings. If the monitor is +<>, it will attempt an SSL +handshake. If `check` is not configured, the monitor will only check to see if +it can establish an SSL/TLS connection. This check can fail either at TCP level +or during certificate validation. + +Example configuration: + +[source,yaml] +------------------------------------------------------------------------------- +- type: tcp + id: tls-mail + name: TLS Mail + hosts: ["mail.example.net"] + ports: [465] + schedule: '@every 5s' + ssl: + certificate_authorities: ['/etc/ca.crt'] + supported_protocols: ["TLSv1.0", "TLSv1.1", "TLSv1.2"] +------------------------------------------------------------------------------- + + +Also see <> for a full description of the `ssl` options. diff --git a/heartbeat/hbtest/hbtestutil.go b/heartbeat/hbtest/hbtestutil.go index 548bc42a0eb..246104638e9 100644 --- a/heartbeat/hbtest/hbtestutil.go +++ b/heartbeat/hbtest/hbtestutil.go @@ -18,10 +18,12 @@ package hbtest import ( + "crypto/tls" "crypto/x509" "fmt" "io" "io/ioutil" + "net" "net/http" "net/http/httptest" "net/url" @@ -29,6 +31,10 @@ import ( "strconv" "strings" "testing" + "time" + + "github.com/elastic/beats/v7/heartbeat/monitors/active/dialchain/tlsmeta" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/heartbeat/hbtestllext" @@ -107,13 +113,25 @@ func ServerPort(server *httptest.Server) (uint16, error) { // TLSChecks validates the given x509 cert at the given position. func TLSChecks(chainIndex, certIndex int, certificate *x509.Certificate) validator.Validator { - return lookslike.MustCompile(map[string]interface{}{ - "tls": map[string]interface{}{ - "rtt.handshake.us": isdef.IsDuration, - "certificate_not_valid_before": certificate.NotBefore, - "certificate_not_valid_after": certificate.NotAfter, - }, - }) + expected := common.MapStr{} + // This function is well tested independently, so we just test that things match up here. + tlsmeta.AddTLSMetadata(expected, tls.ConnectionState{ + Version: tls.VersionTLS13, + HandshakeComplete: true, + CipherSuite: tls.TLS_AES_128_GCM_SHA256, + ServerName: certificate.Subject.CommonName, + PeerCertificates: []*x509.Certificate{certificate}, + }, time.Duration(1)) + + expected.Put("tls.rtt.handshake.us", isdef.IsDuration) + + return lookslike.MustCompile(expected) +} + +func TLSCertChecks(certificate *x509.Certificate) validator.Validator { + expected := common.MapStr{} + tlsmeta.AddCertMetadata(expected, []*x509.Certificate{certificate}) + return lookslike.MustCompile(expected) } // BaseChecks creates a skima.Validator that represents the "monitor" field present @@ -196,6 +214,14 @@ func ErrorChecks(msgSubstr string, errType string) validator.Validator { }) } +func ExpiredCertChecks(cert *x509.Certificate) validator.Validator { + msg := x509.CertificateInvalidError{Cert: cert, Reason: x509.Expired}.Error() + return lookslike.Compose( + ErrorChecks(msg, "io"), + TLSCertChecks(cert), + ) +} + // RespondingTCPChecks creates a skima.Validator that represents the "tcp" field present // in all heartbeat events that use a Tcp connection as part of their DialChain func RespondingTCPChecks() validator.Validator { @@ -215,3 +241,23 @@ func CertToTempFile(t *testing.T, cert *x509.Certificate) *os.File { certFile.WriteString(x509util.CertToPEMString(cert)) return certFile } + +func StartHTTPSServer(t *testing.T, tlsCert tls.Certificate) (host string, port string, cert *x509.Certificate, doClose func() error) { + cert, err := x509.ParseCertificate(tlsCert.Certificate[0]) + require.NoError(t, err) + + // No need to start a real server, since this is invalid, we just + l, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{ + Certificates: []tls.Certificate{tlsCert}, + }) + require.NoError(t, err) + + srv := &http.Server{Handler: HelloWorldHandler(200)} + go func() { + srv.Serve(l) + }() + + host, port, err = net.SplitHostPort(l.Addr().String()) + require.NoError(t, err) + return host, port, cert, srv.Close +} diff --git a/heartbeat/heartbeat.reference.yml b/heartbeat/heartbeat.reference.yml index 89baae61518..cd8addcc09a 100644 --- a/heartbeat/heartbeat.reference.yml +++ b/heartbeat/heartbeat.reference.yml @@ -266,10 +266,10 @@ heartbeat.scheduler: # disabled if set to 0. The default is 0. #limit: 0 - # Set the scheduler it's timezone + # Set the scheduler it's time zone #location: '' -#================================ General ====================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -377,7 +377,7 @@ heartbeat.scheduler: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -394,153 +394,153 @@ heartbeat.scheduler: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Heartbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -553,11 +553,11 @@ heartbeat.scheduler: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -677,7 +677,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -791,7 +812,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -945,6 +966,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -967,7 +991,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -1085,7 +1109,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -1119,7 +1143,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -1132,7 +1156,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Heartbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -1158,11 +1182,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -1206,8 +1232,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1261,7 +1286,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1275,13 +1300,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'heartbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "heartbeat" +#setup.ilm.rollover_alias: 'heartbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'heartbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1296,7 +1321,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1351,9 +1376,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1420,8 +1444,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Heartbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1531,6 +1554,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1542,7 +1586,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1566,12 +1611,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/heartbeat/heartbeat.yml b/heartbeat/heartbeat.yml index aa3e1283f70..425329e7c9a 100644 --- a/heartbeat/heartbeat.yml +++ b/heartbeat/heartbeat.yml @@ -33,14 +33,14 @@ heartbeat.monitors: # Total test connection and data exchange timeout #timeout: 16s -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 index.codec: best_compression #_source.enabled: false -#================================ General ===================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -56,7 +56,7 @@ setup.template.settings: # env: staging -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -73,7 +73,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Heartbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -86,11 +86,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -103,7 +103,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -118,7 +118,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= processors: - add_observer_metadata: @@ -129,7 +129,8 @@ processors: # Lat, Lon " #location: "37.926868, -78.024902" -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -140,8 +141,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# heartbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Heartbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -162,7 +163,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/heartbeat/include/fields.go b/heartbeat/include/fields.go index e21fe351e87..9261227de21 100644 --- a/heartbeat/include/fields.go +++ b/heartbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/heartbeat/monitors/active/dialchain/_meta/fields.yml b/heartbeat/monitors/active/dialchain/_meta/fields.yml index 5d4991a9704..eb7e66bd6f7 100644 --- a/heartbeat/monitors/active/dialchain/_meta/fields.yml +++ b/heartbeat/monitors/active/dialchain/_meta/fields.yml @@ -34,10 +34,12 @@ fields: - name: certificate_not_valid_before type: date - description: Earliest time at which the connection's certificates are valid. + deprecated: 7.8.0 + description: Deprecated in favor of `tls.server.x509.not_before`. Earliest time at which the connection's certificates are valid. - name: certificate_not_valid_after + deprecated: 7.8.0 type: date - description: Latest time at which the connection's certificates are valid. + description: Deprecated in favor of `tls.server.x509.not_after`. Latest time at which the connection's certificates are valid. - name: rtt type: group description: > @@ -52,4 +54,112 @@ - name: us type: long description: Duration in microseconds + - name: server + type: group + description: Detailed x509 certificate metadata + fields: + - name: x509 + type: group + fields: + - name: alternative_names + type: keyword + ignore_above: 1024 + description: List of subject alternative names (SAN). Name types vary by certificate + authority and certificate type but commonly contain IP addresses, DNS names + (and wildcards), and email addresses. + example: '*.elastic.co' + default_field: false + - name: issuer + type: group + fields: + - name: common_name + type: keyword + ignore_above: 1024 + description: List of common name (CN) of issuing certificate authority. + example: DigiCert SHA2 High Assurance Server CA + default_field: false + multi_fields: + - name: text + type: text + analyzer: simple + - name: distinguished_name + type: keyword + ignore_above: 1024 + description: Distinguished name (DN) of issuing certificate authority. + example: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance + Server CA + default_field: false + - name: not_after + type: date + description: Time at which the certificate is no longer considered valid. + example: 2020-07-16 03:15:39+00:00 + default_field: false + - name: not_before + type: date + description: Time at which the certificate is first considered valid. + example: 2019-08-16 01:40:25+00:00 + default_field: false + - name: public_key_algorithm + type: keyword + ignore_above: 1024 + description: Algorithm used to generate the public key. + example: RSA + default_field: false + - name: public_key_curve + type: keyword + ignore_above: 1024 + description: The curve used by the elliptic curve public key algorithm. This + is algorithm specific. + example: nistp521 + default_field: false + - name: public_key_exponent + type: long + description: Exponent used to derive the public key. This is algorithm specific. + example: 65537 + default_field: false + - name: public_key_size + type: long + description: The size of the public key space in bits. + example: 2048 + default_field: false + - name: serial_number + type: keyword + ignore_above: 1024 + description: Unique serial number issued by the certificate authority. For consistency, + if this value is alphanumeric, it should be formatted without colons and uppercase + characters. + example: 55FBB9C7DEBF09809D12CCAA + default_field: false + - name: signature_algorithm + type: keyword + ignore_above: 1024 + description: Identifier for certificate signature algorithm. Recommend using + names found in Go Lang Crypto library (See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353). + example: SHA256-RSA + default_field: false + - name: subject + type: group + fields: + - name: common_name + type: keyword + ignore_above: 1024 + description: List of common names (CN) of subject. + example: r2.shared.global.fastly.net + default_field: false + multi_fields: + - name: text + type: text + analyzer: simple + - name: distinguished_name + type: keyword + ignore_above: 1024 + description: Distinguished name (DN) of the certificate subject entity. + example: C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net + default_field: false + - name: version_number + type: keyword + ignore_above: 1024 + description: Version of x509 format. + example: 3 + default_field: false diff --git a/heartbeat/monitors/active/dialchain/tls.go b/heartbeat/monitors/active/dialchain/tls.go index 6fd2b43c27f..b4b2c006dfb 100644 --- a/heartbeat/monitors/active/dialchain/tls.go +++ b/heartbeat/monitors/active/dialchain/tls.go @@ -19,27 +19,19 @@ package dialchain import ( cryptoTLS "crypto/tls" - "crypto/x509" "fmt" "net" "time" - "github.com/elastic/beats/v7/heartbeat/look" + "github.com/elastic/beats/v7/heartbeat/monitors/active/dialchain/tlsmeta" "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/transport" "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) // TLSLayer configures the TLS layer in a DialerChain. -// -// The layer will update the active event with: -// -// { -// "tls": { -// "rtt": { "handshake": { "us": ... }} -// } -// } +// The layer will update the active event with the TLS RTT and +// crypto/cert details. func TLSLayer(cfg *tlscommon.TLSConfig, to time.Duration) Layer { return func(event *beat.Event, next transport.Dialer) (transport.Dialer, error) { var timer timer @@ -58,56 +50,12 @@ func TLSLayer(cfg *tlscommon.TLSConfig, to time.Duration) Layer { if !ok { panic(fmt.Sprintf("TLS afterDial received a non-tls connection %t. This should never happen", conn)) } - - // TODO: extract TLS connection parameters from connection object. + connState := tlsConn.ConnectionState() timer.stop() - event.PutValue("tls.rtt.handshake", look.RTT(timer.duration())) - addCertMetdata(event.Fields, tlsConn.ConnectionState().PeerCertificates) + tlsmeta.AddTLSMetadata(event.Fields, connState, timer.duration()) return conn, nil }), nil } } - -func addCertMetdata(fields common.MapStr, certs []*x509.Certificate) { - // The behavior here might seem strange. We *always* set a notBefore, but only optionally set a notAfter. - // Why might we do this? - // The root cause is that the x509.Certificate type uses time.Time for these fields instead of *time.Time - // so we have no way to know if the user actually set these fields. The x509 RFC says that only one of the - // two fields must be set. Most tools (including openssl and go's certgen) always set both. BECAUSE WHY NOT - // - // In the wild, however, there are certs missing one of these two fields. - // So, what's the correct behavior here? We cannot know if a field was omitted due to the lack of nullability. - // So, in this case, we try to do what people will want 99.99999999999999999% of the time. - // People might set notBefore to go's zero date intentionally when creating certs. So, we always set that - // field, even if we find a zero value. - // However, it would be weird to set notAfter to the zero value. That could invalidate a cert that was intended - // to be valid forever. So, in that case, we treat the zero value as non-existent. - // This is why notBefore is a time.Time and notAfter is a *time.Time - var chainNotValidBefore time.Time - var chainNotValidAfter *time.Time - - // We need the zero date later - var zeroTime time.Time - - // Here we compute the minimal bounds during which this certificate chain is valid - // To do this correctly, we take the maximum NotBefore and the minimum NotAfter. - // This *should* always wind up being the terminal cert in the chain, but we should - // compute this correctly. - for _, cert := range certs { - if chainNotValidBefore.Before(cert.NotBefore) { - chainNotValidBefore = cert.NotBefore - } - - if cert.NotAfter != zeroTime && (chainNotValidAfter == nil || chainNotValidAfter.After(cert.NotAfter)) { - chainNotValidAfter = &cert.NotAfter - } - } - - fields.Put("tls.certificate_not_valid_before", chainNotValidBefore) - - if chainNotValidAfter != nil { - fields.Put("tls.certificate_not_valid_after", *chainNotValidAfter) - } -} diff --git a/heartbeat/monitors/active/dialchain/tls_test.go b/heartbeat/monitors/active/dialchain/tls_test.go deleted file mode 100644 index 8df41fbd3b0..00000000000 --- a/heartbeat/monitors/active/dialchain/tls_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package dialchain - -import ( - "crypto/x509" - "crypto/x509/pkix" - "math/big" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/elastic/beats/v7/libbeat/common" -) - -func Test_addCertMetdata(t *testing.T) { - goodNotBefore := time.Now().Add(-time.Hour) - goodNotAfter := time.Now().Add(time.Hour) - goodCert := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotBefore: goodNotBefore, - NotAfter: goodNotAfter, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - expiredNotAfter := time.Now().Add(-time.Hour) - expiredCert := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotBefore: goodNotBefore, - NotAfter: expiredNotAfter, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - missingNotBeforeCert := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotAfter: goodNotAfter, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - missingNotAfterCert := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{"Acme Co"}, - }, - NotBefore: goodNotBefore, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - // notBefore is intentionally not a pointer type because go certificates don't have nullable time types - // we cheat a bit and make not after nullable because there's no valid reason to create a cert with go's zero - // time. - // see the addCertMetadata function for more info on this. - type expected struct { - notBefore time.Time - notAfter *time.Time - } - tests := []struct { - name string - certs []*x509.Certificate - expected expected - }{ - { - "Valid cert", - []*x509.Certificate{&goodCert}, - expected{ - notBefore: goodNotBefore, - notAfter: &goodNotAfter, - }, - }, - { - "Expired cert", - []*x509.Certificate{&expiredCert}, - expected{ - notBefore: goodNotBefore, - notAfter: &expiredNotAfter, - }, - }, - { - "Missing not before", - []*x509.Certificate{&missingNotBeforeCert}, - expected{ - notAfter: &goodNotAfter, - }, - }, - { - "Missing not after", - []*x509.Certificate{&missingNotAfterCert}, - expected{ - notBefore: goodNotBefore, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - event := common.MapStr{} - addCertMetdata(event, tt.certs) - v, err := event.GetValue("tls.certificate_not_valid_before") - assert.NoError(t, err) - assert.Equal(t, tt.expected.notBefore, v) - - if tt.expected.notAfter != nil { - v, err := event.GetValue("tls.certificate_not_valid_after") - assert.NoError(t, err) - assert.Equal(t, *tt.expected.notAfter, v) - } else { - ok, _ := event.HasKey("tls.certificate_not_valid_after") - assert.False(t, ok, "event should not have not after %v", event) - } - }) - } -} diff --git a/heartbeat/monitors/active/dialchain/tlsmeta/tlsmeta.go b/heartbeat/monitors/active/dialchain/tlsmeta/tlsmeta.go new file mode 100644 index 00000000000..686da2a3241 --- /dev/null +++ b/heartbeat/monitors/active/dialchain/tlsmeta/tlsmeta.go @@ -0,0 +1,133 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package tlsmeta + +import ( + dsa2 "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "crypto/sha256" + cryptoTLS "crypto/tls" + "crypto/x509" + "fmt" + "time" + + "github.com/elastic/beats/v7/heartbeat/look" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" +) + +func AddTLSMetadata(fields common.MapStr, connState cryptoTLS.ConnectionState, duration time.Duration) { + fields.Put("tls.established", true) + fields.Put("tls.rtt.handshake", look.RTT(duration)) + versionDetails := tlscommon.TLSVersion(connState.Version).Details() + // The only situation in which versionDetails would be nil is if an unknown TLS version were to be + // encountered. Not filling the fields here makes sense, since there's no standard 'unknown' value. + if versionDetails != nil { + fields.Put("tls.version_protocol", versionDetails.Protocol) + fields.Put("tls.version", versionDetails.Version) + } + + if connState.NegotiatedProtocol != "" { + fields.Put("tls.next_protocol", connState.NegotiatedProtocol) + } + fields.Put("tls.cipher", tlscommon.ResolveCipherSuite(connState.CipherSuite)) + + AddCertMetadata(fields, connState.PeerCertificates) +} + +func AddCertMetadata(fields common.MapStr, certs []*x509.Certificate) { + hostCert := certs[0] + + x509Fields := common.MapStr{} + serverFields := common.MapStr{"x509": x509Fields} + tlsFields := common.MapStr{"server": serverFields} + + serverFields.Put("hash.sha1", fmt.Sprintf("%x", sha1.Sum(hostCert.Raw))) + serverFields.Put("hash.sha256", fmt.Sprintf("%x", sha256.Sum256(hostCert.Raw))) + + x509Fields.Put("issuer.common_name", hostCert.Issuer.CommonName) + x509Fields.Put("issuer.distinguished_name", hostCert.Issuer.String()) + x509Fields.Put("subject.common_name", hostCert.Subject.CommonName) + x509Fields.Put("subject.distinguished_name", hostCert.Subject.String()) + x509Fields.Put("serial_number", hostCert.SerialNumber.String()) + x509Fields.Put("signature_algorithm", hostCert.SignatureAlgorithm.String()) + x509Fields.Put("public_key_algorithm", hostCert.PublicKeyAlgorithm.String()) + if rsaKey, ok := hostCert.PublicKey.(*rsa.PublicKey); ok { + sizeInBits := rsaKey.Size() * 8 + x509Fields.Put("public_key_size", sizeInBits) + x509Fields.Put("public_key_exponent", rsaKey.E) + } else if dsaKey, ok := hostCert.PublicKey.(*dsa2.PublicKey); ok { + if dsaKey.Parameters.P != nil { + x509Fields.Put("public_key_size", len(dsaKey.P.Bytes())*8) + } else { + x509Fields.Put("public_key_size", len(dsaKey.P.Bytes())*8) + } + } else if ecdsa, ok := hostCert.PublicKey.(*ecdsa.PublicKey); ok { + x509Fields.Put("public_key_curve", ecdsa.Curve.Params().Name) + } + + chainNotBefore, chainNotAfter := calculateCertTimestamps(certs) + // Legacy non-ECS field + tlsFields.Put("certificate_not_valid_before", chainNotBefore) + x509Fields.Put("not_before", chainNotBefore) + if chainNotAfter != nil { + // Legacy non-ECS field + tlsFields.Put("certificate_not_valid_after", *chainNotAfter) + x509Fields.Put("not_after", *chainNotAfter) + } + + fields.DeepUpdate(common.MapStr{"tls": tlsFields}) +} + +func calculateCertTimestamps(certs []*x509.Certificate) (chainNotBefore time.Time, chainNotAfter *time.Time) { + // The behavior here might seem strange. We *always* set a notBefore, but only optionally set a notAfter. + // Why might we do this? + // The root cause is that the x509.Certificate type uses time.Time for these tlsFields instead of *time.Time + // so we have no way to know if the user actually set these tlsFields. The x509 RFC says that only one of the + // two tlsFields must be set. Most tools (including openssl and go's certgen) always set both. BECAUSE WHY NOT + // + // In the wild, however, there are certs missing one of these two tlsFields. + // So, what's the correct behavior here? We cannot know if a field was omitted due to the lack of nullability. + // So, in this case, we try to do what people will want 99.99999999999999999% of the time. + // People might set notBefore to go's zero date intentionally when creating certs. So, we always set that + // field, even if we find a zero value. + // However, it would be weird to set notAfter to the zero value. That could invalidate a cert that was intended + // to be valid forever. So, in that case, we treat the zero value as non-existent. + // This is why notBefore is a time.Time and notAfter is a *time.Time + + // We need the zero date later + var zeroTime time.Time + + // Here we compute the minimal bounds during which this certificate chain is valid + // To do this correctly, we take the maximum NotBefore and the minimum NotAfter. + // This *should* always wind up being the terminal cert in the chain, but we should + // compute this correctly. + for _, cert := range certs { + if chainNotBefore.Before(cert.NotBefore) { + chainNotBefore = cert.NotBefore + } + + if cert.NotAfter != zeroTime && (chainNotAfter == nil || chainNotAfter.After(cert.NotAfter)) { + chainNotAfter = &cert.NotAfter + } + } + + return +} diff --git a/heartbeat/monitors/active/dialchain/tlsmeta/tlsmeta_test.go b/heartbeat/monitors/active/dialchain/tlsmeta/tlsmeta_test.go new file mode 100644 index 00000000000..609fbbb413d --- /dev/null +++ b/heartbeat/monitors/active/dialchain/tlsmeta/tlsmeta_test.go @@ -0,0 +1,398 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package tlsmeta + +import ( + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "testing" + "time" + + "github.com/elastic/go-lookslike" + "github.com/elastic/go-lookslike/testslike" + + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/heartbeat/look" + "github.com/elastic/beats/v7/libbeat/common" +) + +// Tests for the non-cert fields +func TestAddTLSMetadata(t *testing.T) { + // We always test with this one cert because addCertificateMetadata + // is tested in detail elsewhere + certs := []*x509.Certificate{parseCert(t, elasticCert)} + certMetadata := common.MapStr{} + AddCertMetadata(certMetadata, certs) + + scenarios := []struct { + name string + connState tls.ConnectionState + duration time.Duration + expected common.MapStr + }{ + { + "simple TLSv1.1", + tls.ConnectionState{ + Version: tls.VersionTLS11, + HandshakeComplete: true, + PeerCertificates: certs, + CipherSuite: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ServerName: "example.net", + }, + time.Duration(1), + common.MapStr{ + "established": true, + "rtt": common.MapStr{"handshake": look.RTT(time.Duration(1))}, + "version_protocol": "tls", + "version": "1.1", + "cipher": "ECDHE-ECDSA-AES-256-CBC-SHA", + }, + }, + { + "TLSv1.2 with next_protocol", + tls.ConnectionState{ + Version: tls.VersionTLS12, + HandshakeComplete: true, + PeerCertificates: certs, + CipherSuite: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ServerName: "example.net", + NegotiatedProtocol: "h2", + }, + time.Duration(1), + common.MapStr{ + "established": true, + "rtt": common.MapStr{"handshake": look.RTT(time.Duration(1))}, + "version_protocol": "tls", + "version": "1.2", + "cipher": "ECDHE-ECDSA-AES-256-CBC-SHA", + "next_protocol": "h2", + }, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + // Nest under the TLS namespace to match actual output + expected := common.MapStr{"tls": s.expected} + + // Always add in the cert metadata since we test that in other test funcs, not here + expected.DeepUpdate(certMetadata) + + fields := common.MapStr{} + AddTLSMetadata(fields, s.connState, s.duration) + require.Equal(t, expected, fields) + }) + } +} + +func TestAddCertMetadata(t *testing.T) { + cert := parseCert(t, elasticCert) + chainCert := parseCert(t, elasticChainCert) + certNotBefore, err := time.Parse(time.RFC3339, "2019-08-16T01:40:25Z") + require.NoError(t, err) + certNotAfter, err := time.Parse(time.RFC3339, "2020-07-16T03:15:39Z") + require.NoError(t, err) + + expectedFields := lookslike.Strict(lookslike.MustCompile(map[string]interface{}{ + "certificate_not_valid_after": certNotAfter, + "certificate_not_valid_before": certNotBefore, + "server": common.MapStr{ + "hash": common.MapStr{ + "sha1": "b7b4b89ef0d0caf39d223736f0fdbb03c7b426f1", + "sha256": "12b00d04db0db8caa302bfde043e88f95baceb91e86ac143e93830b4bbec726d", + }, + "x509": common.MapStr{ + "issuer": common.MapStr{ + "common_name": "GlobalSign CloudSSL CA - SHA256 - G3", + "distinguished_name": "CN=GlobalSign CloudSSL CA - SHA256 - G3,O=GlobalSign nv-sa,C=BE", + }, + "subject": common.MapStr{ + "common_name": "r2.shared.global.fastly.net", + "distinguished_name": "CN=r2.shared.global.fastly.net,O=Fastly\\, Inc.,L=San Francisco,ST=California,C=US", + }, + "not_after": certNotAfter, + "not_before": certNotBefore, + "serial_number": "26610543540289562361990401194", + "signature_algorithm": "SHA256-RSA", + "public_key_algorithm": "RSA", + "public_key_size": 2048, + "public_key_exponent": 65537, + }, + }, + })) + + scenarios := []struct { + name string + certs []*x509.Certificate + }{ + { + "single cert fields should all be present", + []*x509.Certificate{cert}, + }, + { + "cert chain should still show single cert fields", + []*x509.Certificate{cert, chainCert}, + }, + } + + for _, scenario := range scenarios { + t.Run(scenario.name, func(t *testing.T) { + fields := common.MapStr{} + AddCertMetadata(fields, scenario.certs) + tls, err := fields.GetValue("tls") + require.NoError(t, err) + testslike.Test(t, expectedFields, tls) + }) + } +} + +// TestCertExpirationMetadata exhaustively tests not before / not after calculation. +func TestCertExpirationMetadata(t *testing.T) { + goodNotBefore := time.Now().Add(-time.Hour) + goodNotAfter := time.Now().Add(time.Hour) + goodCert := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: goodNotBefore, + NotAfter: goodNotAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + expiredNotAfter := time.Now().Add(-time.Hour) + expiredCert := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: goodNotBefore, + NotAfter: expiredNotAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + missingNotBeforeCert := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotAfter: goodNotAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + missingNotAfterCert := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: goodNotBefore, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + // notBefore is intentionally not a pointer type because go certificates don't have nullable time types + // we cheat a bit and make not after nullable because there's no valid reason to create a cert with go's zero + // time. + // see the AddCertMetadata function for more info on this. + type expected struct { + notBefore time.Time + notAfter *time.Time + } + tests := []struct { + name string + certs []*x509.Certificate + expected expected + }{ + { + "Valid cert", + []*x509.Certificate{&goodCert}, + expected{ + notBefore: goodNotBefore, + notAfter: &goodNotAfter, + }, + }, + { + "Expired cert", + []*x509.Certificate{&expiredCert}, + expected{ + notBefore: goodNotBefore, + notAfter: &expiredNotAfter, + }, + }, + { + "Missing not before", + []*x509.Certificate{&missingNotBeforeCert}, + expected{ + notAfter: &goodNotAfter, + }, + }, + { + "Missing not after", + []*x509.Certificate{&missingNotAfterCert}, + expected{ + notBefore: goodNotBefore, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + notBefore, notAfter := calculateCertTimestamps(tt.certs) + + require.Equal(t, tt.expected.notBefore, notBefore) + if tt.expected.notAfter != nil { + require.Equal(t, tt.expected.notAfter, notAfter) + } else { + require.Nil(t, notAfter) + } + }) + } +} + +func parseCert(t *testing.T, pemStr string) *x509.Certificate { + block, _ := pem.Decode([]byte(elasticCert)) + if block == nil { + require.Fail(t, "Test cert could not be parsed") + } + cert, err := x509.ParseCertificate(block.Bytes) + require.NoError(t, err) + return cert +} + +var elasticCert = `-----BEGIN CERTIFICATE----- +MIIPLzCCDhegAwIBAgIMVfu5x96/CYCdEsyqMA0GCSqGSIb3DQEBCwUAMFcxCzAJ +BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMS0wKwYDVQQDEyRH +bG9iYWxTaWduIENsb3VkU1NMIENBIC0gU0hBMjU2IC0gRzMwHhcNMTkwODE2MDE0 +MDI1WhcNMjAwNzE2MDMxNTM5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs +aWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEVMBMGA1UECgwMRmFzdGx5 +LCBJbmMuMSQwIgYDVQQDDBtyMi5zaGFyZWQuZ2xvYmFsLmZhc3RseS5uZXQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnvoHpOqA6CM06MlGViMGMFC4G +YFFEe03GQ5jG3uEUbMNPbl0MSxaWle5xZOVaPcIrV7qyE5yKKDv1fT1e8EkwR+3t +nTK4k2QvH6dPtSPlGHVIjBtS17gM939eZvpvUPxmUc5Ov9cbWgsuStqgFpFjnPBV +R0LqD6YekvS9oXG+4GrNZnQ0wJYF0dbos+E7lRSdniDf/Ul9rF4WAzAEoQYau8pe +eIPlJy8rVrDEgqfCQabYXrLaG68EHHMGadY2EX0yyI/SZh9AU8RdatNHBwj42LGP +9dp3fyEv14usJPGuLVy+8I7TMckQPpPB+NLFECJMwRRfciPjibw1MMSYTOWnAgMB +AAGjggvZMIIL1TAOBgNVHQ8BAf8EBAMCBaAwgYoGCCsGAQUFBwEBBH4wfDBCBggr +BgEFBQcwAoY2aHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNvbS9jYWNlcnQvY2xv +dWRzc2xzaGEyZzMuY3J0MDYGCCsGAQUFBzABhipodHRwOi8vb2NzcDIuZ2xvYmFs +c2lnbi5jb20vY2xvdWRzc2xzaGEyZzMwVgYDVR0gBE8wTTBBBgkrBgEEAaAyARQw +NDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3Np +dG9yeS8wCAYGZ4EMAQICMAkGA1UdEwQCMAAwgglrBgNVHREEggliMIIJXoIbcjIu +c2hhcmVkLmdsb2JhbC5mYXN0bHkubmV0ghEqLmFtcGlmeW11c2ljLmNvbYIPKi5h +cGkuZ2lwaHkuY29tghAqLmFwcC5yb213b2QuY29tghAqLmF3YXl0cmF2ZWwuY29t +ghIqLmJmaWZsYXJlbGl2ZS5jb22CECouYmlsbGluZ2FybS5jb22CCiouYnJhemUu +ZXWCFSouY2FsZ2FyeXN0YW1wZWRlLmNvbYIQKi5jZG4udHJpbGxlci5jb4INKi5j +aXR5bWFwcy5pb4IOKi5kZWFsZXJvbi5jb22CDSouZG92ZW1lZC5jb22CDCouZWxh +c3RpYy5jb4IPKi5mZmhhbmRiYWxsLmZyghEqLmZsZXhzaG9wcGVyLmNvbYIPKi5m +bGlwcC1hZHMuY29tghcqLmZsb3JpZGFldmVyYmxhZGVzLmNvbYIYKi5mb2N1c3Jp +dGUtbm92YXRpb24uY29tghAqLmZyZXNoYm9va3MuY29tggsqLmdpcGh5LmNvbYIV +Ki5pZGFob3N0ZWVsaGVhZHMuY29tghAqLmludGVyYWN0bm93LnR2ghEqLmtjbWF2 +ZXJpY2tzLmNvbYIMKi5rb21ldHMuY29tghEqLm1lZGlhLmdpcGh5LmNvbYIKKi5t +bnRkLm5ldIIMKi5uYXNjYXIuY29tghUqLm9tbmlnb25wcm9zdWl0ZS5jb22CHSou +b3JsYW5kb3NvbGFyYmVhcnNob2NrZXkuY29tggwqLnByZWlzMjQuZGWCDSoucWEu +bW50ZC5uZXSCEyoucmV2ZXJiLWFzc2V0cy5jb22CDCoucmV2ZXJiLmNvbYIMKi5y +b213b2QuY29tghMqLnNjb290ZXJsb3VuZ2UuY29tghgqLnN0YWdpbmcuYmlsbGlu +Z2Vudi5jb22CFiouc3RhZ2luZy5mcmVzaGVudi5jb22CEiouc3dhbXByYWJiaXRz +LmNvbYILKi52ZXJzZS5jb22CDSoudmlkeWFyZC5jb22CDioudmlld2VkaXQuY29t +ghEqLnZvdGVub3cubmJjLmNvbYIMKi52b3Rlbm93LnR2ggsqLndheWluLmNvbYIb +Ki53ZXN0bWluc3Rlcmtlbm5lbGNsdWIub3Jngg9hbXBpZnltdXNpYy5jb22CE2Fw +aS5yZXZlcmJzaXRlcy5jb22CGGFwaS5zdGFnaW5nLmZyZXNoZW52LmNvbYIbYXBp +LnN0YWdpbmcucmV2ZXJic2l0ZXMuY29tgg5hd2F5dHJhdmVsLmNvbYIQYmZpZmxh +cmVsaXZlLmNvbYITYmZsLXRlc3QuYWJjLmdvLmNvbYIOYmZsLmFiYy5nby5jb22C +CGJyYXplLmV1gh5jZG4taW1hZ2VzLmZsaXBwZW50ZXJwcmlzZS5uZXSCF2Nkbi5m +bGlwcGVudGVycHJpc2UubmV0ghJjb3Ntb3NtYWdhemluZS5jb22CDGRlYWxlcm9u +LmNvbYILZG92ZW1lZC5jb22CHWR3dHN2b3RlLWxpdmUtdGVzdC5hYmMuZ28uY29t +ghhkd3Rzdm90ZS1saXZlLmFiYy5nby5jb22CGGR3dHN2b3RlLXRlc3QuYWJjLmdv +LmNvbYITZHd0c3ZvdGUuYWJjLmdvLmNvbYIKZWxhc3RpYy5jb4IMZW1haWwua2du +LmlvghJmLmNsb3VkLmdpdGh1Yi5jb22CHWZhbmJvb3N0LXRlc3QuZmlhZm9ybXVs +YWUuY29tghhmYW5ib29zdC5maWFmb3JtdWxhZS5jb22CDWZmaGFuZGJhbGwuZnKC +D2ZsZXhzaG9wcGVyLmNvbYIVZmxvcmlkYWV2ZXJibGFkZXMuY29tgglnaXBoeS5j +b22CFWdvLmNvbmNhY2FmbGVhZ3VlLmNvbYIcZ28uY29uY2FjYWZuYXRpb25zbGVh +Z3VlLmNvbYIGZ3BoLmlzghNpZGFob3N0ZWVsaGVhZHMuY29tghNpZG9sdm90ZS5h +YmMuZ28uY29tgg1pbmZyb250LnNwb3J0gg5pbnRlcmFjdG5vdy50doIPa2NtYXZl +cmlja3MuY29tggprb21ldHMuY29tghptYWlsLmRldmVsb3BtZW50LmJyYXplLmNv +bYIWbWFuY2hlc3Rlcm1vbmFyY2hzLmNvbYIWbWVkaWEud29ya2FuZG1vbmV5LmNv +bYIXbXkuc3RhZ2luZy5mcmVzaGVudi5jb22CG29ybGFuZG9zb2xhcmJlYXJzaG9j +a2V5LmNvbYIUcGNhLXRlc3QuZW9ubGluZS5jb22CD3BjYS5lb25saW5lLmNvbYIh +cGxmcGwtZmFzdGx5LnN0YWdpbmcuaXNtZ2FtZXMuY29tggpwcmVpczI0LmRlghRw +cmVtaWVyZXNwZWFrZXJzLmNvbYILcWEudGVub3IuY2+CDHFhLnRlbm9yLmNvbYIe +cm9ib3RpYy1jb29rLnNlY3JldGNkbi1zdGcubmV0ghFzY29vdGVybG91bmdlLmNv +bYIac3RhZ2luZy13d3cuZWFzYS5ldXJvcGEuZXWCGHN0YWdpbmcuZGFpbHkuc3F1 +aXJ0Lm9yZ4IUc3RhZ2luZy5mcmVzaGVudi5jb22CEHN3YW1wcmFiYml0cy5jb22C +CHRlbm9yLmNvggl0ZW5vci5jb22CFnRyYWNrLnN3ZWVuZXktbWFpbC5jb22CEHVh +dC5mcmVzaGVudi5jb22CE3VuaWZvcm1zaW5zdG9jay5jb22CF3VzZXJzLnByZW1p +ZXJsZWFndWUuY29tghF1dGFoZ3JpenpsaWVzLmNvbYIJdmVyc2UuY29tggt2aWR5 +YXJkLmNvbYIMdmlld2VkaXQuY29tggp2b3Rlbm93LnR2ggl3YXlpbi5jb22CGXdl +c3RtaW5zdGVya2VubmVsY2x1Yi5vcmeCEXd3dy5jaGlxdWVsbGUuY29tghB3d3cu +Y2hpcXVlbGxlLnNlghJ3d3cuZWFzYS5ldXJvcGEuZXWCGnd3dy5pc3JhZWxuYXRp +b25hbG5ld3MuY29tghh3d3cua29nYW5pbnRlcm5ldC5jb20uYXWCDHd3dy50ZW5v +ci5jb4INd3d3LnRlbm9yLmNvbYIUd3d3LnVhdC5mcmVzaGVudi5jb22CF3d3dy51 +bmlmb3Jtc2luc3RvY2suY29tghV3d3cudXRhaGdyaXp6bGllcy5jb20wHQYDVR0l +BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFKkrh+HOJEc7G7/P +hTcCVZ0NlFjmMB0GA1UdDgQWBBQ7SJi8MbyN4XPx+T1QVj4sLHjhDjCCAQMGCisG +AQQB1nkCBAIEgfQEgfEA7wB1AId1v+dZfPiMQ5lfvfNu/1aNR1Y2/0q1YMG06v9e +oIMPAAABbJgVTmEAAAQDAEYwRAIgeYcRKQDCMIBnswrwBvmmSpCFWhjGl+zabCpo +E3R9nJcCIBaAx/TYKESO7iz+hU6bq7Dwzo0QpTIvho4ZdFfSAAHMAHYAsh4FzIui +zYogTodm+Su5iiUgZ2va+nDnsklTLe+LkF4AAAFsmBVLIQAABAMARzBFAiAfLUaq +ukt75a1pySCxrreQ/+/IAdyOSqXqbH1tZNKlTAIhALlcthwbBCfSSNEjTeJWOXss +clzGt9zAk256uboF0iFLMA0GCSqGSIb3DQEBCwUAA4IBAQCZXc5cmMCeqIVsRnRH +KsuGlT6tP2NdsK1+b9dJguP0zbQoxLg5qBMjRGjDo8BpGOni5mJmRJYDQ/GHKP/d +bd+n/4BDD5jI5/rtl43D+Y1G3S5tCRX/3s+At1LJcuaVRmvnywfE9OLXpI84SWtU +AainsxdCYcvopTOZG9UwkjyuEBV3tVsiQkhRSAzYStM75caRWer2pP7i3AwKNv29 +DDSHahXxUyjgAbD2XQojODT/AltEvuqcSrB2cRGXultLmJXFNDEQ5Om4GcjAk75D +pzNLvZuaXHwWoYdm+YTwdPwuZhWe9TxMYlpZbQR8dux2QXRfARF07Vi0+gOzPE9V +RG7L +-----END CERTIFICATE-----` + +var elasticChainCert = `-----BEGIN CERTIFICATE----- +MIIEizCCA3OgAwIBAgIORvCM288sVGbvMwHdXzQwDQYJKoZIhvcNAQELBQAwVzEL +MAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsT +B1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNTA4MTkw +MDAwMDBaFw0yNTA4MTkwMDAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBH +bG9iYWxTaWduIG52LXNhMS0wKwYDVQQDEyRHbG9iYWxTaWduIENsb3VkU1NMIENB +IC0gU0hBMjU2IC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj +wHXhMpjl2a6EfI3oI19GlVtMoiVw15AEhYDJtfSKZU2Sy6XEQqC2eSUx7fGFIM0T +UT1nrJdNaJszhlyzey2q33egYdH1PPua/NPVlMrJHoAbkJDIrI32YBecMbjFYaLi +blclCG8kmZnPlL/Hi2uwH8oU+hibbBB8mSvaSmPlsk7C/T4QC0j0dwsv8JZLOu69 +Nd6FjdoTDs4BxHHT03fFCKZgOSWnJ2lcg9FvdnjuxURbRb0pO+LGCQ+ivivc41za +Wm+O58kHa36hwFOVgongeFxyqGy+Z2ur5zPZh/L4XCf09io7h+/awkfav6zrJ2R7 +TFPrNOEvmyBNVBJrfSi9AgMBAAGjggFTMIIBTzAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAw +HQYDVR0OBBYEFKkrh+HOJEc7G7/PhTcCVZ0NlFjmMB8GA1UdIwQYMBaAFGB7ZhpF +DZfKiVAvfQTNNKj//P1LMD0GCCsGAQUFBwEBBDEwLzAtBggrBgEFBQcwAYYhaHR0 +cDovL29jc3AuZ2xvYmFsc2lnbi5jb20vcm9vdHIxMDMGA1UdHwQsMCowKKAmoCSG +Imh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC5jcmwwVgYDVR0gBE8wTTAL +BgkrBgEEAaAyARQwPgYGZ4EMAQICMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3 +Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEBCwUAA4IBAQCi +HWmKCo7EFIMqKhJNOSeQTvCNrNKWYkc2XpLR+sWTtTcHZSnS9FNQa8n0/jT13bgd ++vzcFKxWlCecQqoETbftWNmZ0knmIC/Tp3e4Koka76fPhi3WU+kLk5xOq9lF7qSE +hf805A7Au6XOX5WJhXCqwV3szyvT2YPfA8qBpwIyt3dhECVO2XTz2XmCtSZwtFK8 +jzPXiq4Z0PySrS+6PKBIWEde/SBWlSDBch2rZpmk1Xg3SBufskw3Z3r9QtLTVp7T +HY7EDGiWtkdREPd76xUJZPX58GMWLT3fI0I6k2PMq69PVwbH/hRVYs4nERnh9ELt +IjBrNRpKBYCkZd/My2/Q +-----END CERTIFICATE-----` diff --git a/heartbeat/monitors/active/fixtures/expired.cert b/heartbeat/monitors/active/fixtures/expired.cert new file mode 100644 index 00000000000..e39ad893bd6 --- /dev/null +++ b/heartbeat/monitors/active/fixtures/expired.cert @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3zCCAsegAwIBAgIUS+ahW2wxDZ1bT/qYnenS8jrXUcAwDQYJKoZIhvcNAQEL +BQAwfzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1OMRQwEgYDVQQHDAtNaW5uZWFw +b2xpczEVMBMGA1UECgwMRWxhc3RpYywgSW5jMRQwEgYDVQQLDAtFbmdpbmVlcmlu +ZzEgMB4GA1UEAwwXZXhwaXJlZHRlc3QuZXhhbXBsZS5uZXQwHhcNMjAwNDIxMTQw +MDE0WhcNMjAwNDIyMTQwMDE0WjB/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTU4x +FDASBgNVBAcMC01pbm5lYXBvbGlzMRUwEwYDVQQKDAxFbGFzdGljLCBJbmMxFDAS +BgNVBAsMC0VuZ2luZWVyaW5nMSAwHgYDVQQDDBdleHBpcmVkdGVzdC5leGFtcGxl +Lm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKh1iS5EZ7bDSKgW +R3JXAepMIaEewMSdbaoBtuNQb48XJGwI0mudF983a7JxGCSfw9mhVYa4YsSv79UE +XomGrWVrS01Cmf1VRIOmxevWMPhvnE6UH+5VxKUBk5ooNSty4iHkDFy2i5WWjxiv +de6Xqnn/dVQhuT/sW+rU/grCsGcdUwqsWnC547ekqiYRTtyZrdh+U0KRKqy5iBlH +9Woua+CnXmsD7+4MgGekErg9XLRHYveLOmLucbNlAIlRyfMDZL1RlXufcGwhzItz +JNM9N0NJ5bwrpuP0RYlYbbMYal+b1Tn2e8qkMm88hniQkuu69kUpKeewIOr62vIK +tI273GECAwEAAaNTMFEwHQYDVR0OBBYEFKgd6wQcgIdUSjtJREObD+R3q3MPMB8G +A1UdIwQYMBaAFKgd6wQcgIdUSjtJREObD+R3q3MPMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggEBADkBqmCUcvVTqu5IIZ5PLz40jdg2luaDHEA6I2Ga +1ioabETfQhXeaNJflojYm0Bzsy2aneVLGM2KaZ76wN0yvib3MZ4miu4C/mDsR3bB +wq7/CAK2AcJXv1jk0vIrK6DhZfA2HaelBkQ8UHwWK7AO+JmS6jozIt1vySwPI1E7 +lMFWbs3bmsSmunj3+66XS2XguUKzFwUIAEOfsPFqT2OMsPIa7weUWuCV/zMi7fuB +HbgVouYvMTve8wx7+ozDk6CyvlRlx20xwdOvXaH3JILw7gTQWcAEWZLcB2ct1Zks +UTtbIAjBV6s0Pm/2/6MxxkDCVVUpwXiiKBRkHxzkgoH7TQw= +-----END CERTIFICATE----- diff --git a/heartbeat/monitors/active/fixtures/expired.key b/heartbeat/monitors/active/fixtures/expired.key new file mode 100644 index 00000000000..2a11440f7aa --- /dev/null +++ b/heartbeat/monitors/active/fixtures/expired.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCodYkuRGe2w0io +FkdyVwHqTCGhHsDEnW2qAbbjUG+PFyRsCNJrnRffN2uycRgkn8PZoVWGuGLEr+/V +BF6Jhq1la0tNQpn9VUSDpsXr1jD4b5xOlB/uVcSlAZOaKDUrcuIh5AxctouVlo8Y +r3Xul6p5/3VUIbk/7Fvq1P4KwrBnHVMKrFpwueO3pKomEU7cma3YflNCkSqsuYgZ +R/VqLmvgp15rA+/uDIBnpBK4PVy0R2L3izpi7nGzZQCJUcnzA2S9UZV7n3BsIcyL +cyTTPTdDSeW8K6bj9EWJWG2zGGpfm9U59nvKpDJvPIZ4kJLruvZFKSnnsCDq+try +CrSNu9xhAgMBAAECggEBAIc32QYvWESmWeK6B11rI5lqxK+snLT1XLpSp/esb++e +dtjU9/nzXd8JgEP6bZOwPiepTZpW1MjmJA+Lc0rWtMYsqoP4ityDHfzC2CmmgyZX +iFK2qS7I35BHRLA/x/X5QDRN9fJRgJdxA6mf5Xy/dtJ4UDhY3XbHBTzo/IWsoqYQ +4V3WBQYMGlhBArCoOx07pwc9NMTnXwpfe4rUdm3EaGGpe/9JT08JcTyFZfFUeFT1 +lfSYo5i+xPOCQ/FcC5GfWdciyY0c8ej8iwdxZb0kPI4hBu36+D6zD+YoNoC3CQTb +MecRFQ0MeTTuUMCdzFWtg+2FWnJucaLiaK9fKbVzi7UCgYEA0BAlfUdXdeDYMlW3 +2ReeOgH32bchPYwn2UvHYkIhhDp40STVw3BYQ0Zj9yJQXLFaoY1SFhwRJR1kpbSd +IfME/IzR/oMFvRUNQEPQZVH0Mg9FWIXLdXlV4qbU3AyA2r4x+VUCt3jp1n/5rG7g +cmoKBdCXNUAhK30bRGTdXB06Fp8CgYEAz0V+IlkGyDKcyCkja0ypA3AdSod/43az +7HMS3nf32hOFpgQuEtVYZc3NW/rdJFPksnRd6+RlD2nIoHZEa+adl2gESjGH2asw +nhxP/Pr4m8PGZF5BwdILRTVFukf5yrM6g63FgrgA9d+QdCsqoqrctItRyCgcfpL4 +XYXEKVWELP8CgYATxbUKVsFE/n0NK4AxLUFoGc/H7NNH2g3fZIgqGka9HiFlHq8B +x6dbnVDap3QjmucV+ywV1cz7TfPGm4djNoj+xxMdsK3W7i04MjmXp1Yhe7oHU4+m +NkWnKFuKHdYQ84okO6Pqc58lNzwu2sqRlOom60+zS8jbLSRuN3ehzVU72QKBgGm0 +qCo+Ou44maqfCFg9hWiicd3Dkt5feE0bNsFMb5PBJwTO1ux175ojxhqlqshPHLBC +FnAqT7v3mAD1r9lTiIVh3+YysnS5EJdiGw0KtWVDB9fCFkkRpPvLul7RPDw7AZmM +MtGCo8LBHHuSVDEXcG2HK9MnWbjXnWCcyrjFyx3jAoGAYsNGYm+OBr16NNsPtx3S +nRQJz9wqB2mIqNU8rRSjd5EUp03jhHiTEN9DT6iEnLGaTDBUgD2RlPvEVGk1N7FT +nh9tLtg2ytWIC/P+QrKwzdUUa00MSswTxRS3Cmy459UbLBiPgHBJ2h1G7gsiHPOt +erJWqYJ8DXvLzCPdMVzQxj8= +-----END PRIVATE KEY----- diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go index 2db563d0044..86e55b4536c 100644 --- a/heartbeat/monitors/active/http/http_test.go +++ b/heartbeat/monitors/active/http/http_test.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "fmt" "io/ioutil" + "net" "net/http" "net/http/httptest" "net/url" @@ -48,13 +49,13 @@ import ( "github.com/elastic/go-lookslike/validator" ) -func testRequest(t *testing.T, testURL string, useUrls bool) *beat.Event { - return testTLSRequest(t, testURL, useUrls, nil) +func sendSimpleTLSRequest(t *testing.T, testURL string, useUrls bool) *beat.Event { + return sendTLSRequest(t, testURL, useUrls, nil) } -// testTLSRequest tests the given request. certPath is optional, if given +// sendTLSRequest tests the given request. certPath is optional, if given // an empty string no cert will be set. -func testTLSRequest(t *testing.T, testURL string, useUrls bool, extraConfig map[string]interface{}) *beat.Event { +func sendTLSRequest(t *testing.T, testURL string, useUrls bool, extraConfig map[string]interface{}) *beat.Event { configSrc := map[string]interface{}{ "timeout": "1s", } @@ -92,7 +93,7 @@ func testTLSRequest(t *testing.T, testURL string, useUrls bool, extraConfig map[ func checkServer(t *testing.T, handlerFunc http.HandlerFunc, useUrls bool) (*httptest.Server, *beat.Event) { server := httptest.NewServer(handlerFunc) defer server.Close() - event := testRequest(t, server.URL, useUrls) + event := sendSimpleTLSRequest(t, server.URL, useUrls) return server, event } @@ -220,13 +221,6 @@ var downStatuses = []int{ http.StatusNetworkAuthenticationRequired, } -func serverHostname(t *testing.T, server *httptest.Server) string { - surl, err := url.Parse(server.URL) - require.NoError(t, err) - - return surl.Hostname() -} - func TestUpStatuses(t *testing.T) { for _, status := range upStatuses { status := status @@ -347,7 +341,7 @@ func runHTTPSServerCheck( // we give it a few attempts to see if the server can come up before we run the real assertions. var event *beat.Event for i := 0; i < 10; i++ { - event = testTLSRequest(t, server.URL, false, mergedExtraConfig) + event = sendTLSRequest(t, server.URL, false, mergedExtraConfig) if v, err := event.GetValue("monitor.status"); err == nil && reflect.DeepEqual(v, "up") { break } @@ -373,6 +367,30 @@ func TestHTTPSServer(t *testing.T) { runHTTPSServerCheck(t, server, nil) } +func TestExpiredHTTPSServer(t *testing.T) { + tlsCert, err := tls.LoadX509KeyPair("../fixtures/expired.cert", "../fixtures/expired.key") + require.NoError(t, err) + host, port, cert, closeSrv := hbtest.StartHTTPSServer(t, tlsCert) + defer closeSrv() + u := &url.URL{Scheme: "https", Host: net.JoinHostPort(host, port)} + + extraConfig := map[string]interface{}{"ssl.certificate_authorities": "../fixtures/expired.cert"} + event := sendTLSRequest(t, u.String(), true, extraConfig) + + testslike.Test( + t, + lookslike.Strict(lookslike.Compose( + hbtest.BaseChecks("127.0.0.1", "down", "http"), + hbtest.RespondingTCPChecks(), + hbtest.SummaryChecks(0, 1), + hbtest.ExpiredCertChecks(cert), + hbtest.URLChecks(t, &url.URL{Scheme: "https", Host: net.JoinHostPort(host, port)}), + // No HTTP fields expected because we fail at the TCP level + )), + event.Fields, + ) +} + func TestHTTPSx509Auth(t *testing.T) { wd, err := os.Getwd() require.NoError(t, err) @@ -418,7 +436,7 @@ func TestConnRefusedJob(t *testing.T) { url := fmt.Sprintf("http://%s:%d", ip, port) - event := testRequest(t, url, false) + event := sendSimpleTLSRequest(t, url, false) testslike.Test( t, @@ -440,7 +458,7 @@ func TestUnreachableJob(t *testing.T) { port := uint16(1234) url := fmt.Sprintf("http://%s:%d", ip, port) - event := testRequest(t, url, false) + event := sendSimpleTLSRequest(t, url, false) testslike.Test( t, diff --git a/heartbeat/monitors/active/http/task.go b/heartbeat/monitors/active/http/task.go index 65d2f1ae62c..2c227c1d89a 100644 --- a/heartbeat/monitors/active/http/task.go +++ b/heartbeat/monitors/active/http/task.go @@ -20,15 +20,19 @@ package http import ( "bytes" "context" + "crypto/x509" "fmt" "io/ioutil" "net" "net/http" + "net/url" "strconv" "strings" "sync" "time" + "github.com/elastic/beats/v7/heartbeat/monitors/active/dialchain/tlsmeta" + "github.com/elastic/beats/v7/heartbeat/eventext" "github.com/elastic/beats/v7/heartbeat/look" "github.com/elastic/beats/v7/heartbeat/monitors" @@ -232,11 +236,16 @@ func execPing( // Send the HTTP request. We don't immediately return on error since // we may want to add additional fields to contextualize the error. start, resp, errReason := execRequest(client, req) - // If we have no response object or an error was set there probably was an IO error, we can skip the rest of the logic // since that logic is for adding metadata relating to completed HTTP transactions that have errored // in other ways if resp == nil || errReason != nil { + if urlErr, ok := errReason.Unwrap().(*url.Error); ok { + if certErr, ok := urlErr.Err.(x509.CertificateInvalidError); ok { + tlsmeta.AddCertMetadata(event.Fields, []*x509.Certificate{certErr.Cert}) + } + } + return start, time.Now(), errReason } diff --git a/heartbeat/monitors/active/icmp/icmp.go b/heartbeat/monitors/active/icmp/icmp.go index 1cb19c90798..45fdf8a54b3 100644 --- a/heartbeat/monitors/active/icmp/icmp.go +++ b/heartbeat/monitors/active/icmp/icmp.go @@ -32,46 +32,71 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" ) +var debugf = logp.MakeDebug("icmp") + func init() { monitors.RegisterActive("icmp", create) } -var debugf = logp.MakeDebug("icmp") - func create( name string, - cfg *common.Config, + commonConfig *common.Config, ) (jobs []jobs.Job, endpoints int, err error) { + loop, err := getStdLoop() + if err != nil { + logp.Warn("Failed to initialize ICMP loop %v", err) + return nil, 0, err + } + config := DefaultConfig - if err := cfg.Unpack(&config); err != nil { + if err := commonConfig.Unpack(&config); err != nil { return nil, 0, err } - ipVersion := config.Mode.Network() - if len(config.Hosts) > 0 && ipVersion == "" { - err := fmt.Errorf("pinging hosts requires ipv4 or ipv6 mode enabled") + jf, err := newJobFactory(config, monitors.NewStdResolver(), loop) + if err != nil { return nil, 0, err } + return jf.makeJobs() - var loopErr error - loopInit.Do(func() { - debugf("initializing ICMP loop") - loop, loopErr = newICMPLoop() - }) - if loopErr != nil { - logp.Warn("Failed to initialize ICMP loop %v", loopErr) - return nil, 0, loopErr +} + +type jobFactory struct { + config Config + resolver monitors.Resolver + loop ICMPLoop + ipVersion string +} + +func newJobFactory(config Config, resolver monitors.Resolver, loop ICMPLoop) (*jobFactory, error) { + jf := &jobFactory{config: config, resolver: resolver, loop: loop} + err := jf.checkConfig() + if err != nil { + return nil, err } - debugf("ICMP loop successfully initialized") - if err := loop.checkNetworkMode(ipVersion); err != nil { + return jf, nil +} + +func (jf *jobFactory) checkConfig() error { + jf.ipVersion = jf.config.Mode.Network() + if len(jf.config.Hosts) > 0 && jf.ipVersion == "" { + err := fmt.Errorf("pinging hosts requires ipv4 or ipv6 mode enabled") + return err + } + + return nil +} + +func (jf *jobFactory) makeJobs() (j []jobs.Job, endpoints int, err error) { + if err := jf.loop.checkNetworkMode(jf.ipVersion); err != nil { return nil, 0, err } - pingFactory := monitors.MakePingIPFactory(createPingIPFactory(&config)) + pingFactory := jf.pingIPFactory(&jf.config) - for _, host := range config.Hosts { - job, err := monitors.MakeByHostJob(host, config.Mode, monitors.NewStdResolver(), pingFactory) + for _, host := range jf.config.Hosts { + job, err := monitors.MakeByHostJob(host, jf.config.Mode, monitors.NewStdResolver(), pingFactory) if err != nil { return nil, 0, err @@ -82,15 +107,15 @@ func create( return nil, 0, err } - jobs = append(jobs, wrappers.WithURLField(u, job)) + j = append(j, wrappers.WithURLField(u, job)) } - return jobs, len(config.Hosts), nil + return j, len(jf.config.Hosts), nil } -func createPingIPFactory(config *Config) func(*beat.Event, *net.IPAddr) error { - return func(event *beat.Event, ip *net.IPAddr) error { - rtt, n, err := loop.ping(ip, config.Timeout, config.Wait) +func (jf *jobFactory) pingIPFactory(config *Config) func(*net.IPAddr) jobs.Job { + return monitors.MakePingIPFactory(func(event *beat.Event, ip *net.IPAddr) error { + rtt, n, err := jf.loop.ping(ip, config.Timeout, config.Wait) if err != nil { return err } @@ -98,9 +123,9 @@ func createPingIPFactory(config *Config) func(*beat.Event, *net.IPAddr) error { icmpFields := common.MapStr{"requests": n} if err == nil { icmpFields["rtt"] = look.RTT(rtt) - eventext.MergeEventFields(event, icmpFields) + eventext.MergeEventFields(event, common.MapStr{"icmp": icmpFields}) } return nil - } + }) } diff --git a/heartbeat/monitors/active/icmp/icmp_test.go b/heartbeat/monitors/active/icmp/icmp_test.go new file mode 100644 index 00000000000..11e7dae5380 --- /dev/null +++ b/heartbeat/monitors/active/icmp/icmp_test.go @@ -0,0 +1,90 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package icmp + +import ( + "net" + "net/url" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/heartbeat/hbtest" + "github.com/elastic/beats/v7/heartbeat/look" + "github.com/elastic/beats/v7/heartbeat/monitors" + "github.com/elastic/beats/v7/heartbeat/monitors/wrappers" + "github.com/elastic/beats/v7/heartbeat/scheduler/schedule" + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/go-lookslike" + "github.com/elastic/go-lookslike/testslike" +) + +func TestICMPFields(t *testing.T) { + host := "localhost" + hostURL := &url.URL{Scheme: "icmp", Host: host} + ip := "127.0.0.1" + cfg := Config{ + Hosts: []string{host}, + Mode: monitors.IPSettings{IPv4: true, IPv6: false, Mode: monitors.PingAny}, + } + testMockLoop, e := execTestICMPCheck(t, cfg) + + validator := lookslike.Strict( + lookslike.Compose( + hbtest.BaseChecks(ip, "up", "icmp"), + hbtest.SummaryChecks(1, 0), + hbtest.URLChecks(t, hostURL), + hbtest.ResolveChecks(ip), + lookslike.MustCompile(map[string]interface{}{ + "icmp.requests": 1, + "icmp.rtt": look.RTT(testMockLoop.pingRtt), + }), + ), + ) + testslike.Test(t, validator, e.Fields) +} + +func execTestICMPCheck(t *testing.T, cfg Config) (mockLoop, *beat.Event) { + tl := mockLoop{pingRtt: time.Microsecond * 1000, pingRequests: 1} + jf, err := newJobFactory(cfg, monitors.NewStdResolver(), tl) + require.NoError(t, err) + j, endpoints, err := jf.makeJobs() + require.Len(t, j, 1) + require.Equal(t, 1, endpoints) + e := &beat.Event{} + sched, _ := schedule.Parse("@every 1s") + wrapped := wrappers.WrapCommon(j, "test", "", "icmp", sched, time.Duration(0)) + wrapped[0](e) + return tl, e +} + +type mockLoop struct { + pingRtt time.Duration + pingRequests int + pingErr error + checkNetworkModeErr error +} + +func (t mockLoop) checkNetworkMode(mode string) error { + return t.checkNetworkModeErr +} + +func (t mockLoop) ping(addr *net.IPAddr, timeout time.Duration, interval time.Duration) (time.Duration, int, error) { + return t.pingRtt, t.pingRequests, t.pingErr +} diff --git a/heartbeat/monitors/active/icmp/loop.go b/heartbeat/monitors/active/icmp/loop.go index 414686c1d31..de4d0ef4dfc 100644 --- a/heartbeat/monitors/active/icmp/loop.go +++ b/heartbeat/monitors/active/icmp/loop.go @@ -18,371 +18,15 @@ package icmp import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "math/rand" "net" - "os" - "runtime" - "sync" "time" - - "golang.org/x/net/icmp" - "golang.org/x/net/ipv4" - "golang.org/x/net/ipv6" -) - -type icmpLoop struct { - conn4, conn6 *icmp.PacketConn - recv chan packet - - mutex sync.Mutex - requests map[requestID]*requestContext -} - -type timeoutError struct { -} - -const ( - // iana types - protocolICMP = 1 - protocolIPv6ICMP = 58 -) - -type packet struct { - ts time.Time - addr net.Addr - - Type icmp.Type // type, either ipv4.ICMPType or ipv6.ICMPType - Code int // code - Checksum int // checksum - Echo icmp.Echo -} - -type requestID struct { - addr string - proto int - id int - seq int -} - -type requestContext struct { - l *icmpLoop - id requestID - ts time.Time - result chan requestResult -} - -type requestResult struct { - packet packet - err error -} - -var ( - loopInit sync.Once - loop *icmpLoop ) -func noPingCapabilityError(message string) error { - return fmt.Errorf(fmt.Sprintf("Insufficient privileges to perform ICMP ping. %s", message)) -} - -func newICMPLoop() (*icmpLoop, error) { - // Log errors at info level, as the loop is setup globally when ICMP module is loaded - // first (not yet configured). - // With multiple configurations using the icmp loop, we have to postpose - // IPv4/IPv6 checking - conn4 := createListener("IPv4", "ip4:icmp") - conn6 := createListener("IPv6", "ip6:ipv6-icmp") - unprivilegedPossible := false - l := &icmpLoop{ - conn4: conn4, - conn6: conn6, - recv: make(chan packet, 16), - requests: map[requestID]*requestContext{}, - } - - if l.conn4 == nil && l.conn6 == nil { - switch runtime.GOOS { - case "linux", "darwin": - unprivilegedPossible = true - //This is non-privileged ICMP, not udp - l.conn4 = createListener("Unprivileged IPv4", "udp4") - l.conn6 = createListener("Unprivileged IPv6", "udp6") - } - } - - if l.conn4 != nil { - go l.runICMPRecv(l.conn4, protocolICMP) - } - if l.conn6 != nil { - go l.runICMPRecv(l.conn6, protocolIPv6ICMP) - } - - if l.conn4 == nil && l.conn6 == nil { - if unprivilegedPossible { - var buffer bytes.Buffer - path, _ := os.Executable() - buffer.WriteString("You can run without root by setting cap_net_raw:\n sudo setcap cap_net_raw+eip ") - buffer.WriteString(path + " \n") - buffer.WriteString("Your system allows the use of unprivileged ping by setting net.ipv4.ping_group_range \n sysctl -w net.ipv4.ping_group_range=' ' ") - return nil, noPingCapabilityError(buffer.String()) - } - return nil, noPingCapabilityError("You must provide the appropriate permissions to this executable") - } - - return l, nil -} - -func (l *icmpLoop) checkNetworkMode(mode string) error { - ip4, ip6 := false, false - switch mode { - case "ip4": - ip4 = true - case "ip6": - ip6 = true - case "ip": - ip4, ip6 = true, true - default: - return fmt.Errorf("'%v' is not supported", mode) - } - - if ip4 && l.conn4 == nil { - return errors.New("failed to initiate IPv4 support. Check log details for permission configuration") - } - if ip6 && l.conn6 == nil { - return errors.New("failed to initiate IPv6 support. Check log details for permission configuration") - } - - return nil -} - -func (l *icmpLoop) runICMPRecv(conn *icmp.PacketConn, proto int) { - for { - bytes := make([]byte, 512) - conn.SetReadDeadline(time.Now().Add(1 * time.Second)) - _, addr, err := conn.ReadFrom(bytes) - if err != nil { - if neterr, ok := err.(*net.OpError); ok { - if neterr.Timeout() { - continue - } else { - // TODO: report error and quit loop? - return - } - } - } - - ts := time.Now() - m, err := icmp.ParseMessage(proto, bytes) - if err != nil { - continue - } - - // process echo reply only - if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply { - continue - } - echo, ok := m.Body.(*icmp.Echo) - if !ok { - continue - } - - id := requestID{ - addr: addr.String(), - proto: proto, - id: echo.ID, - seq: echo.Seq, - } - - l.mutex.Lock() - ctx := l.requests[id] - if ctx != nil { - delete(l.requests, id) - } - l.mutex.Unlock() - - // no return context available for echo reply -> handle next message - if ctx == nil { - continue - } - - ctx.result <- requestResult{ - packet: packet{ - ts: ts, - addr: addr, - - Type: m.Type, - Code: m.Code, - Checksum: m.Checksum, - Echo: *echo, - }, - } - } -} - -func (l *icmpLoop) ping( - addr *net.IPAddr, - timeout time.Duration, - interval time.Duration, -) (time.Duration, int, error) { - - var err error - toTimer := time.NewTimer(timeout) - defer toTimer.Stop() - - ticker := time.NewTicker(interval) - defer ticker.Stop() - - done := false - doneSignal := make(chan struct{}) - - success := false - var rtt time.Duration - - // results accepts first response received only - results := make(chan time.Duration, 1) - requests := 0 - - awaitResponse := func(ctx *requestContext) { - select { - case <-doneSignal: - ctx.Stop() - - case r := <-ctx.result: - // ctx is removed from request tables automatically a response is - // received. No need to stop it. - - // try to push RTT. The first result available will be reported - select { - case results <- r.packet.ts.Sub(ctx.ts): - default: - } - } - } - - for !done { - var ctx *requestContext - ctx, err = l.sendEchoRequest(addr) - if err != nil { - close(doneSignal) - break - } - go awaitResponse(ctx) - requests++ - - select { - case <-toTimer.C: - // no response for any active request received. Finish loop - // and remove all requests from request table. - done = true - close(doneSignal) - - case <-ticker.C: - // No response yet. Send another request with every tick - - case rtt = <-results: - success = true - - done = true - close(doneSignal) - } - } - - if err != nil { - return 0, 0, err - } - - if !success { - return 0, requests, timeoutError{} - } - - return rtt, requests, nil -} - -func (l *icmpLoop) sendEchoRequest(addr *net.IPAddr) (*requestContext, error) { - var conn *icmp.PacketConn - var proto int - var typ icmp.Type - - if l == nil { - panic("icmp loop not initialized") - } - - if isIPv4(addr.IP) { - conn = l.conn4 - proto = protocolICMP - typ = ipv4.ICMPTypeEcho - } else if isIPv6(addr.IP) { - conn = l.conn6 - proto = protocolIPv6ICMP - typ = ipv6.ICMPTypeEchoRequest - } else { - return nil, fmt.Errorf("%v is unknown ip address", addr) - } - - id := requestID{ - addr: addr.String(), - proto: proto, - id: rand.Intn(0xffff), - seq: rand.Intn(0xffff), - } - - ctx := &requestContext{ - l: l, - id: id, - result: make(chan requestResult, 1), - } - - l.mutex.Lock() - l.requests[id] = ctx - l.mutex.Unlock() - - payloadBuf := make([]byte, 0, 8) - payload := bytes.NewBuffer(payloadBuf) - ts := time.Now() - binary.Write(payload, binary.BigEndian, ts.UnixNano()) - - msg := &icmp.Message{ - Type: typ, - Body: &icmp.Echo{ - ID: id.id, - Seq: id.seq, - Data: payload.Bytes(), - }, - } - encoded, _ := msg.Marshal(nil) - - _, err := conn.WriteTo(encoded, addr) - if err != nil { - return nil, err - } - - ctx.ts = ts - return ctx, nil -} - -func createListener(name, network string) *icmp.PacketConn { - conn, err := icmp.ListenPacket(network, "") - - // XXX: need to check for conn == nil, as 'err != nil' seems always to be - // true, even if error value itself is `nil`. Checking for conn suppresses - // misleading log message. - if conn == nil && err != nil { - return nil - } - return conn -} - -// timeoutError implements net.Error interface -func (timeoutError) Error() string { return "ping timeout" } -func (timeoutError) Timeout() bool { return true } -func (timeoutError) Temporary() bool { return true } - -func (r *requestContext) Stop() { - r.l.mutex.Lock() - delete(r.l.requests, r.id) - r.l.mutex.Unlock() +type ICMPLoop interface { + checkNetworkMode(mode string) error + ping( + addr *net.IPAddr, + timeout time.Duration, + interval time.Duration, + ) (time.Duration, int, error) } diff --git a/heartbeat/monitors/active/icmp/stdloop.go b/heartbeat/monitors/active/icmp/stdloop.go new file mode 100644 index 00000000000..932154c61ad --- /dev/null +++ b/heartbeat/monitors/active/icmp/stdloop.go @@ -0,0 +1,405 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package icmp + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "math/rand" + "net" + "os" + "runtime" + "sync" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +type stdICMPLoop struct { + conn4, conn6 *icmp.PacketConn + recv chan packet + + mutex sync.Mutex + requests map[requestID]*requestContext +} + +type timeoutError struct { +} + +const ( + // iana types + protocolICMP = 1 + protocolIPv6ICMP = 58 +) + +type packet struct { + ts time.Time + addr net.Addr + + Type icmp.Type // type, either ipv4.ICMPType or ipv6.ICMPType + Code int // code + Checksum int // checksum + Echo icmp.Echo +} + +type requestID struct { + addr string + proto int + id int + seq int +} + +type requestContext struct { + l *stdICMPLoop + id requestID + ts time.Time + result chan requestResult +} + +type requestResult struct { + packet packet + err error +} + +// stdLoop is a singleton for our main ICMP loop since it doesn't +// make sense to have multiples. While having a singleton is ugly +// is mandatory for the ICMP interface in go, where all monitors +// must share a single loop. +// These vars should not be used directly, but rather getStdLoop +// should be invoked to initialize and return stdLoop. +var ( + stdICMPLoopInit sync.Once + stdICMPLoopSingleton *stdICMPLoop +) + +func getStdLoop() (*stdICMPLoop, error) { + var loopErr error + stdICMPLoopInit.Do(func() { + debugf("initializing ICMP loop") + stdICMPLoopSingleton, loopErr = newICMPLoop() + if loopErr == nil { + debugf("ICMP loop successfully initialized") + } + }) + return stdICMPLoopSingleton, loopErr +} + +func noPingCapabilityError(message string) error { + return fmt.Errorf(fmt.Sprintf("Insufficient privileges to perform ICMP ping. %s", message)) +} + +func newICMPLoop() (*stdICMPLoop, error) { + // Log errors at info level, as the loop is setup globally when ICMP module is loaded + // first (not yet configured). + // With multiple configurations using the icmp loop, we have to postpose + // IPv4/IPv6 checking + conn4 := createListener("IPv4", "ip4:icmp") + conn6 := createListener("IPv6", "ip6:ipv6-icmp") + unprivilegedPossible := false + l := &stdICMPLoop{ + conn4: conn4, + conn6: conn6, + recv: make(chan packet, 16), + requests: map[requestID]*requestContext{}, + } + + if l.conn4 == nil && l.conn6 == nil { + switch runtime.GOOS { + case "linux", "darwin": + unprivilegedPossible = true + //This is non-privileged ICMP, not udp + l.conn4 = createListener("Unprivileged IPv4", "udp4") + l.conn6 = createListener("Unprivileged IPv6", "udp6") + } + } + + if l.conn4 != nil { + go l.runICMPRecv(l.conn4, protocolICMP) + } + if l.conn6 != nil { + go l.runICMPRecv(l.conn6, protocolIPv6ICMP) + } + + if l.conn4 == nil && l.conn6 == nil { + if unprivilegedPossible { + var buffer bytes.Buffer + path, _ := os.Executable() + buffer.WriteString("You can run without root by setting cap_net_raw:\n sudo setcap cap_net_raw+eip ") + buffer.WriteString(path + " \n") + buffer.WriteString("Your system allows the use of unprivileged ping by setting net.ipv4.ping_group_range \n sysctl -w net.ipv4.ping_group_range=' ' ") + return nil, noPingCapabilityError(buffer.String()) + } + return nil, noPingCapabilityError("You must provide the appropriate permissions to this executable") + } + + return l, nil +} + +func (l *stdICMPLoop) checkNetworkMode(mode string) error { + ip4, ip6 := false, false + switch mode { + case "ip4": + ip4 = true + case "ip6": + ip6 = true + case "ip": + ip4, ip6 = true, true + default: + return fmt.Errorf("'%v' is not supported", mode) + } + + if ip4 && l.conn4 == nil { + return errors.New("failed to initiate IPv4 support. Check log details for permission configuration") + } + if ip6 && l.conn6 == nil { + return errors.New("failed to initiate IPv6 support. Check log details for permission configuration") + } + + return nil +} + +func (l *stdICMPLoop) runICMPRecv(conn *icmp.PacketConn, proto int) { + for { + bytes := make([]byte, 512) + conn.SetReadDeadline(time.Now().Add(1 * time.Second)) + _, addr, err := conn.ReadFrom(bytes) + if err != nil { + if neterr, ok := err.(*net.OpError); ok { + if neterr.Timeout() { + continue + } else { + // TODO: report error and quit loop? + return + } + } + } + + ts := time.Now() + m, err := icmp.ParseMessage(proto, bytes) + if err != nil { + continue + } + + // process echo reply only + if m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply { + continue + } + echo, ok := m.Body.(*icmp.Echo) + if !ok { + continue + } + + id := requestID{ + addr: addr.String(), + proto: proto, + id: echo.ID, + seq: echo.Seq, + } + + l.mutex.Lock() + ctx := l.requests[id] + if ctx != nil { + delete(l.requests, id) + } + l.mutex.Unlock() + + // no return context available for echo reply -> handle next message + if ctx == nil { + continue + } + + ctx.result <- requestResult{ + packet: packet{ + ts: ts, + addr: addr, + + Type: m.Type, + Code: m.Code, + Checksum: m.Checksum, + Echo: *echo, + }, + } + } +} + +func (l *stdICMPLoop) ping( + addr *net.IPAddr, + timeout time.Duration, + interval time.Duration, +) (time.Duration, int, error) { + var err error + toTimer := time.NewTimer(timeout) + defer toTimer.Stop() + + ticker := time.NewTicker(interval) + defer ticker.Stop() + + done := false + doneSignal := make(chan struct{}) + + success := false + var rtt time.Duration + + // results accepts first response received only + results := make(chan time.Duration, 1) + requests := 0 + + awaitResponse := func(ctx *requestContext) { + select { + case <-doneSignal: + ctx.Stop() + + case r := <-ctx.result: + // ctx is removed from request tables automatically a response is + // received. No need to stop it. + + // try to push RTT. The first result available will be reported + select { + case results <- r.packet.ts.Sub(ctx.ts): + default: + } + } + } + + for !done { + var ctx *requestContext + ctx, err = l.sendEchoRequest(addr) + if err != nil { + close(doneSignal) + break + } + go awaitResponse(ctx) + requests++ + + select { + case <-toTimer.C: + // no response for any active request received. Finish loop + // and remove all pingRequests from request table. + done = true + close(doneSignal) + + case <-ticker.C: + // No response yet. Send another request with every tick + + case rtt = <-results: + success = true + + done = true + close(doneSignal) + } + } + + if err != nil { + return 0, 0, err + } + + if !success { + return 0, requests, timeoutError{} + } + + return rtt, requests, nil +} + +func (l *stdICMPLoop) sendEchoRequest(addr *net.IPAddr) (*requestContext, error) { + var conn *icmp.PacketConn + var proto int + var typ icmp.Type + + if l == nil { + panic("icmp loop not initialized") + } + + if isIPv4(addr.IP) { + conn = l.conn4 + proto = protocolICMP + typ = ipv4.ICMPTypeEcho + } else if isIPv6(addr.IP) { + conn = l.conn6 + proto = protocolIPv6ICMP + typ = ipv6.ICMPTypeEchoRequest + } else { + return nil, fmt.Errorf("%v is unknown ip address", addr) + } + + id := requestID{ + addr: addr.String(), + proto: proto, + id: rand.Intn(0xffff), + seq: rand.Intn(0xffff), + } + + ctx := &requestContext{ + l: l, + id: id, + result: make(chan requestResult, 1), + } + + l.mutex.Lock() + l.requests[id] = ctx + l.mutex.Unlock() + + payloadBuf := make([]byte, 0, 8) + payload := bytes.NewBuffer(payloadBuf) + ts := time.Now() + binary.Write(payload, binary.BigEndian, ts.UnixNano()) + + msg := &icmp.Message{ + Type: typ, + Body: &icmp.Echo{ + ID: id.id, + Seq: id.seq, + Data: payload.Bytes(), + }, + } + encoded, _ := msg.Marshal(nil) + + _, err := conn.WriteTo(encoded, addr) + if err != nil { + return nil, err + } + + ctx.ts = ts + return ctx, nil +} + +func createListener(name, network string) *icmp.PacketConn { + conn, err := icmp.ListenPacket(network, "") + + // XXX: need to check for conn == nil, as 'err != nil' seems always to be + // true, even if error value itself is `nil`. Checking for conn suppresses + // misleading log message. + if conn == nil && err != nil { + return nil + } + return conn +} + +// timeoutError implements net.Error interface +func (timeoutError) Error() string { return "ping timeout" } +func (timeoutError) Timeout() bool { return true } +func (timeoutError) Temporary() bool { return true } + +func (r *requestContext) Stop() { + r.l.mutex.Lock() + delete(r.l.requests, r.id) + r.l.mutex.Unlock() +} diff --git a/heartbeat/monitors/active/tcp/tcp.go b/heartbeat/monitors/active/tcp/tcp.go index 05c687dd65b..26f96d2e010 100644 --- a/heartbeat/monitors/active/tcp/tcp.go +++ b/heartbeat/monitors/active/tcp/tcp.go @@ -18,18 +18,19 @@ package tcp import ( + "crypto/x509" "net" "net/url" "time" "github.com/elastic/beats/v7/heartbeat/eventext" "github.com/elastic/beats/v7/heartbeat/look" - "github.com/elastic/beats/v7/heartbeat/reason" - "github.com/elastic/beats/v7/heartbeat/monitors" "github.com/elastic/beats/v7/heartbeat/monitors/active/dialchain" + "github.com/elastic/beats/v7/heartbeat/monitors/active/dialchain/tlsmeta" "github.com/elastic/beats/v7/heartbeat/monitors/jobs" "github.com/elastic/beats/v7/heartbeat/monitors/wrappers" + "github.com/elastic/beats/v7/heartbeat/reason" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/transport" @@ -226,6 +227,9 @@ func (jf *jobFactory) execDialer( conn, err := dialer.Dial("tcp", addr) if err != nil { debugf("dial failed with: %v", err) + if certErr, ok := err.(x509.CertificateInvalidError); ok { + tlsmeta.AddCertMetadata(event.Fields, []*x509.Certificate{certErr.Cert}) + } return reason.IOFailed(err) } defer conn.Close() diff --git a/heartbeat/monitors/active/tcp/tls_test.go b/heartbeat/monitors/active/tcp/tls_test.go index 0628b1694b4..ff4cd569db5 100644 --- a/heartbeat/monitors/active/tcp/tls_test.go +++ b/heartbeat/monitors/active/tcp/tls_test.go @@ -18,12 +18,14 @@ package tcp import ( + "crypto/tls" "crypto/x509" "net" "net/http" "net/http/httptest" "net/url" "os" + "strconv" "testing" "time" @@ -112,6 +114,35 @@ func TestTLSInvalidCert(t *testing.T) { ) } +func TestTLSExpiredCert(t *testing.T) { + certFile := "../fixtures/expired.cert" + tlsCert, err := tls.LoadX509KeyPair(certFile, "../fixtures/expired.key") + require.NoError(t, err) + + ip, portStr, cert, closeSrv := hbtest.StartHTTPSServer(t, tlsCert) + defer closeSrv() + + portInt, err := strconv.Atoi(portStr) + port := uint16(portInt) + require.NoError(t, err) + + host := "localhost" + event := testTLSTCPCheck(t, host, port, certFile, monitors.NewStdResolver()) + + testslike.Test( + t, + lookslike.Strict(lookslike.Compose( + hbtest.RespondingTCPChecks(), + hbtest.BaseChecks(ip, "down", "tcp"), + hbtest.SummaryChecks(0, 1), + hbtest.SimpleURLChecks(t, "ssl", host, port), + hbtest.ResolveChecks(ip), + hbtest.ExpiredCertChecks(cert), + )), + event.Fields, + ) +} + func setupTLSTestServer(t *testing.T) (ip string, port uint16, cert *x509.Certificate, certFile *os.File, teardown func()) { // Start up a TLS Server server, port, err := setupServer(t, func(handler http.Handler) (*httptest.Server, error) { diff --git a/heartbeat/reason/reason.go b/heartbeat/reason/reason.go index 677a87a8971..ad1823af8e3 100644 --- a/heartbeat/reason/reason.go +++ b/heartbeat/reason/reason.go @@ -22,6 +22,7 @@ import "github.com/elastic/beats/v7/libbeat/common" type Reason interface { error Type() string + Unwrap() error } type ValidateError struct { @@ -47,9 +48,11 @@ func IOFailed(err error) Reason { } func (e ValidateError) Error() string { return e.err.Error() } +func (e ValidateError) Unwrap() error { return e.err } func (ValidateError) Type() string { return "validate" } func (e IOError) Error() string { return e.err.Error() } +func (e IOError) Unwrap() error { return e.err } func (IOError) Type() string { return "io" } func FailError(typ string, err error) common.MapStr { diff --git a/heartbeat/scripts/mage/config.go b/heartbeat/scripts/mage/config.go index 5128c7d6672..c1cc2e3f7ac 100644 --- a/heartbeat/scripts/mage/config.go +++ b/heartbeat/scripts/mage/config.go @@ -22,24 +22,13 @@ import ( ) // ConfigFileParams returns the default ConfigFileParams for generating -// packetbeat*.yml files. +// heartbeat*.yml files. func ConfigFileParams() devtools.ConfigFileParams { - return devtools.ConfigFileParams{ - ShortParts: []string{ - devtools.OSSBeatDir("_meta/beat.yml"), - devtools.LibbeatDir("_meta/config.yml.tmpl"), - }, - ReferenceParts: []string{ - devtools.OSSBeatDir("_meta/beat.reference.yml"), - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - }, - DockerParts: []string{ - devtools.OSSBeatDir("_meta/beat.docker.yml"), - devtools.LibbeatDir("_meta/config.docker.yml"), - }, - ExtraVars: map[string]interface{}{ - "UseObserverProcessor": true, - "ExcludeDashboards": true, - }, + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, devtools.OSSBeatDir("_meta/config/*.tmpl")) + p.ExtraVars = map[string]interface{}{ + "UseObserverProcessor": true, + "ExcludeDashboards": true, } + return p } diff --git a/journalbeat/_meta/beat.docker.yml b/journalbeat/_meta/config/beat.docker.yml.tmpl similarity index 97% rename from journalbeat/_meta/beat.docker.yml rename to journalbeat/_meta/config/beat.docker.yml.tmpl index b7d83bd0111..20ae6ee130d 100644 --- a/journalbeat/_meta/beat.docker.yml +++ b/journalbeat/_meta/config/beat.docker.yml.tmpl @@ -1,4 +1,3 @@ journalbeat.inputs: - paths: [] seek: cursor - diff --git a/journalbeat/_meta/beat.reference.yml b/journalbeat/_meta/config/beat.reference.yml.tmpl similarity index 89% rename from journalbeat/_meta/beat.reference.yml rename to journalbeat/_meta/config/beat.reference.yml.tmpl index c50755936d7..3d4bc90b6f1 100644 --- a/journalbeat/_meta/beat.reference.yml +++ b/journalbeat/_meta/config/beat.reference.yml.tmpl @@ -10,7 +10,7 @@ # For more available modules and options, please see the journalbeat.reference.yml sample # configuration file. -#=========================== Journalbeat inputs ============================= +{{header "Journalbeat inputs"}} journalbeat.inputs: # Paths that should be crawled and fetched. Possible values files and directories. @@ -44,13 +44,13 @@ journalbeat.inputs: # env: staging -#========================= Journalbeat global options ============================ +{{header "Journalbeat global options"}} #journalbeat: # Name of the registry file. If a relative path is used, it is considered relative to the # data path. #registry_file: registry -#==================== Elasticsearch template setting ========================== +{{header "Elasticsearch template setting"}} setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression diff --git a/journalbeat/_meta/beat.yml b/journalbeat/_meta/config/beat.yml.tmpl similarity index 87% rename from journalbeat/_meta/beat.yml rename to journalbeat/_meta/config/beat.yml.tmpl index 2bd2c91ce91..9410e82a925 100644 --- a/journalbeat/_meta/beat.yml +++ b/journalbeat/_meta/config/beat.yml.tmpl @@ -10,7 +10,7 @@ # For more available modules and options, please see the journalbeat.reference.yml sample # configuration file. -#=========================== Journalbeat inputs ============================= +{{header "Journalbeat inputs"}} journalbeat.inputs: # Paths that should be crawled and fetched. Possible values files and directories. @@ -39,13 +39,13 @@ journalbeat.inputs: # env: staging -#========================= Journalbeat global options ============================ +{{header "Journalbeat global options"}} #journalbeat: # Name of the registry file. If a relative path is used, it is considered relative to the # data path. #registry_file: registry -#==================== Elasticsearch template setting ========================== +{{header "Elasticsearch template setting"}} setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression diff --git a/journalbeat/docs/fields.asciidoc b/journalbeat/docs/fields.asciidoc index b568e32ed97..c98fa74ebc7 100644 --- a/journalbeat/docs/fields.asciidoc +++ b/journalbeat/docs/fields.asciidoc @@ -33,7 +33,8 @@ Contains common beat fields available in all event types. *`agent.hostname`*:: + -- -Hostname of the agent. +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. + type: keyword diff --git a/journalbeat/include/fields.go b/journalbeat/include/fields.go index 5d1d9a80b6a..207cf4a53e9 100644 --- a/journalbeat/include/fields.go +++ b/journalbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/journalbeat/journalbeat.reference.yml b/journalbeat/journalbeat.reference.yml index b5b8fd9c11d..2bdf764d03b 100644 --- a/journalbeat/journalbeat.reference.yml +++ b/journalbeat/journalbeat.reference.yml @@ -10,7 +10,7 @@ # For more available modules and options, please see the journalbeat.reference.yml sample # configuration file. -#=========================== Journalbeat inputs ============================= +# ============================= Journalbeat inputs ============================= journalbeat.inputs: # Paths that should be crawled and fetched. Possible values files and directories. @@ -44,19 +44,19 @@ journalbeat.inputs: # env: staging -#========================= Journalbeat global options ============================ +# ========================= Journalbeat global options ========================= #journalbeat: # Name of the registry file. If a relative path is used, it is considered relative to the # data path. #registry_file: registry -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false -#================================ General ====================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -164,7 +164,7 @@ setup.template.settings: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -181,153 +181,153 @@ setup.template.settings: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Journalbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -340,11 +340,11 @@ setup.template.settings: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -464,7 +464,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -578,7 +599,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -732,6 +753,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -754,7 +778,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -872,7 +896,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -906,7 +930,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -919,7 +943,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Journalbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -945,11 +969,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -993,8 +1019,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1048,7 +1073,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1062,13 +1087,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'journalbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "journalbeat" +#setup.ilm.rollover_alias: 'journalbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'journalbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1083,7 +1108,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1138,9 +1163,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1207,8 +1231,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Journalbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1318,6 +1341,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1329,7 +1373,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1353,12 +1398,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/journalbeat/journalbeat.yml b/journalbeat/journalbeat.yml index 9767cd72a0f..2dfa8a07f5f 100644 --- a/journalbeat/journalbeat.yml +++ b/journalbeat/journalbeat.yml @@ -10,7 +10,7 @@ # For more available modules and options, please see the journalbeat.reference.yml sample # configuration file. -#=========================== Journalbeat inputs ============================= +# ============================= Journalbeat inputs ============================= journalbeat.inputs: # Paths that should be crawled and fetched. Possible values files and directories. @@ -39,19 +39,19 @@ journalbeat.inputs: # env: staging -#========================= Journalbeat global options ============================ +# ========================= Journalbeat global options ========================= #journalbeat: # Name of the registry file. If a relative path is used, it is considered relative to the # data path. #registry_file: registry -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false -#================================ General ===================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -66,8 +66,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -79,7 +78,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -96,7 +95,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Journalbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -109,11 +108,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -126,7 +125,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -141,7 +140,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -150,7 +149,8 @@ processors: - add_cloud_metadata: ~ - add_docker_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -161,8 +161,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# journalbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Journalbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -183,7 +183,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/journalbeat/magefile.go b/journalbeat/magefile.go index d119e2e7554..f4ef3c694aa 100644 --- a/journalbeat/magefile.go +++ b/journalbeat/magefile.go @@ -193,7 +193,15 @@ func installDependencies(arch string, pkgs ...string) error { return err } - params := append([]string{"install", "-y", "--no-install-recommends"}, pkgs...) + params := append([]string{"install", "-y", + "--no-install-recommends", + + // Journalbeat is built with old versions of Debian that don't update + // their repositories, so they have expired keys. + // Allow unauthenticated packages. + // This was not enough: "-o", "Acquire::Check-Valid-Until=false", + "--allow-unauthenticated", + }, pkgs...) return sh.Run("apt-get", params...) } @@ -223,5 +231,7 @@ func selectImage(platform string) (string, error) { // Config generates both the short/reference/docker configs. func Config() error { - return devtools.Config(devtools.AllConfigTypes, devtools.ConfigFileParams{}, ".") + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, devtools.OSSBeatDir("_meta/config/*.tmpl")) + return devtools.Config(devtools.AllConfigTypes, p, ".") } diff --git a/libbeat/_meta/config.reference.yml.tmpl b/libbeat/_meta/config.reference.yml.tmpl deleted file mode 100644 index 9a9d2fdeb71..00000000000 --- a/libbeat/_meta/config.reference.yml.tmpl +++ /dev/null @@ -1,1307 +0,0 @@ - -#================================ General ====================================== - -# The name of the shipper that publishes the network data. It can be used to group -# all the transactions sent by a single shipper in the web interface. -# If this options is not defined, the hostname is used. -#name: - -# The tags of the shipper are included in their own field with each -# transaction published. Tags make it easy to group servers by different -# logical properties. -#tags: ["service-X", "web-tier"] - -# Optional fields that you can specify to add additional information to the -# output. Fields can be scalar values, arrays, dictionaries, or any nested -# combination of these. -#fields: -# env: staging - -# If this option is set to true, the custom fields are stored as top-level -# fields in the output document instead of being grouped under a fields -# sub-dictionary. Default is false. -#fields_under_root: false - -# Internal queue configuration for buffering events to be published. -#queue: - # Queue type by name (default 'mem') - # The memory queue will present all available events (up to the outputs - # bulk_max_size) to the output, the moment the output is ready to server - # another batch of events. - #mem: - # Max number of events the queue can buffer. - #events: 4096 - - # Hints the minimum number of events stored in the queue, - # before providing a batch of events to the outputs. - # The default value is set to 2048. - # A value of 0 ensures events are immediately available - # to be sent to the outputs. - #flush.min_events: 2048 - - # Maximum duration after which events are available to the outputs, - # if the number of events stored in the queue is < `flush.min_events`. - #flush.timeout: 1s - - # The spool queue will store events in a local spool file, before - # forwarding the events to the outputs. - # - # Beta: spooling to disk is currently a beta feature. Use with care. - # - # The spool file is a circular buffer, which blocks once the file/buffer is full. - # Events are put into a write buffer and flushed once the write buffer - # is full or the flush_timeout is triggered. - # Once ACKed by the output, events are removed immediately from the queue, - # making space for new events to be persisted. - #spool: - # The file namespace configures the file path and the file creation settings. - # Once the file exists, the `size`, `page_size` and `prealloc` settings - # will have no more effect. - #file: - # Location of spool file. The default value is ${path.data}/spool.dat. - #path: "${path.data}/spool.dat" - - # Configure file permissions if file is created. The default value is 0600. - #permissions: 0600 - - # File size hint. The spool blocks, once this limit is reached. The default value is 100 MiB. - #size: 100MiB - - # The files page size. A file is split into multiple pages of the same size. The default value is 4KiB. - #page_size: 4KiB - - # If prealloc is set, the required space for the file is reserved using - # truncate. The default value is true. - #prealloc: true - - # Spool writer settings - # Events are serialized into a write buffer. The write buffer is flushed if: - # - The buffer limit has been reached. - # - The configured limit of buffered events is reached. - # - The flush timeout is triggered. - #write: - # Sets the write buffer size. - #buffer_size: 1MiB - - # Maximum duration after which events are flushed if the write buffer - # is not full yet. The default value is 1s. - #flush.timeout: 1s - - # Number of maximum buffered events. The write buffer is flushed once the - # limit is reached. - #flush.events: 16384 - - # Configure the on-disk event encoding. The encoding can be changed - # between restarts. - # Valid encodings are: json, ubjson, and cbor. - #codec: cbor - #read: - # Reader flush timeout, waiting for more events to become available, so - # to fill a complete batch as required by the outputs. - # If flush_timeout is 0, all available events are forwarded to the - # outputs immediately. - # The default value is 0s. - #flush.timeout: 0s - -# Sets the maximum number of CPUs that can be executing simultaneously. The -# default is the number of logical CPUs available in the system. -#max_procs: - -#================================ Processors =================================== - -# Processors are used to reduce the number of fields in the exported event or to -# enhance the event with external metadata. This section defines a list of -# processors that are applied one by one and the first one receives the initial -# event: -# -# event -> filter1 -> event1 -> filter2 ->event2 ... -# -# The supported processors are drop_fields, drop_event, include_fields, -# decode_json_fields, and add_cloud_metadata. -# -# For example, you can use the following processors to keep the fields that -# contain CPU load percentages, but remove the fields that contain CPU ticks -# values: -# -#processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] -# -# The following example drops the events that have the HTTP response code 200: -# -#processors: -#- drop_event: -# when: -# equals: -# http.code: 200 -# -# The following example renames the field a to b: -# -#processors: -#- rename: -# fields: -# - from: "a" -# to: "b" -# -# The following example tokenizes the string into fields: -# -#processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" -# -# The following example enriches each event with metadata from the cloud -# provider about the host machine. It works on EC2, GCE, DigitalOcean, -# Tencent Cloud, and Alibaba Cloud. -# -#processors: -#- add_cloud_metadata: ~ -# -# The following example enriches each event with the machine's local time zone -# offset from UTC. -# -#processors: -#- add_locale: -# format: offset -# -# The following example enriches each event with docker metadata, it matches -# given fields to an existing container id and adds info from that container: -# -#processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" -# -# The following example enriches each event with docker metadata, it matches -# container id from log path available in `source` field (by default it expects -# it to be /var/lib/docker/containers/*/*.log). -# -#processors: -#- add_docker_metadata: ~ -# -# The following example enriches each event with host metadata. -# -#processors: -#- add_host_metadata: ~ -# -# The following example enriches each event with process metadata using -# process IDs included in the event. -# -#processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent -# -# The following example decodes fields containing JSON strings -# and replaces the strings with valid JSON objects. -# -#processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false -# -#processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true -# -# The following example copies the value of message to message_copied -# -#processors: -#- copy_fields: -# fields: -# - from: message -# to: message_copied -# fail_on_error: true -# ignore_missing: false -# -# The following example truncates the value of message to 1024 bytes -# -#processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true -# -# The following example preserves the raw message under event.original -# -#processors: -#- copy_fields: -# fields: -# - from: message -# to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true -# -# The following example URL-decodes the value of field1 to field2 -# -#processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true - -#============================= Elastic Cloud ================================== - -# These settings simplify using {{.BeatName | title}} with the Elastic Cloud (https://cloud.elastic.co/). - -# The cloud.id setting overwrites the `output.elasticsearch.hosts` and -# `setup.kibana.host` options. -# You can find the `cloud.id` in the Elastic Cloud web UI. -#cloud.id: - -# The cloud.auth setting overwrites the `output.elasticsearch.username` and -# `output.elasticsearch.password` settings. The format is `:`. -#cloud.auth: - -#================================ Outputs ====================================== - -# Configure what output to use when sending the data collected by the beat. - -#-------------------------- Elasticsearch output ------------------------------- -output.elasticsearch: - # Boolean flag to enable or disable the output module. - #enabled: true - - # Array of hosts to connect to. - # Scheme and port can be left out and will be set to the default (http and 9200) - # In case you specify and additional path, the scheme is required: http://localhost:9200/path - # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 - hosts: ["localhost:9200"] - - # Set gzip compression level. - #compression_level: 0 - - # Configure escaping HTML symbols in strings. - #escape_html: false - - # Protocol - either `http` (default) or `https`. - #protocol: "https" - - # Authentication credentials - either API key or username/password. - #api_key: "id:api_key" - #username: "elastic" - #password: "changeme" - - # Dictionary of HTTP parameters to pass within the URL with index operations. - #parameters: - #param1: value1 - #param2: value2 - - # Number of workers per Elasticsearch host. - #worker: 1 - - # Optional index name. The default is "beat-index-prefix" plus date - # and generates [beat-index-prefix-]YYYY.MM.DD keys. - # In case you modify this pattern you must update setup.template.name and setup.template.pattern accordingly. - #index: "beat-index-prefix-%{[agent.version]}-%{+yyyy.MM.dd}" - - # Optional ingest node pipeline. By default no pipeline will be used. - #pipeline: "" - - # Optional HTTP path - #path: "/elasticsearch" - - # Custom HTTP headers to add to each request - #headers: - # X-My-Header: Contents of the header - - # Proxy server URL - #proxy_url: http://proxy:3128 - - # Whether to disable proxy settings for outgoing connections. If true, this - # takes precedence over both the proxy_url field and any environment settings - # (HTTP_PROXY, HTTPS_PROXY). The default is false. - #proxy_disable: false - - # The number of times a particular Elasticsearch index operation is attempted. If - # the indexing operation doesn't succeed after this many retries, the events are - # dropped. The default is 3. - #max_retries: 3 - - # The maximum number of events to bulk in a single Elasticsearch bulk API index request. - # The default is 50. - #bulk_max_size: 50 - - # The number of seconds to wait before trying to reconnect to Elasticsearch - # after a network error. After waiting backoff.init seconds, the Beat - # tries to reconnect. If the attempt fails, the backoff timer is increased - # exponentially up to backoff.max. After a successful connection, the backoff - # timer is reset. The default is 1s. - #backoff.init: 1s - - # The maximum number of seconds to wait before attempting to connect to - # Elasticsearch after a network error. The default is 60s. - #backoff.max: 60s - - # Configure HTTP request timeout before failing a request to Elasticsearch. - #timeout: 90 - - # Use SSL settings for HTTPS. - #ssl.enabled: true - - # Configure SSL verification mode. If `none` is configured, all server hosts - # and certificates will be accepted. In this mode, SSL-based connections are - # susceptible to man-in-the-middle attacks. Use only for testing. Default is - # `full`. - #ssl.verification_mode: full - - # List of supported/valid TLS versions. By default all TLS versions from 1.1 - # up to 1.3 are enabled. - #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] - - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" - - # Client certificate key - #ssl.key: "/etc/pki/client/cert.key" - - # Optional passphrase for decrypting the certificate key. - #ssl.key_passphrase: '' - - # Configure cipher suites to be used for SSL connections - #ssl.cipher_suites: [] - - # Configure curve types for ECDHE-based cipher suites - #ssl.curve_types: [] - - # Configure what types of renegotiation are supported. Valid options are - # never, once, and freely. Default is never. - #ssl.renegotiation: never - - # Configure a pin that can be used to do extra validation of the verified certificate chain, - # this allow you to ensure that a specific certificate is used to validate the chain of trust. - # - # The pin is a base64 encoded string of the SHA-256 fingerprint. - #ssl.ca_sha256: "" -{{if not .ExcludeLogstash}} -#----------------------------- Logstash output --------------------------------- -#output.logstash: - # Boolean flag to enable or disable the output module. - #enabled: true - - # The Logstash hosts - #hosts: ["localhost:5044"] - - # Number of workers per Logstash host. - #worker: 1 - - # Set gzip compression level. - #compression_level: 3 - - # Configure escaping HTML symbols in strings. - #escape_html: false - - # Optional maximum time to live for a connection to Logstash, after which the - # connection will be re-established. A value of `0s` (the default) will - # disable this feature. - # - # Not yet supported for async connections (i.e. with the "pipelining" option set) - #ttl: 30s - - # Optionally load-balance events between Logstash hosts. Default is false. - #loadbalance: false - - # Number of batches to be sent asynchronously to Logstash while processing - # new batches. - #pipelining: 2 - - # If enabled only a subset of events in a batch of events is transferred per - # transaction. The number of events to be sent increases up to `bulk_max_size` - # if no error is encountered. - #slow_start: false - - # The number of seconds to wait before trying to reconnect to Logstash - # after a network error. After waiting backoff.init seconds, the Beat - # tries to reconnect. If the attempt fails, the backoff timer is increased - # exponentially up to backoff.max. After a successful connection, the backoff - # timer is reset. The default is 1s. - #backoff.init: 1s - - # The maximum number of seconds to wait before attempting to connect to - # Logstash after a network error. The default is 60s. - #backoff.max: 60s - - # Optional index name. The default index name is set to beat-index-prefix - # in all lowercase. - #index: 'beat-index-prefix' - - # SOCKS5 proxy server URL - #proxy_url: socks5://user:password@socks5-server:2233 - - # Resolve names locally when using a proxy server. Defaults to false. - #proxy_use_local_resolver: false - - # Enable SSL support. SSL is automatically enabled if any SSL setting is set. - #ssl.enabled: true - - # Configure SSL verification mode. If `none` is configured, all server hosts - # and certificates will be accepted. In this mode, SSL based connections are - # susceptible to man-in-the-middle attacks. Use only for testing. Default is - # `full`. - #ssl.verification_mode: full - - # List of supported/valid TLS versions. By default all TLS versions from 1.1 - # up to 1.3 are enabled. - #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] - - # Optional SSL configuration options. SSL is off by default. - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" - - # Client certificate key - #ssl.key: "/etc/pki/client/cert.key" - - # Optional passphrase for decrypting the Certificate Key. - #ssl.key_passphrase: '' - - # Configure cipher suites to be used for SSL connections - #ssl.cipher_suites: [] - - # Configure curve types for ECDHE-based cipher suites - #ssl.curve_types: [] - - # Configure what types of renegotiation are supported. Valid options are - # never, once, and freely. Default is never. - #ssl.renegotiation: never - - # Configure a pin that can be used to do extra validation of the verified certificate chain, - # this allow you to ensure that a specific certificate is used to validate the chain of trust. - # - # The pin is a base64 encoded string of the SHA-256 fingerprint. - #ssl.ca_sha256: "" - - # The number of times to retry publishing an event after a publishing failure. - # After the specified number of retries, the events are typically dropped. - # Some Beats, such as Filebeat and Winlogbeat, ignore the max_retries setting - # and retry until all events are published. Set max_retries to a value less - # than 0 to retry until all events are published. The default is 3. - #max_retries: 3 - - # The maximum number of events to bulk in a single Logstash request. The - # default is 2048. - #bulk_max_size: 2048 - - # The number of seconds to wait for responses from the Logstash server before - # timing out. The default is 30s. - #timeout: 30s -{{end}}{{if not .ExcludeKafka}} -#------------------------------- Kafka output ---------------------------------- -#output.kafka: - # Boolean flag to enable or disable the output module. - #enabled: true - - # The list of Kafka broker addresses from which to fetch the cluster metadata. - # The cluster metadata contain the actual Kafka brokers events are published - # to. - #hosts: ["localhost:9092"] - - # The Kafka topic used for produced events. The setting can be a format string - # using any event field. To set the topic from document type use `%{[type]}`. - #topic: beats - - # The Kafka event key setting. Use format string to create a unique event key. - # By default no event key will be generated. - #key: '' - - # The Kafka event partitioning strategy. Default hashing strategy is `hash` - # using the `output.kafka.key` setting or randomly distributes events if - # `output.kafka.key` is not configured. - #partition.hash: - # If enabled, events will only be published to partitions with reachable - # leaders. Default is false. - #reachable_only: false - - # Configure alternative event field names used to compute the hash value. - # If empty `output.kafka.key` setting will be used. - # Default value is empty list. - #hash: [] - - # Authentication details. Password is required if username is set. - #username: '' - #password: '' - - # Kafka version {{.BeatName | title}} is assumed to run against. Defaults to the "1.0.0". - #version: '1.0.0' - - # Configure JSON encoding - #codec.json: - # Pretty-print JSON event - #pretty: false - - # Configure escaping HTML symbols in strings. - #escape_html: false - - # Metadata update configuration. Metadata contains leader information - # used to decide which broker to use when publishing. - #metadata: - # Max metadata request retry attempts when cluster is in middle of leader - # election. Defaults to 3 retries. - #retry.max: 3 - - # Wait time between retries during leader elections. Default is 250ms. - #retry.backoff: 250ms - - # Refresh metadata interval. Defaults to every 10 minutes. - #refresh_frequency: 10m - - # Strategy for fetching the topics metadata from the broker. Default is false. - #full: false - - # The number of concurrent load-balanced Kafka output workers. - #worker: 1 - - # The number of times to retry publishing an event after a publishing failure. - # After the specified number of retries, events are typically dropped. - # Some Beats, such as Filebeat, ignore the max_retries setting and retry until - # all events are published. Set max_retries to a value less than 0 to retry - # until all events are published. The default is 3. - #max_retries: 3 - - # The maximum number of events to bulk in a single Kafka request. The default - # is 2048. - #bulk_max_size: 2048 - - # Duration to wait before sending bulk Kafka request. 0 is no delay. The default - # is 0. - #bulk_flush_frequency: 0s - - # The number of seconds to wait for responses from the Kafka brokers before - # timing out. The default is 30s. - #timeout: 30s - - # The maximum duration a broker will wait for number of required ACKs. The - # default is 10s. - #broker_timeout: 10s - - # The number of messages buffered for each Kafka broker. The default is 256. - #channel_buffer_size: 256 - - # The keep-alive period for an active network connection. If 0s, keep-alives - # are disabled. The default is 0 seconds. - #keep_alive: 0 - - # Sets the output compression codec. Must be one of none, snappy and gzip. The - # default is gzip. - #compression: gzip - - # Set the compression level. Currently only gzip provides a compression level - # between 0 and 9. The default value is chosen by the compression algorithm. - #compression_level: 4 - - # The maximum permitted size of JSON-encoded messages. Bigger messages will be - # dropped. The default value is 1000000 (bytes). This value should be equal to - # or less than the broker's message.max.bytes. - #max_message_bytes: 1000000 - - # The ACK reliability level required from broker. 0=no response, 1=wait for - # local commit, -1=wait for all replicas to commit. The default is 1. Note: - # If set to 0, no ACKs are returned by Kafka. Messages might be lost silently - # on error. - #required_acks: 1 - - # The configurable ClientID used for logging, debugging, and auditing - # purposes. The default is "beats". - #client_id: beats - - # Enable SSL support. SSL is automatically enabled if any SSL setting is set. - #ssl.enabled: true - - # Optional SSL configuration options. SSL is off by default. - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # Configure SSL verification mode. If `none` is configured, all server hosts - # and certificates will be accepted. In this mode, SSL based connections are - # susceptible to man-in-the-middle attacks. Use only for testing. Default is - # `full`. - #ssl.verification_mode: full - - # List of supported/valid TLS versions. By default all TLS versions from 1.1 - # up to 1.3 are enabled. - #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] - - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" - - # Client Certificate Key - #ssl.key: "/etc/pki/client/cert.key" - - # Optional passphrase for decrypting the Certificate Key. - #ssl.key_passphrase: '' - - # Configure cipher suites to be used for SSL connections - #ssl.cipher_suites: [] - - # Configure curve types for ECDHE-based cipher suites - #ssl.curve_types: [] - - # Configure what types of renegotiation are supported. Valid options are - # never, once, and freely. Default is never. - #ssl.renegotiation: never - - # Authentication type to use with Kerberos. Available options: keytab, password. - #kerberos.auth_type: password - - # Path to the keytab file. It is used when auth_type is set to keytab. - #kerberos.keytab: /etc/security/keytabs/kafka.keytab - - # Path to the Kerberos configuration. - #kerberos.config_path: /etc/krb5.conf - - # The service name. Service principal name is contructed from - # service_name/hostname@realm. - #kerberos.service_name: kafka - - # Name of the Kerberos user. - #kerberos.username: elastic - - # Password of the Kerberos user. It is used when auth_type is set to password. - #kerberos.password: changeme - - # Kerberos realm. - #kerberos.realm: ELASTIC -{{end}}{{if not .ExcludeRedis}} -#------------------------------- Redis output ---------------------------------- -#output.redis: - # Boolean flag to enable or disable the output module. - #enabled: true - - # Configure JSON encoding - #codec.json: - # Pretty print json event - #pretty: false - - # Configure escaping HTML symbols in strings. - #escape_html: false - - # The list of Redis servers to connect to. If load-balancing is enabled, the - # events are distributed to the servers in the list. If one server becomes - # unreachable, the events are distributed to the reachable servers only. - # The hosts setting supports redis and rediss urls with custom password like - # redis://:password@localhost:6379. - #hosts: ["localhost:6379"] - - # The name of the Redis list or channel the events are published to. The - # default is {{.BeatName}}. - #key: {{.BeatName}} - - # The password to authenticate to Redis with. The default is no authentication. - #password: - - # The Redis database number where the events are published. The default is 0. - #db: 0 - - # The Redis data type to use for publishing events. If the data type is list, - # the Redis RPUSH command is used. If the data type is channel, the Redis - # PUBLISH command is used. The default value is list. - #datatype: list - - # The number of workers to use for each host configured to publish events to - # Redis. Use this setting along with the loadbalance option. For example, if - # you have 2 hosts and 3 workers, in total 6 workers are started (3 for each - # host). - #worker: 1 - - # If set to true and multiple hosts or workers are configured, the output - # plugin load balances published events onto all Redis hosts. If set to false, - # the output plugin sends all events to only one host (determined at random) - # and will switch to another host if the currently selected one becomes - # unreachable. The default value is true. - #loadbalance: true - - # The Redis connection timeout in seconds. The default is 5 seconds. - #timeout: 5s - - # The number of times to retry publishing an event after a publishing failure. - # After the specified number of retries, the events are typically dropped. - # Some Beats, such as Filebeat, ignore the max_retries setting and retry until - # all events are published. Set max_retries to a value less than 0 to retry - # until all events are published. The default is 3. - #max_retries: 3 - - # The number of seconds to wait before trying to reconnect to Redis - # after a network error. After waiting backoff.init seconds, the Beat - # tries to reconnect. If the attempt fails, the backoff timer is increased - # exponentially up to backoff.max. After a successful connection, the backoff - # timer is reset. The default is 1s. - #backoff.init: 1s - - # The maximum number of seconds to wait before attempting to connect to - # Redis after a network error. The default is 60s. - #backoff.max: 60s - - # The maximum number of events to bulk in a single Redis request or pipeline. - # The default is 2048. - #bulk_max_size: 2048 - - # The URL of the SOCKS5 proxy to use when connecting to the Redis servers. The - # value must be a URL with a scheme of socks5://. - #proxy_url: - - # This option determines whether Redis hostnames are resolved locally when - # using a proxy. The default value is false, which means that name resolution - # occurs on the proxy server. - #proxy_use_local_resolver: false - - # Enable SSL support. SSL is automatically enabled, if any SSL setting is set. - #ssl.enabled: true - - # Configure SSL verification mode. If `none` is configured, all server hosts - # and certificates will be accepted. In this mode, SSL based connections are - # susceptible to man-in-the-middle attacks. Use only for testing. Default is - # `full`. - #ssl.verification_mode: full - - # List of supported/valid TLS versions. By default all TLS versions from 1.1 - # up to 1.3 are enabled. - #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] - - # Optional SSL configuration options. SSL is off by default. - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" - - # Client Certificate Key - #ssl.key: "/etc/pki/client/cert.key" - - # Optional passphrase for decrypting the Certificate Key. - #ssl.key_passphrase: '' - - # Configure cipher suites to be used for SSL connections - #ssl.cipher_suites: [] - - # Configure curve types for ECDHE based cipher suites - #ssl.curve_types: [] - - # Configure what types of renegotiation are supported. Valid options are - # never, once, and freely. Default is never. - #ssl.renegotiation: never -{{end}}{{if not .ExcludeFileOutput}} -#------------------------------- File output ----------------------------------- -#output.file: - # Boolean flag to enable or disable the output module. - #enabled: true - - # Configure JSON encoding - #codec.json: - # Pretty-print JSON event - #pretty: false - - # Configure escaping HTML symbols in strings. - #escape_html: false - - # Path to the directory where to save the generated files. The option is - # mandatory. - #path: "/tmp/{{.BeatName}}" - - # Name of the generated files. The default is `{{.BeatName}}` and it generates - # files: `{{.BeatName}}`, `{{.BeatName}}.1`, `{{.BeatName}}.2`, etc. - #filename: {{.BeatName}} - - # Maximum size in kilobytes of each file. When this size is reached, and on - # every {{.BeatName | title}} restart, the files are rotated. The default value is 10240 - # kB. - #rotate_every_kb: 10000 - - # Maximum number of files under path. When this number of files is reached, - # the oldest file is deleted and the rest are shifted from last to first. The - # default is 7 files. - #number_of_files: 7 - - # Permissions to use for file creation. The default is 0600. - #permissions: 0600 -{{end}}{{if not .ExcludeConsole}} -#----------------------------- Console output --------------------------------- -#output.console: - # Boolean flag to enable or disable the output module. - #enabled: true - - # Configure JSON encoding - #codec.json: - # Pretty-print JSON event - #pretty: false - - # Configure escaping HTML symbols in strings. - #escape_html: false -{{end}} -#================================= Paths ====================================== - -# The home path for the {{.BeatName | title}} installation. This is the default base path -# for all other path settings and for miscellaneous files that come with the -# distribution (for example, the sample dashboards). -# If not set by a CLI flag or in the configuration file, the default for the -# home path is the location of the binary. -#path.home: - -# The configuration path for the {{.BeatName | title}} installation. This is the default -# base path for configuration files, including the main YAML configuration file -# and the Elasticsearch template file. If not set by a CLI flag or in the -# configuration file, the default for the configuration path is the home path. -#path.config: ${path.home} - -# The data path for the {{.BeatName | title}} installation. This is the default base path -# for all the files in which {{.BeatName | title}} needs to store its data. If not set by a -# CLI flag or in the configuration file, the default for the data path is a data -# subdirectory inside the home path. -#path.data: ${path.home}/data - -# The logs path for a {{.BeatName | title}} installation. This is the default location for -# the Beat's log files. If not set by a CLI flag or in the configuration file, -# the default for the logs path is a logs subdirectory inside the home path. -#path.logs: ${path.home}/logs - -#================================ Keystore ========================================== -# Location of the Keystore containing the keys and their sensitive values. -#keystore.path: "${path.config}/beats.keystore" - -#============================== Dashboards ===================================== -# These settings control loading the sample dashboards to the Kibana index. Loading -# the dashboards are disabled by default and can be enabled either by setting the -# options here, or by using the `-setup` CLI flag or the `setup` command. -#setup.dashboards.enabled: false - -# The directory from where to read the dashboards. The default is the `kibana` -# folder in the home path. -#setup.dashboards.directory: ${path.home}/kibana - -# The URL from where to download the dashboards archive. It is used instead of -# the directory if it has a value. -#setup.dashboards.url: - -# The file archive (zip file) from where to read the dashboards. It is used instead -# of the directory when it has a value. -#setup.dashboards.file: - -# In case the archive contains the dashboards from multiple Beats, this lets you -# select which one to load. You can load all the dashboards in the archive by -# setting this to the empty string. -#setup.dashboards.beat: {{.BeatName}} - -# The name of the Kibana index to use for setting the configuration. Default is ".kibana" -#setup.dashboards.kibana_index: .kibana - -# The Elasticsearch index name. This overwrites the index name defined in the -# dashboards and index pattern. Example: testbeat-* -#setup.dashboards.index: - -# Always use the Kibana API for loading the dashboards instead of autodetecting -# how to install the dashboards by first querying Elasticsearch. -#setup.dashboards.always_kibana: false - -# If true and Kibana is not reachable at the time when dashboards are loaded, -# it will retry to reconnect to Kibana instead of exiting with an error. -#setup.dashboards.retry.enabled: false - -# Duration interval between Kibana connection retries. -#setup.dashboards.retry.interval: 1s - -# Maximum number of retries before exiting with an error, 0 for unlimited retrying. -#setup.dashboards.retry.maximum: 0 - - -#============================== Template ===================================== - -# A template is used to set the mapping in Elasticsearch -# By default template loading is enabled and the template is loaded. -# These settings can be adjusted to load your own template or overwrite existing ones. - -# Set to false to disable template loading. -#setup.template.enabled: true - -# Template name. By default the template name is "beat-index-prefix-%{[agent.version]}" -# The template name and pattern has to be set in case the Elasticsearch index pattern is modified. -#setup.template.name: "beat-index-prefix-%{[agent.version]}" - -# Template pattern. By default the template pattern is "-%{[agent.version]}-*" to apply to the default index settings. -# The first part is the version of the beat and then -* is used to match all daily indices. -# The template name and pattern has to be set in case the Elasticsearch index pattern is modified. -#setup.template.pattern: "beat-index-prefix-%{[agent.version]}-*" - -# Path to fields.yml file to generate the template -#setup.template.fields: "${path.config}/fields.yml" - -# A list of fields to be added to the template and Kibana index pattern. Also -# specify setup.template.overwrite: true to overwrite the existing template. -#setup.template.append_fields: -#- name: field_name -# type: field_type - -# Enable JSON template loading. If this is enabled, the fields.yml is ignored. -#setup.template.json.enabled: false - -# Path to the JSON template file -#setup.template.json.path: "${path.config}/template.json" - -# Name under which the template is stored in Elasticsearch -#setup.template.json.name: "" - -# Overwrite existing template -#setup.template.overwrite: false - -# Elasticsearch template settings -setup.template.settings: - - # A dictionary of settings to place into the settings.index dictionary - # of the Elasticsearch template. For more details, please check - # https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html - #index: - #number_of_shards: 1 - #codec: best_compression - - # A dictionary of settings for the _source field. For more details, please check - # https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html - #_source: - #enabled: false - -#============================== Setup ILM ===================================== - -# Configure index lifecycle management (ILM). These settings create a write -# alias and add additional settings to the index template. When ILM is enabled, -# output.elasticsearch.index is ignored, and the write alias is used to set the -# index name. - -# Enable ILM support. Valid values are true, false, and auto. When set to auto -# (the default), the Beat uses index lifecycle management when it connects to a -# cluster that supports ILM; otherwise, it creates daily indices. -#setup.ilm.enabled: auto - -# Set the prefix used in the index lifecycle write alias name. The default alias -# name is 'beatname-%{[agent.version]}'. -#setup.ilm.rollover_alias: "beat-index-prefix" - -# Set the rollover index pattern. The default is "%{now/d}-000001". -#setup.ilm.pattern: "{now/d}-000001" - -# Set the lifecycle policy name. The default policy name is -# 'beatname'. -#setup.ilm.policy_name: "mypolicy" - -# The path to a JSON file that contains a lifecycle policy configuration. Used -# to load your own lifecycle policy. -#setup.ilm.policy_file: - -# Disable the check for an existing lifecycle policy. The default is true. If -# you disable this check, set setup.ilm.overwrite: true so the lifecycle policy -# can be installed. -#setup.ilm.check_exists: true - -# Overwrite the lifecycle policy at startup. The default is false. -#setup.ilm.overwrite: false - -#============================== Kibana ===================================== - -# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. -# This requires a Kibana endpoint configuration. -setup.kibana: - - # Kibana Host - # Scheme and port can be left out and will be set to the default (http and 5601) - # In case you specify and additional path, the scheme is required: http://localhost:5601/path - # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 - #host: "localhost:5601" - - # Optional protocol and basic auth credentials. - #protocol: "https" - #username: "elastic" - #password: "changeme" - - # Optional HTTP path - #path: "" - - # Optional Kibana space ID. - #space.id: "" - - # Use SSL settings for HTTPS. Default is true. - #ssl.enabled: true - - # Configure SSL verification mode. If `none` is configured, all server hosts - # and certificates will be accepted. In this mode, SSL based connections are - # susceptible to man-in-the-middle attacks. Use only for testing. Default is - # `full`. - #ssl.verification_mode: full - - # List of supported/valid TLS versions. By default all TLS versions from 1.1 - # up to 1.3 are enabled. - #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] - - # SSL configuration. The default is off. - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" - - # Client certificate key - #ssl.key: "/etc/pki/client/cert.key" - - # Optional passphrase for decrypting the certificate key. - #ssl.key_passphrase: '' - - # Configure cipher suites to be used for SSL connections - #ssl.cipher_suites: [] - - # Configure curve types for ECDHE-based cipher suites - #ssl.curve_types: [] - - - -#================================ Logging ====================================== -# There are four options for the log output: file, stderr, syslog, eventlog -# The file output is the default. - -# Sets log level. The default log level is info. -# Available log levels are: error, warning, info, debug -#logging.level: info - -# Enable debug output for selected components. To enable all selectors use ["*"] -# Other available selectors are "beat", "publish", "service" -# Multiple selectors can be chained. -#logging.selectors: [ ] - -# Send all logging output to stderr. The default is false. -#logging.to_stderr: false - -# Send all logging output to syslog. The default is false. -#logging.to_syslog: false - -# Send all logging output to Windows Event Logs. The default is false. -#logging.to_eventlog: false - -# If enabled, {{.BeatName | title}} periodically logs its internal metrics that have changed -# in the last period. For each metric that changed, the delta from the value at -# the beginning of the period is logged. Also, the total values for -# all non-zero internal metrics are logged on shutdown. The default is true. -#logging.metrics.enabled: true - -# The period after which to log the internal metrics. The default is 30s. -#logging.metrics.period: 30s - -# Logging to rotating files. Set logging.to_files to false to disable logging to -# files. -logging.to_files: true -logging.files: - # Configure the path where the logs are written. The default is the logs directory - # under the home path (the binary location). - #path: /var/log/{{.BeatName}} - - # The name of the files where the logs are written to. - #name: {{.BeatName}} - - # Configure log file size limit. If limit is reached, log file will be - # automatically rotated - #rotateeverybytes: 10485760 # = 10MB - - # Number of rotated log files to keep. Oldest files will be deleted first. - #keepfiles: 7 - - # The permissions mask to apply when rotating log files. The default value is 0600. - # Must be a valid Unix-style file permissions mask expressed in octal notation. - #permissions: 0600 - - # Enable log file rotation on time intervals in addition to size-based rotation. - # Intervals must be at least 1s. Values of 1m, 1h, 24h, 7*24h, 30*24h, and 365*24h - # are boundary-aligned with minutes, hours, days, weeks, months, and years as - # reported by the local system clock. All other intervals are calculated from the - # Unix epoch. Defaults to disabled. - #interval: 0 - - # Rotate existing logs on startup rather than appending to the existing - # file. Defaults to true. - # rotateonstartup: true - -# Set to true to log messages in JSON format. -#logging.json: false - - -#============================== X-Pack Monitoring =============================== -# {{.BeatName | title}} can export internal metrics to a central Elasticsearch monitoring -# cluster. This requires xpack monitoring to be enabled in Elasticsearch. The -# reporting is disabled by default. - -# Set to true to enable the monitoring reporter. -#monitoring.enabled: false - -# Sets the UUID of the Elasticsearch cluster under which monitoring data for this -# {{.BeatName | title}} instance will appear in the Stack Monitoring UI. If output.elasticsearch -# is enabled, the UUID is derived from the Elasticsearch cluster referenced by output.elasticsearch. -#monitoring.cluster_uuid: - -# Uncomment to send the metrics to Elasticsearch. Most settings from the -# Elasticsearch output are accepted here as well. -# Note that the settings should point to your Elasticsearch *monitoring* cluster. -# Any setting that is not set is automatically inherited from the Elasticsearch -# output configuration, so if you have the Elasticsearch output configured such -# that it is pointing to your Elasticsearch monitoring cluster, you can simply -# uncomment the following line. -#monitoring.elasticsearch: - - # Array of hosts to connect to. - # Scheme and port can be left out and will be set to the default (http and 9200) - # In case you specify and additional path, the scheme is required: http://localhost:9200/path - # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 - #hosts: ["localhost:9200"] - - # Set gzip compression level. - #compression_level: 0 - - # Protocol - either `http` (default) or `https`. - #protocol: "https" - - # Authentication credentials - either API key or username/password. - #api_key: "id:api_key" - #username: "beats_system" - #password: "changeme" - - # Dictionary of HTTP parameters to pass within the URL with index operations. - #parameters: - #param1: value1 - #param2: value2 - - # Custom HTTP headers to add to each request - #headers: - # X-My-Header: Contents of the header - - # Proxy server url - #proxy_url: http://proxy:3128 - - # The number of times a particular Elasticsearch index operation is attempted. If - # the indexing operation doesn't succeed after this many retries, the events are - # dropped. The default is 3. - #max_retries: 3 - - # The maximum number of events to bulk in a single Elasticsearch bulk API index request. - # The default is 50. - #bulk_max_size: 50 - - # The number of seconds to wait before trying to reconnect to Elasticsearch - # after a network error. After waiting backoff.init seconds, the Beat - # tries to reconnect. If the attempt fails, the backoff timer is increased - # exponentially up to backoff.max. After a successful connection, the backoff - # timer is reset. The default is 1s. - #backoff.init: 1s - - # The maximum number of seconds to wait before attempting to connect to - # Elasticsearch after a network error. The default is 60s. - #backoff.max: 60s - - # Configure HTTP request timeout before failing an request to Elasticsearch. - #timeout: 90 - - # Use SSL settings for HTTPS. - #ssl.enabled: true - - # Configure SSL verification mode. If `none` is configured, all server hosts - # and certificates will be accepted. In this mode, SSL based connections are - # susceptible to man-in-the-middle attacks. Use only for testing. Default is - # `full`. - #ssl.verification_mode: full - - # List of supported/valid TLS versions. By default all TLS versions from 1.1 - # up to 1.3 are enabled. - #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] - - # SSL configuration. The default is off. - # List of root certificates for HTTPS server verifications - #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # Certificate for SSL client authentication - #ssl.certificate: "/etc/pki/client/cert.pem" - - # Client certificate key - #ssl.key: "/etc/pki/client/cert.key" - - # Optional passphrase for decrypting the certificate key. - #ssl.key_passphrase: '' - - # Configure cipher suites to be used for SSL connections - #ssl.cipher_suites: [] - - # Configure curve types for ECDHE-based cipher suites - #ssl.curve_types: [] - - # Configure what types of renegotiation are supported. Valid options are - # never, once, and freely. Default is never. - #ssl.renegotiation: never - - #metrics.period: 10s - #state.period: 1m - -# The `monitoring.cloud.id` setting overwrites the `monitoring.elasticsearch.hosts` -# setting. You can find the value for this setting in the Elastic Cloud web UI. -#monitoring.cloud.id: - -# The `monitoring.cloud.auth` setting overwrites the `monitoring.elasticsearch.username` -# and `monitoring.elasticsearch.password` settings. The format is `:`. -#monitoring.cloud.auth: - -#================================ HTTP Endpoint ====================================== -# Each beat can expose internal metrics through a HTTP endpoint. For security -# reasons the endpoint is disabled by default. This feature is currently experimental. -# Stats can be access through http://localhost:5066/stats . For pretty JSON output -# append ?pretty to the URL. - -# Defines if the HTTP endpoint is enabled. -#http.enabled: false - -# The HTTP endpoint will bind to this hostname, IP address, unix socket or named pipe. -# When using IP addresses, it is recommended to only use localhost. -#http.host: localhost - -# Port on which the HTTP endpoint will bind. Default is 5066. -#http.port: 5066 - -# Define which user should be owning the named pipe. -#http.named_pipe.user: - -# Define which the permissions that should be applied to the named pipe, use the Security -# Descriptor Definition Language (SDDL) to define the permission. This option cannot be used with -# `http.user`. -#http.named_pipe.security_descriptor: - -#============================= Process Security ================================ - -# Enable or disable seccomp system call filtering on Linux. Default is enabled. -#seccomp.enabled: true - -#================================= Migration ================================== - -# This allows to enable 6.7 migration aliases -#migration.6_to_7.enabled: false diff --git a/libbeat/_meta/config.yml.tmpl b/libbeat/_meta/config.yml.tmpl index 2d5e510e33f..3cf0e3e00af 100644 --- a/libbeat/_meta/config.yml.tmpl +++ b/libbeat/_meta/config.yml.tmpl @@ -90,6 +90,7 @@ output.elasticsearch: #ssl.key: "/etc/pki/client/cert.key" {{end}} #================================ Processors ===================================== +{{if .UseProcessorsTemplate}}{{template "processors.yml.tmpl" .}}{{else -}} {{if not .UseObserverProcessor}} # Configure processors to enhance or manipulate events generated by the beat. @@ -112,7 +113,7 @@ processors: #name: us-east-1a # Lat, Lon " #location: "37.926868, -78.024902" -{{end}} +{{end}}{{end}} #================================ Logging ===================================== # Sets log level. The default log level is info. diff --git a/libbeat/_meta/config.docker.yml b/libbeat/_meta/config/default.docker.yml.tmpl similarity index 83% rename from libbeat/_meta/config.docker.yml rename to libbeat/_meta/config/default.docker.yml.tmpl index 6ce79dc1c42..720aafc117d 100644 --- a/libbeat/_meta/config.docker.yml +++ b/libbeat/_meta/config/default.docker.yml.tmpl @@ -1,3 +1,4 @@ +{{block "beat.docker.yml.tmpl" .}}{{end}} processors: - add_cloud_metadata: ~ - add_docker_metadata: ~ diff --git a/libbeat/_meta/config/default.reference.yml.tmpl b/libbeat/_meta/config/default.reference.yml.tmpl new file mode 100644 index 00000000000..c7c75d51cf4 --- /dev/null +++ b/libbeat/_meta/config/default.reference.yml.tmpl @@ -0,0 +1,22 @@ +{{block "beat.reference.yml.tmpl" . }}{{end}} +{{template "general.reference.yml.tmpl" .}} +{{template "processors.reference.yml.tmpl" .}} +{{template "elastic-cloud.yml.tmpl" .}} +{{template "outputs.yml.tmpl" .}} +{{template "output-elasticsearch.reference.yml.tmpl" .}} +{{template "output-logstash.reference.yml.tmpl" .}} +{{if not .ExcludeKafka}}{{template "output-kafka.reference.yml.tmpl" .}}{{end}} +{{if not .ExcludeRedis}}{{template "output-redis.reference.yml.tmpl" .}}{{end}} +{{if not .ExcludeFileOutput}}{{template "output-file.reference.yml.tmpl" .}}{{end}} +{{if not .ExcludeConsole}}{{template "output-console.reference.yml.tmpl" .}}{{end}} +{{template "paths.reference.yml.tmpl" .}} +{{template "keystore.reference.yml.tmpl" .}} +{{template "setup.dashboards.reference.yml.tmpl" .}} +{{template "setup.template.reference.yml.tmpl" .}} +{{template "setup.ilm.reference.yml.tmpl" .}} +{{template "setup.kibana.reference.yml.tmpl" .}} +{{template "logging.reference.yml.tmpl" .}} +{{template "monitoring.reference.yml.tmpl" .}} +{{template "http.reference.yml.tmpl" .}} +{{template "seccomp.reference.yml.tmpl" .}} +{{template "migration.yml.tmpl" .}} diff --git a/libbeat/_meta/config/default.short.yml.tmpl b/libbeat/_meta/config/default.short.yml.tmpl new file mode 100644 index 00000000000..c18d2abd8a5 --- /dev/null +++ b/libbeat/_meta/config/default.short.yml.tmpl @@ -0,0 +1,12 @@ +{{block "beat.yml.tmpl" .}}{{end}} +{{template "general.yml.tmpl" .}} +{{if not .ExcludeDashboards}}{{template "setup.dashboards.yml.tmpl" .}}{{end}} +{{template "setup.kibana.yml.tmpl" .}} +{{template "elastic-cloud.yml.tmpl" .}} +{{template "outputs.yml.tmpl" .}} +{{template "output-elasticsearch.yml.tmpl" .}} +{{template "output-logstash.yml.tmpl" .}} +{{template "processors.yml.tmpl" .}} +{{template "logging.yml.tmpl" .}} +{{template "monitoring.yml.tmpl" .}} +{{template "migration.yml.tmpl" .}} diff --git a/libbeat/_meta/config/elastic-cloud.yml.tmpl b/libbeat/_meta/config/elastic-cloud.yml.tmpl new file mode 100644 index 00000000000..f736f6ff659 --- /dev/null +++ b/libbeat/_meta/config/elastic-cloud.yml.tmpl @@ -0,0 +1,12 @@ +{{header "Elastic Cloud"}} + +# These settings simplify using {{ .BeatName | title }} with the Elastic Cloud (https://cloud.elastic.co/). + +# The cloud.id setting overwrites the `output.elasticsearch.hosts` and +# `setup.kibana.host` options. +# You can find the `cloud.id` in the Elastic Cloud web UI. +#cloud.id: + +# The cloud.auth setting overwrites the `output.elasticsearch.username` and +# `output.elasticsearch.password` settings. The format is `:`. +#cloud.auth: diff --git a/libbeat/_meta/config/general.reference.yml.tmpl b/libbeat/_meta/config/general.reference.yml.tmpl new file mode 100644 index 00000000000..8500d01c39e --- /dev/null +++ b/libbeat/_meta/config/general.reference.yml.tmpl @@ -0,0 +1,107 @@ +{{header "General"}} + +# The name of the shipper that publishes the network data. It can be used to group +# all the transactions sent by a single shipper in the web interface. +# If this options is not defined, the hostname is used. +#name: + +# The tags of the shipper are included in their own field with each +# transaction published. Tags make it easy to group servers by different +# logical properties. +#tags: ["service-X", "web-tier"] + +# Optional fields that you can specify to add additional information to the +# output. Fields can be scalar values, arrays, dictionaries, or any nested +# combination of these. +#fields: +# env: staging + +# If this option is set to true, the custom fields are stored as top-level +# fields in the output document instead of being grouped under a fields +# sub-dictionary. Default is false. +#fields_under_root: false + +# Internal queue configuration for buffering events to be published. +#queue: + # Queue type by name (default 'mem') + # The memory queue will present all available events (up to the outputs + # bulk_max_size) to the output, the moment the output is ready to server + # another batch of events. + #mem: + # Max number of events the queue can buffer. + #events: 4096 + + # Hints the minimum number of events stored in the queue, + # before providing a batch of events to the outputs. + # The default value is set to 2048. + # A value of 0 ensures events are immediately available + # to be sent to the outputs. + #flush.min_events: 2048 + + # Maximum duration after which events are available to the outputs, + # if the number of events stored in the queue is < `flush.min_events`. + #flush.timeout: 1s + + # The spool queue will store events in a local spool file, before + # forwarding the events to the outputs. + # + # Beta: spooling to disk is currently a beta feature. Use with care. + # + # The spool file is a circular buffer, which blocks once the file/buffer is full. + # Events are put into a write buffer and flushed once the write buffer + # is full or the flush_timeout is triggered. + # Once ACKed by the output, events are removed immediately from the queue, + # making space for new events to be persisted. + #spool: + # The file namespace configures the file path and the file creation settings. + # Once the file exists, the `size`, `page_size` and `prealloc` settings + # will have no more effect. + #file: + # Location of spool file. The default value is ${path.data}/spool.dat. + #path: "${path.data}/spool.dat" + + # Configure file permissions if file is created. The default value is 0600. + #permissions: 0600 + + # File size hint. The spool blocks, once this limit is reached. The default value is 100 MiB. + #size: 100MiB + + # The files page size. A file is split into multiple pages of the same size. The default value is 4KiB. + #page_size: 4KiB + + # If prealloc is set, the required space for the file is reserved using + # truncate. The default value is true. + #prealloc: true + + # Spool writer settings + # Events are serialized into a write buffer. The write buffer is flushed if: + # - The buffer limit has been reached. + # - The configured limit of buffered events is reached. + # - The flush timeout is triggered. + #write: + # Sets the write buffer size. + #buffer_size: 1MiB + + # Maximum duration after which events are flushed if the write buffer + # is not full yet. The default value is 1s. + #flush.timeout: 1s + + # Number of maximum buffered events. The write buffer is flushed once the + # limit is reached. + #flush.events: 16384 + + # Configure the on-disk event encoding. The encoding can be changed + # between restarts. + # Valid encodings are: json, ubjson, and cbor. + #codec: cbor + #read: + # Reader flush timeout, waiting for more events to become available, so + # to fill a complete batch as required by the outputs. + # If flush_timeout is 0, all available events are forwarded to the + # outputs immediately. + # The default value is 0s. + #flush.timeout: 0s + +# Sets the maximum number of CPUs that can be executing simultaneously. The +# default is the number of logical CPUs available in the system. +#max_procs: diff --git a/libbeat/_meta/config/general.yml.tmpl b/libbeat/_meta/config/general.yml.tmpl new file mode 100644 index 00000000000..1c85044c1b9 --- /dev/null +++ b/libbeat/_meta/config/general.yml.tmpl @@ -0,0 +1,14 @@ +{{header "General"}} + +# The name of the shipper that publishes the network data. It can be used to group +# all the transactions sent by a single shipper in the web interface. +#name: + +# The tags of the shipper are included in their own field with each +# transaction published. +#tags: ["service-X", "web-tier"] + +# Optional fields that you can specify to add additional information to the +# output. +#fields: +# env: staging diff --git a/libbeat/_meta/config/http.reference.yml.tmpl b/libbeat/_meta/config/http.reference.yml.tmpl new file mode 100644 index 00000000000..19a9f5fcd50 --- /dev/null +++ b/libbeat/_meta/config/http.reference.yml.tmpl @@ -0,0 +1,24 @@ +{{header "HTTP Endpoint"}} + +# Each beat can expose internal metrics through a HTTP endpoint. For security +# reasons the endpoint is disabled by default. This feature is currently experimental. +# Stats can be access through http://localhost:5066/stats . For pretty JSON output +# append ?pretty to the URL. + +# Defines if the HTTP endpoint is enabled. +#http.enabled: false + +# The HTTP endpoint will bind to this hostname, IP address, unix socket or named pipe. +# When using IP addresses, it is recommended to only use localhost. +#http.host: localhost + +# Port on which the HTTP endpoint will bind. Default is 5066. +#http.port: 5066 + +# Define which user should be owning the named pipe. +#http.named_pipe.user: + +# Define which the permissions that should be applied to the named pipe, use the Security +# Descriptor Definition Language (SDDL) to define the permission. This option cannot be used with +# `http.user`. +#http.named_pipe.security_descriptor: diff --git a/libbeat/_meta/config/keystore.reference.yml.tmpl b/libbeat/_meta/config/keystore.reference.yml.tmpl new file mode 100644 index 00000000000..2c4de9757a1 --- /dev/null +++ b/libbeat/_meta/config/keystore.reference.yml.tmpl @@ -0,0 +1,4 @@ +{{header "Keystore"}} + +# Location of the Keystore containing the keys and their sensitive values. +#keystore.path: "${path.config}/beats.keystore" diff --git a/libbeat/_meta/config/logging.reference.yml.tmpl b/libbeat/_meta/config/logging.reference.yml.tmpl new file mode 100644 index 00000000000..0c3000dc060 --- /dev/null +++ b/libbeat/_meta/config/logging.reference.yml.tmpl @@ -0,0 +1,67 @@ +{{header "Logging"}} + +# There are four options for the log output: file, stderr, syslog, eventlog +# The file output is the default. + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: info + +# Enable debug output for selected components. To enable all selectors use ["*"] +# Other available selectors are "beat", "publish", "service" +# Multiple selectors can be chained. +#logging.selectors: [ ] + +# Send all logging output to stderr. The default is false. +#logging.to_stderr: false + +# Send all logging output to syslog. The default is false. +#logging.to_syslog: false + +# Send all logging output to Windows Event Logs. The default is false. +#logging.to_eventlog: false + +# If enabled, {{.BeatName | title}} periodically logs its internal metrics that have changed +# in the last period. For each metric that changed, the delta from the value at +# the beginning of the period is logged. Also, the total values for +# all non-zero internal metrics are logged on shutdown. The default is true. +#logging.metrics.enabled: true + +# The period after which to log the internal metrics. The default is 30s. +#logging.metrics.period: 30s + +# Logging to rotating files. Set logging.to_files to false to disable logging to +# files. +logging.to_files: true +logging.files: + # Configure the path where the logs are written. The default is the logs directory + # under the home path (the binary location). + #path: /var/log/{{.BeatName}} + + # The name of the files where the logs are written to. + #name: {{.BeatName}} + + # Configure log file size limit. If limit is reached, log file will be + # automatically rotated + #rotateeverybytes: 10485760 # = 10MB + + # Number of rotated log files to keep. Oldest files will be deleted first. + #keepfiles: 7 + + # The permissions mask to apply when rotating log files. The default value is 0600. + # Must be a valid Unix-style file permissions mask expressed in octal notation. + #permissions: 0600 + + # Enable log file rotation on time intervals in addition to size-based rotation. + # Intervals must be at least 1s. Values of 1m, 1h, 24h, 7*24h, 30*24h, and 365*24h + # are boundary-aligned with minutes, hours, days, weeks, months, and years as + # reported by the local system clock. All other intervals are calculated from the + # Unix epoch. Defaults to disabled. + #interval: 0 + + # Rotate existing logs on startup rather than appending to the existing + # file. Defaults to true. + # rotateonstartup: true + +# Set to true to log messages in JSON format. +#logging.json: false diff --git a/libbeat/_meta/config/logging.yml.tmpl b/libbeat/_meta/config/logging.yml.tmpl new file mode 100644 index 00000000000..a639acc76ab --- /dev/null +++ b/libbeat/_meta/config/logging.yml.tmpl @@ -0,0 +1,10 @@ +{{header "Logging"}} + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: debug + +# At debug level, you can selectively enable logging only for some components. +# To enable all selectors use ["*"]. Examples of other selectors are "beat", +# "publish", "service". +#logging.selectors: ["*"] diff --git a/libbeat/_meta/config/migration.yml.tmpl b/libbeat/_meta/config/migration.yml.tmpl new file mode 100644 index 00000000000..8abeab86be4 --- /dev/null +++ b/libbeat/_meta/config/migration.yml.tmpl @@ -0,0 +1,4 @@ +{{header "Migration"}} + +# This allows to enable 6.7 migration aliases +#migration.6_to_7.enabled: {{not .Reference}} diff --git a/libbeat/_meta/config/monitoring.reference.yml.tmpl b/libbeat/_meta/config/monitoring.reference.yml.tmpl new file mode 100644 index 00000000000..187b92678eb --- /dev/null +++ b/libbeat/_meta/config/monitoring.reference.yml.tmpl @@ -0,0 +1,141 @@ +{{header "X-Pack Monitoring"}} +# {{.BeatName | title}} can export internal metrics to a central Elasticsearch monitoring +# cluster. This requires xpack monitoring to be enabled in Elasticsearch. The +# reporting is disabled by default. + +# Set to true to enable the monitoring reporter. +#monitoring.enabled: false + +# Sets the UUID of the Elasticsearch cluster under which monitoring data for this +# {{.BeatName | title}} instance will appear in the Stack Monitoring UI. If output.elasticsearch +# is enabled, the UUID is derived from the Elasticsearch cluster referenced by output.elasticsearch. +#monitoring.cluster_uuid: + +# Uncomment to send the metrics to Elasticsearch. Most settings from the +# Elasticsearch output are accepted here as well. +# Note that the settings should point to your Elasticsearch *monitoring* cluster. +# Any setting that is not set is automatically inherited from the Elasticsearch +# output configuration, so if you have the Elasticsearch output configured such +# that it is pointing to your Elasticsearch monitoring cluster, you can simply +# uncomment the following line. +#monitoring.elasticsearch: + + # Array of hosts to connect to. + # Scheme and port can be left out and will be set to the default (http and 9200) + # In case you specify and additional path, the scheme is required: http://localhost:9200/path + # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 + #hosts: ["localhost:9200"] + + # Set gzip compression level. + #compression_level: 0 + + # Protocol - either `http` (default) or `https`. + #protocol: "https" + + # Authentication credentials - either API key or username/password. + #api_key: "id:api_key" + #username: "beats_system" + #password: "changeme" + + # Dictionary of HTTP parameters to pass within the URL with index operations. + #parameters: + #param1: value1 + #param2: value2 + + # Custom HTTP headers to add to each request + #headers: + # X-My-Header: Contents of the header + + # Proxy server url + #proxy_url: http://proxy:3128 + + # The number of times a particular Elasticsearch index operation is attempted. If + # the indexing operation doesn't succeed after this many retries, the events are + # dropped. The default is 3. + #max_retries: 3 + + # The maximum number of events to bulk in a single Elasticsearch bulk API index request. + # The default is 50. + #bulk_max_size: 50 + + # The number of seconds to wait before trying to reconnect to Elasticsearch + # after a network error. After waiting backoff.init seconds, the Beat + # tries to reconnect. If the attempt fails, the backoff timer is increased + # exponentially up to backoff.max. After a successful connection, the backoff + # timer is reset. The default is 1s. + #backoff.init: 1s + + # The maximum number of seconds to wait before attempting to connect to + # Elasticsearch after a network error. The default is 60s. + #backoff.max: 60s + + # Configure HTTP request timeout before failing an request to Elasticsearch. + #timeout: 90 + + # Use SSL settings for HTTPS. + #ssl.enabled: true + + # Configure SSL verification mode. If `none` is configured, all server hosts + # and certificates will be accepted. In this mode, SSL based connections are + # susceptible to man-in-the-middle attacks. Use only for testing. Default is + # `full`. + #ssl.verification_mode: full + + # List of supported/valid TLS versions. By default all TLS versions from 1.1 + # up to 1.3 are enabled. + #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] + + # SSL configuration. The default is off. + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" + + # Client certificate key + #ssl.key: "/etc/pki/client/cert.key" + + # Optional passphrase for decrypting the certificate key. + #ssl.key_passphrase: '' + + # Configure cipher suites to be used for SSL connections + #ssl.cipher_suites: [] + + # Configure curve types for ECDHE-based cipher suites + #ssl.curve_types: [] + + # Configure what types of renegotiation are supported. Valid options are + # never, once, and freely. Default is never. + #ssl.renegotiation: never + + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + + #metrics.period: 10s + #state.period: 1m + +# The `monitoring.cloud.id` setting overwrites the `monitoring.elasticsearch.hosts` +# setting. You can find the value for this setting in the Elastic Cloud web UI. +#monitoring.cloud.id: + +# The `monitoring.cloud.auth` setting overwrites the `monitoring.elasticsearch.username` +# and `monitoring.elasticsearch.password` settings. The format is `:`. +#monitoring.cloud.auth: diff --git a/libbeat/_meta/config/monitoring.yml.tmpl b/libbeat/_meta/config/monitoring.yml.tmpl new file mode 100644 index 00000000000..6253cb167d5 --- /dev/null +++ b/libbeat/_meta/config/monitoring.yml.tmpl @@ -0,0 +1,21 @@ +{{header "X-Pack Monitoring"}} +# {{.BeatName | title }} can export internal metrics to a central Elasticsearch monitoring +# cluster. This requires xpack monitoring to be enabled in Elasticsearch. The +# reporting is disabled by default. + +# Set to true to enable the monitoring reporter. +#monitoring.enabled: false + +# Sets the UUID of the Elasticsearch cluster under which monitoring data for this +# {{ .BeatName | title }} instance will appear in the Stack Monitoring UI. If output.elasticsearch +# is enabled, the UUID is derived from the Elasticsearch cluster referenced by output.elasticsearch. +#monitoring.cluster_uuid: + +# Uncomment to send the metrics to Elasticsearch. Most settings from the +# Elasticsearch output are accepted here as well. +# Note that the settings should point to your Elasticsearch *monitoring* cluster. +# Any setting that is not set is automatically inherited from the Elasticsearch +# output configuration, so if you have the Elasticsearch output configured such +# that it is pointing to your Elasticsearch monitoring cluster, you can simply +# uncomment the following line. +#monitoring.elasticsearch: diff --git a/libbeat/_meta/config/output-console.reference.yml.tmpl b/libbeat/_meta/config/output-console.reference.yml.tmpl new file mode 100644 index 00000000000..163e9382807 --- /dev/null +++ b/libbeat/_meta/config/output-console.reference.yml.tmpl @@ -0,0 +1,12 @@ +{{subheader "Console Output"}} +#output.console: + # Boolean flag to enable or disable the output module. + #enabled: true + + # Configure JSON encoding + #codec.json: + # Pretty-print JSON event + #pretty: false + + # Configure escaping HTML symbols in strings. + #escape_html: false diff --git a/libbeat/_meta/config/output-elasticsearch.reference.yml.tmpl b/libbeat/_meta/config/output-elasticsearch.reference.yml.tmpl new file mode 100644 index 00000000000..5de92febf3c --- /dev/null +++ b/libbeat/_meta/config/output-elasticsearch.reference.yml.tmpl @@ -0,0 +1,140 @@ +{{subheader "Elasticsearch Output"}} +output.elasticsearch: + # Boolean flag to enable or disable the output module. + #enabled: true + + # Array of hosts to connect to. + # Scheme and port can be left out and will be set to the default (http and 9200) + # In case you specify and additional path, the scheme is required: http://localhost:9200/path + # IPv6 addresses should always be defined as: https://[2001:db8::1]:9200 + hosts: ["localhost:9200"] + + # Set gzip compression level. + #compression_level: 0 + + # Configure escaping HTML symbols in strings. + #escape_html: false + + # Protocol - either `http` (default) or `https`. + #protocol: "https" + + # Authentication credentials - either API key or username/password. + #api_key: "id:api_key" + #username: "elastic" + #password: "changeme" + + # Dictionary of HTTP parameters to pass within the URL with index operations. + #parameters: + #param1: value1 + #param2: value2 + + # Number of workers per Elasticsearch host. + #worker: 1 + + # Optional index name. The default is "{{.BeatIndexPrefix}}" plus date + # and generates [{{.BeatIndexPrefix}}-]YYYY.MM.DD keys. + # In case you modify this pattern you must update setup.template.name and setup.template.pattern accordingly. + #index: "{{.BeatIndexPrefix}}-%{[agent.version]}-%{+yyyy.MM.dd}" + + # Optional ingest node pipeline. By default no pipeline will be used. + #pipeline: "" + + # Optional HTTP path + #path: "/elasticsearch" + + # Custom HTTP headers to add to each request + #headers: + # X-My-Header: Contents of the header + + # Proxy server URL + #proxy_url: http://proxy:3128 + + # Whether to disable proxy settings for outgoing connections. If true, this + # takes precedence over both the proxy_url field and any environment settings + # (HTTP_PROXY, HTTPS_PROXY). The default is false. + #proxy_disable: false + + # The number of times a particular Elasticsearch index operation is attempted. If + # the indexing operation doesn't succeed after this many retries, the events are + # dropped. The default is 3. + #max_retries: 3 + + # The maximum number of events to bulk in a single Elasticsearch bulk API index request. + # The default is 50. + #bulk_max_size: 50 + + # The number of seconds to wait before trying to reconnect to Elasticsearch + # after a network error. After waiting backoff.init seconds, the Beat + # tries to reconnect. If the attempt fails, the backoff timer is increased + # exponentially up to backoff.max. After a successful connection, the backoff + # timer is reset. The default is 1s. + #backoff.init: 1s + + # The maximum number of seconds to wait before attempting to connect to + # Elasticsearch after a network error. The default is 60s. + #backoff.max: 60s + + # Configure HTTP request timeout before failing a request to Elasticsearch. + #timeout: 90 + + # Use SSL settings for HTTPS. + #ssl.enabled: true + + # Configure SSL verification mode. If `none` is configured, all server hosts + # and certificates will be accepted. In this mode, SSL-based connections are + # susceptible to man-in-the-middle attacks. Use only for testing. Default is + # `full`. + #ssl.verification_mode: full + + # List of supported/valid TLS versions. By default all TLS versions from 1.1 + # up to 1.3 are enabled. + #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] + + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" + + # Client certificate key + #ssl.key: "/etc/pki/client/cert.key" + + # Optional passphrase for decrypting the certificate key. + #ssl.key_passphrase: '' + + # Configure cipher suites to be used for SSL connections + #ssl.cipher_suites: [] + + # Configure curve types for ECDHE-based cipher suites + #ssl.curve_types: [] + + # Configure what types of renegotiation are supported. Valid options are + # never, once, and freely. Default is never. + #ssl.renegotiation: never + + # Configure a pin that can be used to do extra validation of the verified certificate chain, + # this allow you to ensure that a specific certificate is used to validate the chain of trust. + # + # The pin is a base64 encoded string of the SHA-256 fingerprint. + #ssl.ca_sha256: "" + + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC diff --git a/libbeat/_meta/config/output-elasticsearch.yml.tmpl b/libbeat/_meta/config/output-elasticsearch.yml.tmpl new file mode 100644 index 00000000000..7c1287e01a1 --- /dev/null +++ b/libbeat/_meta/config/output-elasticsearch.yml.tmpl @@ -0,0 +1,12 @@ +{{subheader "Elasticsearch Output"}} +output.elasticsearch: + # Array of hosts to connect to. + hosts: ["localhost:9200"] + + # Protocol - either `http` (default) or `https`. + #protocol: "https" + + # Authentication credentials - either API key or username/password. + #api_key: "id:api_key" + #username: "elastic" + #password: "changeme" diff --git a/libbeat/_meta/config/output-file.reference.yml.tmpl b/libbeat/_meta/config/output-file.reference.yml.tmpl new file mode 100644 index 00000000000..2c383444107 --- /dev/null +++ b/libbeat/_meta/config/output-file.reference.yml.tmpl @@ -0,0 +1,33 @@ +{{subheader "File Output"}} +#output.file: + # Boolean flag to enable or disable the output module. + #enabled: true + + # Configure JSON encoding + #codec.json: + # Pretty-print JSON event + #pretty: false + + # Configure escaping HTML symbols in strings. + #escape_html: false + + # Path to the directory where to save the generated files. The option is + # mandatory. + #path: "/tmp/{{.BeatName}}" + + # Name of the generated files. The default is `{{.BeatName}}` and it generates + # files: `{{.BeatName}}`, `{{.BeatName}}.1`, `{{.BeatName}}.2`, etc. + #filename: {{.BeatName}} + + # Maximum size in kilobytes of each file. When this size is reached, and on + # every {{.BeatName | title}} restart, the files are rotated. The default value is 10240 + # kB. + #rotate_every_kb: 10000 + + # Maximum number of files under path. When this number of files is reached, + # the oldest file is deleted and the rest are shifted from last to first. The + # default is 7 files. + #number_of_files: 7 + + # Permissions to use for file creation. The default is 0600. + #permissions: 0600 diff --git a/libbeat/_meta/config/output-kafka.reference.yml.tmpl b/libbeat/_meta/config/output-kafka.reference.yml.tmpl new file mode 100644 index 00000000000..50efb87fbb1 --- /dev/null +++ b/libbeat/_meta/config/output-kafka.reference.yml.tmpl @@ -0,0 +1,178 @@ +{{subheader "Kafka Output"}} +#output.kafka: + # Boolean flag to enable or disable the output module. + #enabled: true + + # The list of Kafka broker addresses from which to fetch the cluster metadata. + # The cluster metadata contain the actual Kafka brokers events are published + # to. + #hosts: ["localhost:9092"] + + # The Kafka topic used for produced events. The setting can be a format string + # using any event field. To set the topic from document type use `%{[type]}`. + #topic: beats + + # The Kafka event key setting. Use format string to create a unique event key. + # By default no event key will be generated. + #key: '' + + # The Kafka event partitioning strategy. Default hashing strategy is `hash` + # using the `output.kafka.key` setting or randomly distributes events if + # `output.kafka.key` is not configured. + #partition.hash: + # If enabled, events will only be published to partitions with reachable + # leaders. Default is false. + #reachable_only: false + + # Configure alternative event field names used to compute the hash value. + # If empty `output.kafka.key` setting will be used. + # Default value is empty list. + #hash: [] + + # Authentication details. Password is required if username is set. + #username: '' + #password: '' + + # Kafka version {{.BeatName | title}} is assumed to run against. Defaults to the "1.0.0". + #version: '1.0.0' + + # Configure JSON encoding + #codec.json: + # Pretty-print JSON event + #pretty: false + + # Configure escaping HTML symbols in strings. + #escape_html: false + + # Metadata update configuration. Metadata contains leader information + # used to decide which broker to use when publishing. + #metadata: + # Max metadata request retry attempts when cluster is in middle of leader + # election. Defaults to 3 retries. + #retry.max: 3 + + # Wait time between retries during leader elections. Default is 250ms. + #retry.backoff: 250ms + + # Refresh metadata interval. Defaults to every 10 minutes. + #refresh_frequency: 10m + + # Strategy for fetching the topics metadata from the broker. Default is false. + #full: false + + # The number of concurrent load-balanced Kafka output workers. + #worker: 1 + + # The number of times to retry publishing an event after a publishing failure. + # After the specified number of retries, events are typically dropped. + # Some Beats, such as Filebeat, ignore the max_retries setting and retry until + # all events are published. Set max_retries to a value less than 0 to retry + # until all events are published. The default is 3. + #max_retries: 3 + + # The maximum number of events to bulk in a single Kafka request. The default + # is 2048. + #bulk_max_size: 2048 + + # Duration to wait before sending bulk Kafka request. 0 is no delay. The default + # is 0. + #bulk_flush_frequency: 0s + + # The number of seconds to wait for responses from the Kafka brokers before + # timing out. The default is 30s. + #timeout: 30s + + # The maximum duration a broker will wait for number of required ACKs. The + # default is 10s. + #broker_timeout: 10s + + # The number of messages buffered for each Kafka broker. The default is 256. + #channel_buffer_size: 256 + + # The keep-alive period for an active network connection. If 0s, keep-alives + # are disabled. The default is 0 seconds. + #keep_alive: 0 + + # Sets the output compression codec. Must be one of none, snappy and gzip. The + # default is gzip. + #compression: gzip + + # Set the compression level. Currently only gzip provides a compression level + # between 0 and 9. The default value is chosen by the compression algorithm. + #compression_level: 4 + + # The maximum permitted size of JSON-encoded messages. Bigger messages will be + # dropped. The default value is 1000000 (bytes). This value should be equal to + # or less than the broker's message.max.bytes. + #max_message_bytes: 1000000 + + # The ACK reliability level required from broker. 0=no response, 1=wait for + # local commit, -1=wait for all replicas to commit. The default is 1. Note: + # If set to 0, no ACKs are returned by Kafka. Messages might be lost silently + # on error. + #required_acks: 1 + + # The configurable ClientID used for logging, debugging, and auditing + # purposes. The default is "beats". + #client_id: beats + + # Enable SSL support. SSL is automatically enabled if any SSL setting is set. + #ssl.enabled: true + + # Optional SSL configuration options. SSL is off by default. + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + + # Configure SSL verification mode. If `none` is configured, all server hosts + # and certificates will be accepted. In this mode, SSL based connections are + # susceptible to man-in-the-middle attacks. Use only for testing. Default is + # `full`. + #ssl.verification_mode: full + + # List of supported/valid TLS versions. By default all TLS versions from 1.1 + # up to 1.3 are enabled. + #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] + + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" + + # Client Certificate Key + #ssl.key: "/etc/pki/client/cert.key" + + # Optional passphrase for decrypting the Certificate Key. + #ssl.key_passphrase: '' + + # Configure cipher suites to be used for SSL connections + #ssl.cipher_suites: [] + + # Configure curve types for ECDHE-based cipher suites + #ssl.curve_types: [] + + # Configure what types of renegotiation are supported. Valid options are + # never, once, and freely. Default is never. + #ssl.renegotiation: never + + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/security/keytabs/kafka.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # The service name. Service principal name is contructed from + # service_name/hostname@realm. + #kerberos.service_name: kafka + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC diff --git a/libbeat/_meta/config/output-logstash.reference.yml.tmpl b/libbeat/_meta/config/output-logstash.reference.yml.tmpl new file mode 100644 index 00000000000..da182d0496e --- /dev/null +++ b/libbeat/_meta/config/output-logstash.reference.yml.tmpl @@ -0,0 +1,113 @@ +{{subheader "Logstash Output"}} +#output.logstash: + # Boolean flag to enable or disable the output module. + #enabled: true + + # The Logstash hosts + #hosts: ["localhost:5044"] + + # Number of workers per Logstash host. + #worker: 1 + + # Set gzip compression level. + #compression_level: 3 + + # Configure escaping HTML symbols in strings. + #escape_html: false + + # Optional maximum time to live for a connection to Logstash, after which the + # connection will be re-established. A value of `0s` (the default) will + # disable this feature. + # + # Not yet supported for async connections (i.e. with the "pipelining" option set) + #ttl: 30s + + # Optionally load-balance events between Logstash hosts. Default is false. + #loadbalance: false + + # Number of batches to be sent asynchronously to Logstash while processing + # new batches. + #pipelining: 2 + + # If enabled only a subset of events in a batch of events is transferred per + # transaction. The number of events to be sent increases up to `bulk_max_size` + # if no error is encountered. + #slow_start: false + + # The number of seconds to wait before trying to reconnect to Logstash + # after a network error. After waiting backoff.init seconds, the Beat + # tries to reconnect. If the attempt fails, the backoff timer is increased + # exponentially up to backoff.max. After a successful connection, the backoff + # timer is reset. The default is 1s. + #backoff.init: 1s + + # The maximum number of seconds to wait before attempting to connect to + # Logstash after a network error. The default is 60s. + #backoff.max: 60s + + # Optional index name. The default index name is set to {{.BeatIndexPrefix}} + # in all lowercase. + #index: '{{.BeatIndexPrefix}}' + + # SOCKS5 proxy server URL + #proxy_url: socks5://user:password@socks5-server:2233 + + # Resolve names locally when using a proxy server. Defaults to false. + #proxy_use_local_resolver: false + + # Enable SSL support. SSL is automatically enabled if any SSL setting is set. + #ssl.enabled: true + + # Configure SSL verification mode. If `none` is configured, all server hosts + # and certificates will be accepted. In this mode, SSL based connections are + # susceptible to man-in-the-middle attacks. Use only for testing. Default is + # `full`. + #ssl.verification_mode: full + + # List of supported/valid TLS versions. By default all TLS versions from 1.1 + # up to 1.3 are enabled. + #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] + + # Optional SSL configuration options. SSL is off by default. + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" + + # Client certificate key + #ssl.key: "/etc/pki/client/cert.key" + + # Optional passphrase for decrypting the Certificate Key. + #ssl.key_passphrase: '' + + # Configure cipher suites to be used for SSL connections + #ssl.cipher_suites: [] + + # Configure curve types for ECDHE-based cipher suites + #ssl.curve_types: [] + + # Configure what types of renegotiation are supported. Valid options are + # never, once, and freely. Default is never. + #ssl.renegotiation: never + + # Configure a pin that can be used to do extra validation of the verified certificate chain, + # this allow you to ensure that a specific certificate is used to validate the chain of trust. + # + # The pin is a base64 encoded string of the SHA-256 fingerprint. + #ssl.ca_sha256: "" + + # The number of times to retry publishing an event after a publishing failure. + # After the specified number of retries, the events are typically dropped. + # Some Beats, such as Filebeat and Winlogbeat, ignore the max_retries setting + # and retry until all events are published. Set max_retries to a value less + # than 0 to retry until all events are published. The default is 3. + #max_retries: 3 + + # The maximum number of events to bulk in a single Logstash request. The + # default is 2048. + #bulk_max_size: 2048 + + # The number of seconds to wait for responses from the Logstash server before + # timing out. The default is 30s. + #timeout: 30s diff --git a/libbeat/_meta/config/output-logstash.yml.tmpl b/libbeat/_meta/config/output-logstash.yml.tmpl new file mode 100644 index 00000000000..a937e0de99e --- /dev/null +++ b/libbeat/_meta/config/output-logstash.yml.tmpl @@ -0,0 +1,14 @@ +{{subheader "Logstash Output"}} +#output.logstash: + # The Logstash hosts + #hosts: ["localhost:5044"] + + # Optional SSL. By default is off. + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" + + # Client Certificate Key + #ssl.key: "/etc/pki/client/cert.key" diff --git a/libbeat/_meta/config/output-redis.reference.yml.tmpl b/libbeat/_meta/config/output-redis.reference.yml.tmpl new file mode 100644 index 00000000000..3b8fa47f292 --- /dev/null +++ b/libbeat/_meta/config/output-redis.reference.yml.tmpl @@ -0,0 +1,117 @@ +{{subheader "Redis Output"}} +#output.redis: + # Boolean flag to enable or disable the output module. + #enabled: true + + # Configure JSON encoding + #codec.json: + # Pretty print json event + #pretty: false + + # Configure escaping HTML symbols in strings. + #escape_html: false + + # The list of Redis servers to connect to. If load-balancing is enabled, the + # events are distributed to the servers in the list. If one server becomes + # unreachable, the events are distributed to the reachable servers only. + # The hosts setting supports redis and rediss urls with custom password like + # redis://:password@localhost:6379. + #hosts: ["localhost:6379"] + + # The name of the Redis list or channel the events are published to. The + # default is {{.BeatName}}. + #key: {{.BeatName}} + + # The password to authenticate to Redis with. The default is no authentication. + #password: + + # The Redis database number where the events are published. The default is 0. + #db: 0 + + # The Redis data type to use for publishing events. If the data type is list, + # the Redis RPUSH command is used. If the data type is channel, the Redis + # PUBLISH command is used. The default value is list. + #datatype: list + + # The number of workers to use for each host configured to publish events to + # Redis. Use this setting along with the loadbalance option. For example, if + # you have 2 hosts and 3 workers, in total 6 workers are started (3 for each + # host). + #worker: 1 + + # If set to true and multiple hosts or workers are configured, the output + # plugin load balances published events onto all Redis hosts. If set to false, + # the output plugin sends all events to only one host (determined at random) + # and will switch to another host if the currently selected one becomes + # unreachable. The default value is true. + #loadbalance: true + + # The Redis connection timeout in seconds. The default is 5 seconds. + #timeout: 5s + + # The number of times to retry publishing an event after a publishing failure. + # After the specified number of retries, the events are typically dropped. + # Some Beats, such as Filebeat, ignore the max_retries setting and retry until + # all events are published. Set max_retries to a value less than 0 to retry + # until all events are published. The default is 3. + #max_retries: 3 + + # The number of seconds to wait before trying to reconnect to Redis + # after a network error. After waiting backoff.init seconds, the Beat + # tries to reconnect. If the attempt fails, the backoff timer is increased + # exponentially up to backoff.max. After a successful connection, the backoff + # timer is reset. The default is 1s. + #backoff.init: 1s + + # The maximum number of seconds to wait before attempting to connect to + # Redis after a network error. The default is 60s. + #backoff.max: 60s + + # The maximum number of events to bulk in a single Redis request or pipeline. + # The default is 2048. + #bulk_max_size: 2048 + + # The URL of the SOCKS5 proxy to use when connecting to the Redis servers. The + # value must be a URL with a scheme of socks5://. + #proxy_url: + + # This option determines whether Redis hostnames are resolved locally when + # using a proxy. The default value is false, which means that name resolution + # occurs on the proxy server. + #proxy_use_local_resolver: false + + # Enable SSL support. SSL is automatically enabled, if any SSL setting is set. + #ssl.enabled: true + + # Configure SSL verification mode. If `none` is configured, all server hosts + # and certificates will be accepted. In this mode, SSL based connections are + # susceptible to man-in-the-middle attacks. Use only for testing. Default is + # `full`. + #ssl.verification_mode: full + + # List of supported/valid TLS versions. By default all TLS versions from 1.1 + # up to 1.3 are enabled. + #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] + + # Optional SSL configuration options. SSL is off by default. + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" + + # Client Certificate Key + #ssl.key: "/etc/pki/client/cert.key" + + # Optional passphrase for decrypting the Certificate Key. + #ssl.key_passphrase: '' + + # Configure cipher suites to be used for SSL connections + #ssl.cipher_suites: [] + + # Configure curve types for ECDHE based cipher suites + #ssl.curve_types: [] + + # Configure what types of renegotiation are supported. Valid options are + # never, once, and freely. Default is never. + #ssl.renegotiation: never diff --git a/libbeat/_meta/config/outputs.yml.tmpl b/libbeat/_meta/config/outputs.yml.tmpl new file mode 100644 index 00000000000..662c88cfbcf --- /dev/null +++ b/libbeat/_meta/config/outputs.yml.tmpl @@ -0,0 +1,3 @@ +{{header "Outputs"}} + +# Configure what output to use when sending the data collected by the beat. diff --git a/libbeat/_meta/config/paths.reference.yml.tmpl b/libbeat/_meta/config/paths.reference.yml.tmpl new file mode 100644 index 00000000000..7907777f5e1 --- /dev/null +++ b/libbeat/_meta/config/paths.reference.yml.tmpl @@ -0,0 +1,25 @@ +{{header "Paths"}} + +# The home path for the {{.BeatName | title}} installation. This is the default base path +# for all other path settings and for miscellaneous files that come with the +# distribution (for example, the sample dashboards). +# If not set by a CLI flag or in the configuration file, the default for the +# home path is the location of the binary. +#path.home: + +# The configuration path for the {{.BeatName | title}} installation. This is the default +# base path for configuration files, including the main YAML configuration file +# and the Elasticsearch template file. If not set by a CLI flag or in the +# configuration file, the default for the configuration path is the home path. +#path.config: ${path.home} + +# The data path for the {{.BeatName | title}} installation. This is the default base path +# for all the files in which {{.BeatName | title}} needs to store its data. If not set by a +# CLI flag or in the configuration file, the default for the data path is a data +# subdirectory inside the home path. +#path.data: ${path.home}/data + +# The logs path for a {{.BeatName | title}} installation. This is the default location for +# the Beat's log files. If not set by a CLI flag or in the configuration file, +# the default for the logs path is a logs subdirectory inside the home path. +#path.logs: ${path.home}/logs diff --git a/libbeat/_meta/config/processors.reference.yml.tmpl b/libbeat/_meta/config/processors.reference.yml.tmpl new file mode 100644 index 00000000000..c7c081b49bb --- /dev/null +++ b/libbeat/_meta/config/processors.reference.yml.tmpl @@ -0,0 +1,162 @@ +{{header "Processors"}} + +# Processors are used to reduce the number of fields in the exported event or to +# enhance the event with external metadata. This section defines a list of +# processors that are applied one by one and the first one receives the initial +# event: +# +# event -> filter1 -> event1 -> filter2 ->event2 ... +# +# The supported processors are drop_fields, drop_event, include_fields, +# decode_json_fields, and add_cloud_metadata. +# +# For example, you can use the following processors to keep the fields that +# contain CPU load percentages, but remove the fields that contain CPU ticks +# values: +# +#processors: +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] +# +# The following example drops the events that have the HTTP response code 200: +# +#processors: +# - drop_event: +# when: +# equals: +# http.code: 200 +# +# The following example renames the field a to b: +# +#processors: +# - rename: +# fields: +# - from: "a" +# to: "b" +# +# The following example tokenizes the string into fields: +# +#processors: +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" +# +# The following example enriches each event with metadata from the cloud +# provider about the host machine. It works on EC2, GCE, DigitalOcean, +# Tencent Cloud, and Alibaba Cloud. +# +#processors: +# - add_cloud_metadata: ~ +# +# The following example enriches each event with the machine's local time zone +# offset from UTC. +# +#processors: +# - add_locale: +# format: offset +# +# The following example enriches each event with docker metadata, it matches +# given fields to an existing container id and adds info from that container: +# +#processors: +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" +# +# The following example enriches each event with docker metadata, it matches +# container id from log path available in `source` field (by default it expects +# it to be /var/lib/docker/containers/*/*.log). +# +#processors: +# - add_docker_metadata: ~ +# +# The following example enriches each event with host metadata. +# +#processors: +# - add_host_metadata: ~ +# +# The following example enriches each event with process metadata using +# process IDs included in the event. +# +#processors: +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent +# +# The following example decodes fields containing JSON strings +# and replaces the strings with valid JSON objects. +# +#processors: +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false +# +#processors: +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true +# +# The following example copies the value of message to message_copied +# +#processors: +# - copy_fields: +# fields: +# - from: message +# to: message_copied +# fail_on_error: true +# ignore_missing: false +# +# The following example truncates the value of message to 1024 bytes +# +#processors: +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true +# +# The following example preserves the raw message under event.original +# +#processors: +# - copy_fields: +# fields: +# - from: message +# to: event.original +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true +# +# The following example URL-decodes the value of field1 to field2 +# +#processors: +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true diff --git a/libbeat/_meta/config/processors.yml.tmpl b/libbeat/_meta/config/processors.yml.tmpl new file mode 100644 index 00000000000..b935f85dbac --- /dev/null +++ b/libbeat/_meta/config/processors.yml.tmpl @@ -0,0 +1,24 @@ +{{header "Processors"}} +{{if not .UseObserverProcessor}} +# Configure processors to enhance or manipulate events generated by the beat. + +processors: + - add_host_metadata: ~ + - add_cloud_metadata: ~ +{{- if .UseDockerMetadataProcessor }} + - add_docker_metadata: ~{{ end }} + +{{- if .UseKubernetesMetadataProcessor }} + - add_kubernetes_metadata: ~ +{{- else -}} +{{ end }} +{{else}} +processors: + - add_observer_metadata: + # Optional, but recommended geo settings for the location {{ .BeatName | title }} is running in + #geo: + # Token describing this location + #name: us-east-1a + # Lat, Lon " + #location: "37.926868, -78.024902" +{{end}} diff --git a/libbeat/_meta/config/seccomp.reference.yml.tmpl b/libbeat/_meta/config/seccomp.reference.yml.tmpl new file mode 100644 index 00000000000..54edc3326e3 --- /dev/null +++ b/libbeat/_meta/config/seccomp.reference.yml.tmpl @@ -0,0 +1,4 @@ +{{header "Process Security"}} + +# Enable or disable seccomp system call filtering on Linux. Default is enabled. +#seccomp.enabled: true diff --git a/libbeat/_meta/config/setup.dashboards.reference.yml.tmpl b/libbeat/_meta/config/setup.dashboards.reference.yml.tmpl new file mode 100644 index 00000000000..1d3b0798007 --- /dev/null +++ b/libbeat/_meta/config/setup.dashboards.reference.yml.tmpl @@ -0,0 +1,44 @@ +{{header "Dashboards"}} + +# These settings control loading the sample dashboards to the Kibana index. Loading +# the dashboards are disabled by default and can be enabled either by setting the +# options here, or by using the `-setup` CLI flag or the `setup` command. +#setup.dashboards.enabled: false + +# The directory from where to read the dashboards. The default is the `kibana` +# folder in the home path. +#setup.dashboards.directory: ${path.home}/kibana + +# The URL from where to download the dashboards archive. It is used instead of +# the directory if it has a value. +#setup.dashboards.url: + +# The file archive (zip file) from where to read the dashboards. It is used instead +# of the directory when it has a value. +#setup.dashboards.file: + +# In case the archive contains the dashboards from multiple Beats, this lets you +# select which one to load. You can load all the dashboards in the archive by +# setting this to the empty string. +#setup.dashboards.beat: {{.BeatName}} + +# The name of the Kibana index to use for setting the configuration. Default is ".kibana" +#setup.dashboards.kibana_index: .kibana + +# The Elasticsearch index name. This overwrites the index name defined in the +# dashboards and index pattern. Example: testbeat-* +#setup.dashboards.index: + +# Always use the Kibana API for loading the dashboards instead of autodetecting +# how to install the dashboards by first querying Elasticsearch. +#setup.dashboards.always_kibana: false + +# If true and Kibana is not reachable at the time when dashboards are loaded, +# it will retry to reconnect to Kibana instead of exiting with an error. +#setup.dashboards.retry.enabled: false + +# Duration interval between Kibana connection retries. +#setup.dashboards.retry.interval: 1s + +# Maximum number of retries before exiting with an error, 0 for unlimited retrying. +#setup.dashboards.retry.maximum: 0 diff --git a/libbeat/_meta/config/setup.dashboards.yml.tmpl b/libbeat/_meta/config/setup.dashboards.yml.tmpl new file mode 100644 index 00000000000..227b742a86e --- /dev/null +++ b/libbeat/_meta/config/setup.dashboards.yml.tmpl @@ -0,0 +1,11 @@ +{{header "Dashboards"}} +# These settings control loading the sample dashboards to the Kibana index. Loading +# the dashboards is disabled by default and can be enabled either by setting the +# options here or by using the `setup` command. +#setup.dashboards.enabled: false + +# The URL from where to download the dashboards archive. By default this URL +# has a value which is computed based on the Beat name and version. For released +# versions, this URL points to the dashboard archive on the artifacts.elastic.co +# website. +#setup.dashboards.url: diff --git a/libbeat/_meta/config/setup.ilm.reference.yml.tmpl b/libbeat/_meta/config/setup.ilm.reference.yml.tmpl new file mode 100644 index 00000000000..1416da0cc8d --- /dev/null +++ b/libbeat/_meta/config/setup.ilm.reference.yml.tmpl @@ -0,0 +1,34 @@ +{{header "Index Lifecycle Management (ILM)"}} + +# Configure index lifecycle management (ILM). These settings create a write +# alias and add additional settings to the index template. When ILM is enabled, +# output.elasticsearch.index is ignored, and the write alias is used to set the +# index name. + +# Enable ILM support. Valid values are true, false, and auto. When set to auto +# (the default), the Beat uses index lifecycle management when it connects to a +# cluster that supports ILM; otherwise, it creates daily indices. +#setup.ilm.enabled: auto + +# Set the prefix used in the index lifecycle write alias name. The default alias +# name is '{{.BeatName}}-%{[agent.version]}'. +#setup.ilm.rollover_alias: '{{.BeatIndexPrefix}}' + +# Set the rollover index pattern. The default is "%{now/d}-000001". +#setup.ilm.pattern: "{now/d}-000001" + +# Set the lifecycle policy name. The default policy name is +# 'beatname'. +#setup.ilm.policy_name: "mypolicy" + +# The path to a JSON file that contains a lifecycle policy configuration. Used +# to load your own lifecycle policy. +#setup.ilm.policy_file: + +# Disable the check for an existing lifecycle policy. The default is true. If +# you disable this check, set setup.ilm.overwrite: true so the lifecycle policy +# can be installed. +#setup.ilm.check_exists: true + +# Overwrite the lifecycle policy at startup. The default is false. +#setup.ilm.overwrite: false diff --git a/libbeat/_meta/config/setup.kibana.reference.yml.tmpl b/libbeat/_meta/config/setup.kibana.reference.yml.tmpl new file mode 100644 index 00000000000..603b3da4196 --- /dev/null +++ b/libbeat/_meta/config/setup.kibana.reference.yml.tmpl @@ -0,0 +1,54 @@ +{{header "Kibana"}} + +# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. +# This requires a Kibana endpoint configuration. +setup.kibana: + + # Kibana Host + # Scheme and port can be left out and will be set to the default (http and 5601) + # In case you specify and additional path, the scheme is required: http://localhost:5601/path + # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 + #host: "localhost:5601" + + # Optional protocol and basic auth credentials. + #protocol: "https" + #username: "elastic" + #password: "changeme" + + # Optional HTTP path + #path: "" + + # Optional Kibana space ID. + #space.id: "" + + # Use SSL settings for HTTPS. Default is true. + #ssl.enabled: true + + # Configure SSL verification mode. If `none` is configured, all server hosts + # and certificates will be accepted. In this mode, SSL based connections are + # susceptible to man-in-the-middle attacks. Use only for testing. Default is + # `full`. + #ssl.verification_mode: full + + # List of supported/valid TLS versions. By default all TLS versions from 1.1 + # up to 1.3 are enabled. + #ssl.supported_protocols: [TLSv1.1, TLSv1.2, TLSv1.3] + + # SSL configuration. The default is off. + # List of root certificates for HTTPS server verifications + #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + + # Certificate for SSL client authentication + #ssl.certificate: "/etc/pki/client/cert.pem" + + # Client certificate key + #ssl.key: "/etc/pki/client/cert.key" + + # Optional passphrase for decrypting the certificate key. + #ssl.key_passphrase: '' + + # Configure cipher suites to be used for SSL connections + #ssl.cipher_suites: [] + + # Configure curve types for ECDHE-based cipher suites + #ssl.curve_types: [] diff --git a/libbeat/_meta/config/setup.kibana.yml.tmpl b/libbeat/_meta/config/setup.kibana.yml.tmpl new file mode 100644 index 00000000000..6954fb814c7 --- /dev/null +++ b/libbeat/_meta/config/setup.kibana.yml.tmpl @@ -0,0 +1,16 @@ +{{header "Kibana"}} + +# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. +# This requires a Kibana endpoint configuration. +setup.kibana: + + # Kibana Host + # Scheme and port can be left out and will be set to the default (http and 5601) + # In case you specify and additional path, the scheme is required: http://localhost:5601/path + # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601 + #host: "localhost:5601" + + # Kibana Space ID + # ID of the Kibana Space into which the dashboards should be loaded. By default, + # the Default Space will be used. + #space.id: diff --git a/libbeat/_meta/config/setup.template.reference.yml.tmpl b/libbeat/_meta/config/setup.template.reference.yml.tmpl new file mode 100644 index 00000000000..48d23d9d0c9 --- /dev/null +++ b/libbeat/_meta/config/setup.template.reference.yml.tmpl @@ -0,0 +1,53 @@ +{{header "Template"}} + +# A template is used to set the mapping in Elasticsearch +# By default template loading is enabled and the template is loaded. +# These settings can be adjusted to load your own template or overwrite existing ones. + +# Set to false to disable template loading. +#setup.template.enabled: true + +# Template name. By default the template name is "{{.BeatIndexPrefix}}-%{[agent.version]}" +# The template name and pattern has to be set in case the Elasticsearch index pattern is modified. +#setup.template.name: "{{.BeatIndexPrefix}}-%{[agent.version]}" + +# Template pattern. By default the template pattern is "-%{[agent.version]}-*" to apply to the default index settings. +# The first part is the version of the beat and then -* is used to match all daily indices. +# The template name and pattern has to be set in case the Elasticsearch index pattern is modified. +#setup.template.pattern: "{{.BeatIndexPrefix}}-%{[agent.version]}-*" + +# Path to fields.yml file to generate the template +#setup.template.fields: "${path.config}/fields.yml" + +# A list of fields to be added to the template and Kibana index pattern. Also +# specify setup.template.overwrite: true to overwrite the existing template. +#setup.template.append_fields: +#- name: field_name +# type: field_type + +# Enable JSON template loading. If this is enabled, the fields.yml is ignored. +#setup.template.json.enabled: false + +# Path to the JSON template file +#setup.template.json.path: "${path.config}/template.json" + +# Name under which the template is stored in Elasticsearch +#setup.template.json.name: "" + +# Overwrite existing template +#setup.template.overwrite: false + +# Elasticsearch template settings +setup.template.settings: + + # A dictionary of settings to place into the settings.index dictionary + # of the Elasticsearch template. For more details, please check + # https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html + #index: + #number_of_shards: 1 + #codec: best_compression + + # A dictionary of settings for the _source field. For more details, please check + # https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html + #_source: + #enabled: false diff --git a/libbeat/_meta/fields.common.yml b/libbeat/_meta/fields.common.yml index fb76c921c97..c93e5b4fc42 100644 --- a/libbeat/_meta/fields.common.yml +++ b/libbeat/_meta/fields.common.yml @@ -6,7 +6,9 @@ fields: - name: agent.hostname type: keyword - description: Hostname of the agent. + description: > + Deprecated - use agent.name or agent.id to identify an agent. + Hostname of the agent. - name: beat.timezone type: alias diff --git a/libbeat/autodiscover/appenders/config/config.go b/libbeat/autodiscover/appenders/config/config.go index 018ee1b587d..60f8a543f4a 100644 --- a/libbeat/autodiscover/appenders/config/config.go +++ b/libbeat/autodiscover/appenders/config/config.go @@ -104,7 +104,7 @@ func (c *configAppender) Append(event bus.Event) { } // Apply the template - template.ApplyConfigTemplate(event, cfgs) + template.ApplyConfigTemplate(event, cfgs, false) } // Replace old config with newly appended configs diff --git a/libbeat/autodiscover/autodiscover.go b/libbeat/autodiscover/autodiscover.go index c4941c4b176..fe209e722ea 100644 --- a/libbeat/autodiscover/autodiscover.go +++ b/libbeat/autodiscover/autodiscover.go @@ -29,6 +29,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/reload" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -54,7 +55,7 @@ type EventConfigurer interface { // new modules when any configured providers does a match type Autodiscover struct { bus bus.Bus - defaultPipeline beat.Pipeline + defaultPipeline beat.PipelineConnector factory cfgfile.RunnerFactory configurer EventConfigurer providers []Provider @@ -68,10 +69,11 @@ type Autodiscover struct { // NewAutodiscover instantiates and returns a new Autodiscover manager func NewAutodiscover( name string, - pipeline beat.Pipeline, + pipeline beat.PipelineConnector, factory cfgfile.RunnerFactory, configurer EventConfigurer, config *Config, + keystore keystore.Keystore, ) (*Autodiscover, error) { logger := logp.NewLogger("autodiscover") @@ -81,7 +83,7 @@ func NewAutodiscover( // Init providers var providers []Provider for _, providerCfg := range config.Providers { - provider, err := Registry.BuildProvider(bus, providerCfg) + provider, err := Registry.BuildProvider(bus, providerCfg, keystore) if err != nil { return nil, errors.Wrap(err, "error in autodiscover provider settings") } @@ -191,10 +193,7 @@ func (a *Autodiscover) handleStart(event bus.Event) bool { if a.logger.IsDebug() { for _, c := range configs { - rc := map[string]interface{}{} - c.Unpack(&rc) - - a.logger.Debugf("Generated config: %+v", rc) + a.logger.Debugf("Generated config: %+v", common.DebugString(c, true)) } } @@ -202,7 +201,7 @@ func (a *Autodiscover) handleStart(event bus.Event) bool { for _, config := range configs { hash, err := cfgfile.HashConfig(config) if err != nil { - a.logger.Debugf("Could not hash config %v: %v", config, err) + a.logger.Debugf("Could not hash config %v: %v", common.DebugString(config, true), err) continue } @@ -216,7 +215,7 @@ func (a *Autodiscover) handleStart(event bus.Event) bool { dynFields := a.meta.Store(hash, meta) if a.configs[eventID][hash] != nil { - a.logger.Debugf("Config %v is already running", config) + a.logger.Debugf("Config %v is already running", common.DebugString(config, true)) continue } diff --git a/libbeat/autodiscover/autodiscover_test.go b/libbeat/autodiscover/autodiscover_test.go index 182cd99f3cb..23a3fe14da3 100644 --- a/libbeat/autodiscover/autodiscover_test.go +++ b/libbeat/autodiscover/autodiscover_test.go @@ -30,6 +30,7 @@ import ( "github.com/elastic/beats/v7/libbeat/cfgfile" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/tests/resources" ) @@ -142,7 +143,7 @@ func TestAutodiscover(t *testing.T) { // Register mock autodiscover provider busChan := make(chan bus.Bus, 1) Registry = NewRegistry() - Registry.AddProvider("mock", func(b bus.Bus, uuid uuid.UUID, c *common.Config) (Provider, error) { + Registry.AddProvider("mock", func(b bus.Bus, uuid uuid.UUID, c *common.Config, k keystore.Keystore) (Provider, error) { // intercept bus to mock events busChan <- b @@ -164,9 +165,9 @@ func TestAutodiscover(t *testing.T) { config := Config{ Providers: []*common.Config{providerConfig}, } - + k, _ := keystore.NewFileKeystore("test") // Create autodiscover manager - autodiscover, err := NewAutodiscover("test", nil, &adapter, &adapter, &config) + autodiscover, err := NewAutodiscover("test", nil, &adapter, &adapter, &config, k) if err != nil { t.Fatal(err) } @@ -266,7 +267,7 @@ func TestAutodiscoverHash(t *testing.T) { busChan := make(chan bus.Bus, 1) Registry = NewRegistry() - Registry.AddProvider("mock", func(b bus.Bus, uuid uuid.UUID, c *common.Config) (Provider, error) { + Registry.AddProvider("mock", func(b bus.Bus, uuid uuid.UUID, c *common.Config, k keystore.Keystore) (Provider, error) { // intercept bus to mock events busChan <- b @@ -291,9 +292,9 @@ func TestAutodiscoverHash(t *testing.T) { config := Config{ Providers: []*common.Config{providerConfig}, } - + k, _ := keystore.NewFileKeystore("test") // Create autodiscover manager - autodiscover, err := NewAutodiscover("test", nil, &adapter, &adapter, &config) + autodiscover, err := NewAutodiscover("test", nil, &adapter, &adapter, &config, k) if err != nil { t.Fatal(err) } @@ -332,7 +333,7 @@ func TestAutodiscoverWithConfigCheckFailures(t *testing.T) { // Register mock autodiscover provider busChan := make(chan bus.Bus, 1) Registry = NewRegistry() - Registry.AddProvider("mock", func(b bus.Bus, uuid uuid.UUID, c *common.Config) (Provider, error) { + Registry.AddProvider("mock", func(b bus.Bus, uuid uuid.UUID, c *common.Config, k keystore.Keystore) (Provider, error) { // intercept bus to mock events busChan <- b @@ -357,9 +358,9 @@ func TestAutodiscoverWithConfigCheckFailures(t *testing.T) { config := Config{ Providers: []*common.Config{providerConfig}, } - + k, _ := keystore.NewFileKeystore("test") // Create autodiscover manager - autodiscover, err := NewAutodiscover("test", nil, &adapter, &adapter, &config) + autodiscover, err := NewAutodiscover("test", nil, &adapter, &adapter, &config, k) if err != nil { t.Fatal(err) } diff --git a/libbeat/autodiscover/provider.go b/libbeat/autodiscover/provider.go index f7bfefc69d0..510e09ab4bf 100644 --- a/libbeat/autodiscover/provider.go +++ b/libbeat/autodiscover/provider.go @@ -26,6 +26,7 @@ import ( "github.com/elastic/beats/v7/libbeat/cfgfile" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" ) // Provider for autodiscover @@ -34,7 +35,7 @@ type Provider interface { } // ProviderBuilder creates a new provider based on the given config and returns it -type ProviderBuilder func(bus.Bus, uuid.UUID, *common.Config) (Provider, error) +type ProviderBuilder func(bus.Bus, uuid.UUID, *common.Config, keystore.Keystore) (Provider, error) // AddProvider registers a new ProviderBuilder func (r *registry) AddProvider(name string, provider ProviderBuilder) error { @@ -69,7 +70,7 @@ func (r *registry) GetProvider(name string) ProviderBuilder { } // BuildProvider reads provider configuration and instantiate one -func (r *registry) BuildProvider(bus bus.Bus, c *common.Config) (Provider, error) { +func (r *registry) BuildProvider(bus bus.Bus, c *common.Config, keystore keystore.Keystore) (Provider, error) { var config ProviderConfig err := c.Unpack(&config) if err != nil { @@ -86,5 +87,5 @@ func (r *registry) BuildProvider(bus bus.Bus, c *common.Config) (Provider, error return nil, err } - return builder(bus, uuid, c) + return builder(bus, uuid, c, keystore) } diff --git a/libbeat/autodiscover/providers/docker/docker.go b/libbeat/autodiscover/providers/docker/docker.go index 42a551ebf72..9bfa13000b1 100644 --- a/libbeat/autodiscover/providers/docker/docker.go +++ b/libbeat/autodiscover/providers/docker/docker.go @@ -33,6 +33,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/docker" "github.com/elastic/beats/v7/libbeat/common/safemapstr" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -55,10 +56,11 @@ type Provider struct { stoppers map[string]*time.Timer stopTrigger chan *dockerContainerMetadata logger *logp.Logger + keystore keystore.Keystore } // AutodiscoverBuilder builds and returns an autodiscover provider -func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodiscover.Provider, error) { +func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config, keystore keystore.Keystore) (autodiscover.Provider, error) { logger := logp.NewLogger("docker") errWrap := func(err error) error { @@ -115,6 +117,7 @@ func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodis stoppers: make(map[string]*time.Timer), stopTrigger: make(chan *dockerContainerMetadata), logger: logger, + keystore: keystore, }, nil } @@ -303,6 +306,8 @@ func (d *Provider) emitContainer(container *docker.Container, meta *dockerMetada } func (d *Provider) publish(event bus.Event) { + // attach keystore to the event to be consumed by the static configs + event["keystore"] = d.keystore // Try to match a config if config := d.templates.GetConfig(event); config != nil { event["config"] = config diff --git a/libbeat/autodiscover/providers/docker/docker_integration_test.go b/libbeat/autodiscover/providers/docker/docker_integration_test.go index b8afbafbb62..0e10af438ff 100644 --- a/libbeat/autodiscover/providers/docker/docker_integration_test.go +++ b/libbeat/autodiscover/providers/docker/docker_integration_test.go @@ -23,14 +23,14 @@ import ( "testing" "time" - "github.com/elastic/beats/v7/libbeat/autodiscover/template" - "github.com/elastic/beats/v7/libbeat/logp" - "github.com/gofrs/uuid" "github.com/stretchr/testify/assert" + "github.com/elastic/beats/v7/libbeat/autodiscover/template" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" + "github.com/elastic/beats/v7/libbeat/logp" dk "github.com/elastic/beats/v7/libbeat/tests/docker" ) @@ -53,7 +53,8 @@ func TestDockerStart(t *testing.T) { s := &template.MapperSettings{nil, nil} config.Templates = *s - provider, err := AutodiscoverBuilder(bus, UUID, common.MustNewConfigFrom(config)) + k, _ := keystore.NewFileKeystore("test") + provider, err := AutodiscoverBuilder(bus, UUID, common.MustNewConfigFrom(config), k) if err != nil { t.Fatal(err) } diff --git a/libbeat/autodiscover/providers/jolokia/jolokia.go b/libbeat/autodiscover/providers/jolokia/jolokia.go index b370d747b89..4a18ffffec9 100644 --- a/libbeat/autodiscover/providers/jolokia/jolokia.go +++ b/libbeat/autodiscover/providers/jolokia/jolokia.go @@ -27,6 +27,7 @@ import ( "github.com/elastic/beats/v7/libbeat/autodiscover/template" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" ) func init() { @@ -48,11 +49,12 @@ type Provider struct { appenders autodiscover.Appenders templates template.Mapper discovery DiscoveryProber + keystore keystore.Keystore } // AutodiscoverBuilder builds a Jolokia Discovery autodiscover provider, it fails if // there is some problem with the configuration -func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodiscover.Provider, error) { +func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config, keystore keystore.Keystore) (autodiscover.Provider, error) { errWrap := func(err error) error { return errors.Wrap(err, "error setting up jolokia autodiscover provider") } @@ -92,6 +94,7 @@ func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodis builders: builders, appenders: appenders, discovery: discovery, + keystore: keystore, }, nil } @@ -106,6 +109,8 @@ func (p *Provider) Start() { } func (p *Provider) publish(event bus.Event) { + // attach keystore to the event to be consumed by the static configs + event["keystore"] = p.keystore if config := p.templates.GetConfig(event); config != nil { event["config"] = config } else if config := p.builders.GetConfig(event); config != nil { diff --git a/libbeat/autodiscover/providers/kubernetes/kubernetes.go b/libbeat/autodiscover/providers/kubernetes/kubernetes.go index ec3480fa00b..4a4a4566f8e 100644 --- a/libbeat/autodiscover/providers/kubernetes/kubernetes.go +++ b/libbeat/autodiscover/providers/kubernetes/kubernetes.go @@ -30,6 +30,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/kubernetes" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -54,10 +55,11 @@ type Provider struct { appenders autodiscover.Appenders logger *logp.Logger eventer Eventer + keystore keystore.Keystore } // AutodiscoverBuilder builds and returns an autodiscover provider -func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodiscover.Provider, error) { +func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config, keystore keystore.Keystore) (autodiscover.Provider, error) { logger := logp.NewLogger("autodiscover") errWrap := func(err error) error { @@ -97,6 +99,7 @@ func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodis builders: builders, appenders: appenders, logger: logger, + keystore: keystore, } switch config.Resource { @@ -135,6 +138,8 @@ func (p *Provider) String() string { } func (p *Provider) publish(event bus.Event) { + // attach keystore to the event to be consumed by the static configs + event["keystore"] = p.keystore // Try to match a config if config := p.templates.GetConfig(event); config != nil { event["config"] = config diff --git a/libbeat/autodiscover/providers/kubernetes/node_test.go b/libbeat/autodiscover/providers/kubernetes/node_test.go index 0685adfe1bd..f2fbe78dba6 100644 --- a/libbeat/autodiscover/providers/kubernetes/node_test.go +++ b/libbeat/autodiscover/providers/kubernetes/node_test.go @@ -33,6 +33,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/kubernetes" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -112,6 +113,7 @@ func TestGenerateHints_Node(t *testing.T) { } func TestEmitEvent_Node(t *testing.T) { + k, _ := keystore.NewFileKeystore("test") name := "metricbeat" nodeIP := "192.168.0.1" uid := "005f3b90-4b9d-12f8-acf0-31020a840133" @@ -160,6 +162,7 @@ func TestEmitEvent_Node(t *testing.T) { "host": "192.168.0.1", "id": uid, "provider": UUID, + "keystore": k, "kubernetes": common.MapStr{ "node": common.MapStr{ "name": "metricbeat", @@ -219,6 +222,7 @@ func TestEmitEvent_Node(t *testing.T) { "host": "", "id": uid, "provider": UUID, + "keystore": k, "kubernetes": common.MapStr{ "node": common.MapStr{ "name": "metricbeat", @@ -252,6 +256,7 @@ func TestEmitEvent_Node(t *testing.T) { bus: bus.New(logp.NewLogger("bus"), "test"), templates: mapper, logger: logp.NewLogger("kubernetes"), + keystore: k, } no := &node{ diff --git a/libbeat/autodiscover/providers/kubernetes/pod_test.go b/libbeat/autodiscover/providers/kubernetes/pod_test.go index f63dbdf1e73..05b50987b2e 100644 --- a/libbeat/autodiscover/providers/kubernetes/pod_test.go +++ b/libbeat/autodiscover/providers/kubernetes/pod_test.go @@ -33,6 +33,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/kubernetes" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -332,6 +333,7 @@ func TestGenerateHints(t *testing.T) { } func TestEmitEvent(t *testing.T) { + k, _ := keystore.NewFileKeystore("test") name := "filebeat" namespace := "default" podIP := "127.0.0.1" @@ -395,6 +397,7 @@ func TestEmitEvent(t *testing.T) { "host": "127.0.0.1", "id": cid, "provider": UUID, + "keystore": k, "kubernetes": common.MapStr{ "container": common.MapStr{ "id": "foobar", @@ -527,6 +530,7 @@ func TestEmitEvent(t *testing.T) { "host": "", "id": cid, "provider": UUID, + "keystore": k, "kubernetes": common.MapStr{ "container": common.MapStr{ "id": "", @@ -596,6 +600,7 @@ func TestEmitEvent(t *testing.T) { "host": "127.0.0.1", "id": cid, "provider": UUID, + "keystore": k, "kubernetes": common.MapStr{ "container": common.MapStr{ "id": "", @@ -645,6 +650,7 @@ func TestEmitEvent(t *testing.T) { bus: bus.New(logp.NewLogger("bus"), "test"), templates: mapper, logger: logp.NewLogger("kubernetes"), + keystore: k, } pod := &pod{ diff --git a/libbeat/autodiscover/providers/kubernetes/service_test.go b/libbeat/autodiscover/providers/kubernetes/service_test.go index 6d6582b3ff2..0e3c8ddb0a8 100644 --- a/libbeat/autodiscover/providers/kubernetes/service_test.go +++ b/libbeat/autodiscover/providers/kubernetes/service_test.go @@ -33,6 +33,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/kubernetes" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -233,6 +234,7 @@ func TestGenerateHints_Service(t *testing.T) { } func TestEmitEvent_Service(t *testing.T) { + k, _ := keystore.NewFileKeystore("test") name := "metricbeat" namespace := "default" clusterIP := "192.168.0.1" @@ -280,6 +282,7 @@ func TestEmitEvent_Service(t *testing.T) { "host": "192.168.0.1", "id": uid, "provider": UUID, + "keystore": k, "port": 8080, "kubernetes": common.MapStr{ "service": common.MapStr{ @@ -369,6 +372,7 @@ func TestEmitEvent_Service(t *testing.T) { "id": uid, "port": 8080, "provider": UUID, + "keystore": k, "kubernetes": common.MapStr{ "service": common.MapStr{ "name": "metricbeat", @@ -405,6 +409,7 @@ func TestEmitEvent_Service(t *testing.T) { bus: bus.New(logp.NewLogger("bus"), "test"), templates: mapper, logger: logp.NewLogger("kubernetes"), + keystore: k, } service := &service{ diff --git a/libbeat/autodiscover/template/config.go b/libbeat/autodiscover/template/config.go index 151f76dde0f..0ce05526ecb 100644 --- a/libbeat/autodiscover/template/config.go +++ b/libbeat/autodiscover/template/config.go @@ -21,6 +21,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/conditions" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/go-ucfg" ) @@ -81,7 +82,7 @@ func (c Mapper) GetConfig(event bus.Event) []*common.Config { continue } - configs := ApplyConfigTemplate(event, mapping.Configs) + configs := ApplyConfigTemplate(event, mapping.Configs, true) if configs != nil { result = append(result, configs...) } @@ -90,7 +91,7 @@ func (c Mapper) GetConfig(event bus.Event) []*common.Config { } // ApplyConfigTemplate takes a set of templated configs and applys information in an event map -func ApplyConfigTemplate(event bus.Event, configs []*common.Config) []*common.Config { +func ApplyConfigTemplate(event bus.Event, configs []*common.Config, keystoreEnabled bool) []*common.Config { var result []*common.Config // unpack input vars, err := ucfg.NewFrom(map[string]interface{}{ @@ -105,6 +106,19 @@ func ApplyConfigTemplate(event bus.Event, configs []*common.Config) []*common.Co ucfg.ResolveEnv, ucfg.VarExp, } + + if keystoreEnabled { + if val, ok := event["keystore"]; ok { + eventKeystore := val.(keystore.Keystore) + opts = append(opts, ucfg.Resolve(keystore.ResolverWrap(eventKeystore))) + delete(event, "keystore") + } + } else { + if _, ok := event["keystore"]; ok { + delete(event, "keystore") + } + } + for _, config := range configs { c, err := ucfg.NewFrom(config, opts...) if err != nil { diff --git a/libbeat/autodiscover/template/config_test.go b/libbeat/autodiscover/template/config_test.go index 570de15a840..ccb27a7127a 100644 --- a/libbeat/autodiscover/template/config_test.go +++ b/libbeat/autodiscover/template/config_test.go @@ -18,12 +18,16 @@ package template import ( + "os" + "path/filepath" "testing" + "github.com/docker/docker/pkg/ioutils" "github.com/stretchr/testify/assert" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" ) func TestConfigsMapping(t *testing.T) { @@ -93,6 +97,61 @@ func TestConfigsMapping(t *testing.T) { } } +func TestConfigsMappingKeystore(t *testing.T) { + secret := "mapping_secret" + //expected config + config, _ := common.NewConfigFrom(map[string]interface{}{ + "correct": "config", + "password": secret, + }) + + path := getTemporaryKeystoreFile() + defer os.Remove(path) + // store the secret + keystore := createAnExistingKeystore(path, secret) + + tests := []struct { + mapping string + event bus.Event + expected []*common.Config + }{ + // Match config + { + mapping: ` +- condition.equals: + foo: 3 + config: + - correct: config + password: "${PASSWORD}"`, + event: bus.Event{ + "foo": 3, + "keystore": keystore, + }, + expected: []*common.Config{config}, + }, + } + + for _, test := range tests { + var mappings MapperSettings + config, err := common.NewConfigWithYAML([]byte(test.mapping), "") + if err != nil { + t.Fatal(err) + } + + if err := config.Unpack(&mappings); err != nil { + t.Fatal(err) + } + + mapper, err := NewConfigMapper(mappings) + if err != nil { + t.Fatal(err) + } + + res := mapper.GetConfig(test.event) + assert.Equal(t, test.expected, res) + } +} + func TestNilConditionConfig(t *testing.T) { var mappings MapperSettings data := ` @@ -111,3 +170,31 @@ func TestNilConditionConfig(t *testing.T) { assert.NoError(t, err) assert.Nil(t, mappings[0].ConditionConfig) } + +// create a keystore with an existing key +/// `PASSWORD` with the value of `secret` variable. +func createAnExistingKeystore(path string, secret string) keystore.Keystore { + keyStore, err := keystore.NewFileKeystore(path) + // Fail fast in the test suite + if err != nil { + panic(err) + } + + writableKeystore, err := keystore.AsWritableKeystore(keyStore) + if err != nil { + panic(err) + } + + writableKeystore.Store("PASSWORD", []byte(secret)) + writableKeystore.Save() + return keyStore +} + +// create a temporary file on disk to save the keystore. +func getTemporaryKeystoreFile() string { + path, err := ioutils.TempDir("", "testing") + if err != nil { + panic(err) + } + return filepath.Join(path, "keystore") +} diff --git a/libbeat/beat/beat.go b/libbeat/beat/beat.go index 3aaed4c6641..75585ba8992 100644 --- a/libbeat/beat/beat.go +++ b/libbeat/beat/beat.go @@ -19,6 +19,7 @@ package beat import ( "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/management" ) @@ -66,6 +67,8 @@ type Beat struct { Fields []byte // Data from fields.yml ConfigManager management.ConfigManager // config manager + + Keystore keystore.Keystore } // BeatConfig struct contains the basic configuration of every beat diff --git a/libbeat/beat/pipeline.go b/libbeat/beat/pipeline.go index c4f4665af1b..9d6fbf04e86 100644 --- a/libbeat/beat/pipeline.go +++ b/libbeat/beat/pipeline.go @@ -110,6 +110,9 @@ type ProcessingConfig struct { // KeepNull determines whether published events will keep null values or omit them. KeepNull bool + // Disables the addition of host.name if it was enabled for the publisher. + DisableHost bool + // Private contains additional information to be passed to the processing // pipeline builder. Private interface{} diff --git a/libbeat/cfgfile/list.go b/libbeat/cfgfile/list.go index db02c56bbdd..d3b05994e9a 100644 --- a/libbeat/cfgfile/list.go +++ b/libbeat/cfgfile/list.go @@ -35,12 +35,12 @@ type RunnerList struct { runners map[uint64]Runner mutex sync.RWMutex factory RunnerFactory - pipeline beat.Pipeline + pipeline beat.PipelineConnector logger *logp.Logger } // NewRunnerList builds and returns a RunnerList -func NewRunnerList(name string, factory RunnerFactory, pipeline beat.Pipeline) *RunnerList { +func NewRunnerList(name string, factory RunnerFactory, pipeline beat.PipelineConnector) *RunnerList { return &RunnerList{ runners: map[uint64]Runner{}, factory: factory, diff --git a/libbeat/cfgfile/reload.go b/libbeat/cfgfile/reload.go index 990264c3a6b..d5b9d9c315f 100644 --- a/libbeat/cfgfile/reload.go +++ b/libbeat/cfgfile/reload.go @@ -97,7 +97,7 @@ type Runner interface { // Reloader is used to register and reload modules type Reloader struct { - pipeline beat.Pipeline + pipeline beat.PipelineConnector config DynamicConfig path string done chan struct{} @@ -105,7 +105,7 @@ type Reloader struct { } // NewReloader creates new Reloader instance for the given config -func NewReloader(pipeline beat.Pipeline, cfg *common.Config) *Reloader { +func NewReloader(pipeline beat.PipelineConnector, cfg *common.Config) *Reloader { config := DefaultDynamicConfig cfg.Unpack(&config) diff --git a/libbeat/cmd/instance/beat.go b/libbeat/cmd/instance/beat.go index a9e59173633..5439e311301 100644 --- a/libbeat/cmd/instance/beat.go +++ b/libbeat/cmd/instance/beat.go @@ -33,6 +33,8 @@ import ( "strings" "time" + "go.elastic.co/apm" + "github.com/gofrs/uuid" errw "github.com/pkg/errors" "go.uber.org/zap" @@ -121,6 +123,8 @@ var debugf = logp.MakeDebug("beat") func init() { initRand() + // we need to close the default tracer to prevent the beat sending events to localhost:8200 + apm.DefaultTracer.Close() } // initRand initializes the runtime random number generator seed using @@ -331,11 +335,17 @@ func (b *Beat) createBeater(bt beat.Creator) (beat.Beater, error) { } } + tracer, err := apm.NewTracer(b.Info.Beat, b.Info.Version) + if err != nil { + return nil, err + } + pipeline, err := pipeline.Load(b.Info, pipeline.Monitors{ Metrics: reg, Telemetry: monitoring.GetNamespace("state").GetRegistry(), Logger: logp.L().Named("publisher"), + Tracer: tracer, }, b.Config.Pipeline, b.processing, @@ -584,6 +594,7 @@ func (b *Beat) configure(settings Settings) error { } b.keystore = store + b.Beat.Keystore = store err = cloudid.OverwriteSettings(cfg) if err != nil { return err diff --git a/libbeat/common/jsontransform/jsonhelper.go b/libbeat/common/jsontransform/jsonhelper.go index 1490bcff170..164e1e9e1f4 100644 --- a/libbeat/common/jsontransform/jsonhelper.go +++ b/libbeat/common/jsontransform/jsonhelper.go @@ -29,11 +29,12 @@ import ( // WriteJSONKeys writes the json keys to the given event based on the overwriteKeys option and the addErrKey func WriteJSONKeys(event *beat.Event, keys map[string]interface{}, overwriteKeys bool, addErrKey bool) { if !overwriteKeys { - for k, v := range keys { - if _, exists := event.Fields[k]; !exists && k != "@timestamp" && k != "@metadata" { - event.Fields[k] = v - } - } + // @timestamp and @metadata fields are root-level fields. We remove them so they + // don't become part of event.Fields. + removeKeys(keys, "@timestamp", "@metadata") + + // Then, perform deep update without overwriting + event.Fields.DeepUpdateNoOverwrite(keys) return } @@ -64,7 +65,7 @@ func WriteJSONKeys(event *beat.Event, keys map[string]interface{}, overwriteKeys } case map[string]interface{}: - event.Meta.Update(common.MapStr(m)) + event.Meta.DeepUpdate(common.MapStr(m)) default: event.SetErrorWithOption(createJSONError("failed to update @metadata"), addErrKey) @@ -83,13 +84,21 @@ func WriteJSONKeys(event *beat.Event, keys map[string]interface{}, overwriteKeys continue } event.Fields[k] = vstr - - default: - event.Fields[k] = v } } + + // We have accounted for @timestamp, @metadata, type above. So let's remove these keys and + // deep update the event with the rest of the keys. + removeKeys(keys, "@timestamp", "@metadata", "type") + event.Fields.DeepUpdate(keys) } func createJSONError(message string) common.MapStr { return common.MapStr{"message": message, "type": "json"} } + +func removeKeys(keys map[string]interface{}, names ...string) { + for _, name := range names { + delete(keys, name) + } +} diff --git a/libbeat/common/jsontransform/jsonhelper_test.go b/libbeat/common/jsontransform/jsonhelper_test.go new file mode 100644 index 00000000000..d7679579be1 --- /dev/null +++ b/libbeat/common/jsontransform/jsonhelper_test.go @@ -0,0 +1,136 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package jsontransform + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" +) + +func TestWriteJSONKeys(t *testing.T) { + now := time.Now() + now = now.Round(time.Second) + + eventTimestamp := time.Date(2020, 01, 01, 01, 01, 00, 0, time.UTC) + eventMetadata := common.MapStr{ + "foo": "bar", + "baz": common.MapStr{ + "qux": 17, + }, + } + eventFields := common.MapStr{ + "top_a": 23, + "top_b": common.MapStr{ + "inner_c": "see", + "inner_d": "dee", + }, + } + + tests := map[string]struct { + keys map[string]interface{} + overwriteKeys bool + expectedMetadata common.MapStr + expectedTimestamp time.Time + expectedFields common.MapStr + }{ + "overwrite_true": { + overwriteKeys: true, + keys: map[string]interface{}{ + "@metadata": map[string]interface{}{ + "foo": "NEW_bar", + "baz": map[string]interface{}{ + "qux": "NEW_qux", + "durrr": "COMPLETELY_NEW", + }, + }, + "@timestamp": now.Format(time.RFC3339), + "top_b": map[string]interface{}{ + "inner_d": "NEW_dee", + "inner_e": "COMPLETELY_NEW_e", + }, + "top_c": "COMPLETELY_NEW_c", + }, + expectedMetadata: common.MapStr{ + "foo": "NEW_bar", + "baz": common.MapStr{ + "qux": "NEW_qux", + "durrr": "COMPLETELY_NEW", + }, + }, + expectedTimestamp: now, + expectedFields: common.MapStr{ + "top_a": 23, + "top_b": common.MapStr{ + "inner_c": "see", + "inner_d": "NEW_dee", + "inner_e": "COMPLETELY_NEW_e", + }, + "top_c": "COMPLETELY_NEW_c", + }, + }, + "overwrite_false": { + overwriteKeys: false, + keys: map[string]interface{}{ + "@metadata": map[string]interface{}{ + "foo": "NEW_bar", + "baz": map[string]interface{}{ + "qux": "NEW_qux", + "durrr": "COMPLETELY_NEW", + }, + }, + "@timestamp": now.Format(time.RFC3339), + "top_b": map[string]interface{}{ + "inner_d": "NEW_dee", + "inner_e": "COMPLETELY_NEW_e", + }, + "top_c": "COMPLETELY_NEW_c", + }, + expectedMetadata: eventMetadata.Clone(), + expectedTimestamp: eventTimestamp, + expectedFields: common.MapStr{ + "top_a": 23, + "top_b": common.MapStr{ + "inner_c": "see", + "inner_d": "dee", + "inner_e": "COMPLETELY_NEW_e", + }, + "top_c": "COMPLETELY_NEW_c", + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + event := &beat.Event{ + Timestamp: eventTimestamp, + Meta: eventMetadata.Clone(), + Fields: eventFields.Clone(), + } + + WriteJSONKeys(event, test.keys, test.overwriteKeys, false) + require.Equal(t, test.expectedMetadata, event.Meta) + require.Equal(t, test.expectedTimestamp.UnixNano(), event.Timestamp.UnixNano()) + require.Equal(t, test.expectedFields, event.Fields) + }) + } +} diff --git a/libbeat/common/mapstr.go b/libbeat/common/mapstr.go index 8cf589b0f04..d70f05dd349 100644 --- a/libbeat/common/mapstr.go +++ b/libbeat/common/mapstr.go @@ -96,20 +96,26 @@ func (m MapStr) deepUpdateMap(d MapStr, overwrite bool) { } func deepUpdateValue(old interface{}, val MapStr, overwrite bool) interface{} { - if old == nil { - return val - } - switch sub := old.(type) { case MapStr: + if sub == nil { + return val + } + sub.deepUpdateMap(val, overwrite) return sub case map[string]interface{}: + if sub == nil { + return val + } + tmp := MapStr(sub) tmp.deepUpdateMap(val, overwrite) return tmp default: - // This should never happen + // We reach the default branch if old is no map or if old == nil. + // In either case we return `val`, such that the old value is completely + // replaced when merging. return val } } diff --git a/libbeat/common/mapstr_test.go b/libbeat/common/mapstr_test.go index de633ff162e..e1ffb8f5ee4 100644 --- a/libbeat/common/mapstr_test.go +++ b/libbeat/common/mapstr_test.go @@ -87,6 +87,11 @@ func TestMapStrDeepUpdate(t *testing.T) { MapStr{"a.b": 1}, MapStr{"a": 1, "a.b": 1}, }, + { + MapStr{"a": (MapStr)(nil)}, + MapStr{"a": MapStr{"b": 1}}, + MapStr{"a": MapStr{"b": 1}}, + }, } for i, test := range tests { diff --git a/libbeat/common/seccomp/policy_linux_386.go b/libbeat/common/seccomp/policy_linux_386.go index 76b24714cac..acbc69ddd1f 100644 --- a/libbeat/common/seccomp/policy_linux_386.go +++ b/libbeat/common/seccomp/policy_linux_386.go @@ -32,6 +32,7 @@ func init() { "access", "brk", "chmod", + "chown", "clock_gettime", "clone", "close", diff --git a/libbeat/common/seccomp/policy_linux_amd64.go b/libbeat/common/seccomp/policy_linux_amd64.go index 92b5fbe488a..bf1e4bc31c5 100644 --- a/libbeat/common/seccomp/policy_linux_amd64.go +++ b/libbeat/common/seccomp/policy_linux_amd64.go @@ -35,6 +35,7 @@ func init() { "bind", "brk", "chmod", + "chown", "clock_gettime", "clone", "close", diff --git a/libbeat/common/transport/kerberos/client.go b/libbeat/common/transport/kerberos/client.go new file mode 100644 index 00000000000..1cbfb0a4338 --- /dev/null +++ b/libbeat/common/transport/kerberos/client.go @@ -0,0 +1,65 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package kerberos + +import ( + "fmt" + "net/http" + + krbclient "gopkg.in/jcmturner/gokrb5.v7/client" + krbconfig "gopkg.in/jcmturner/gokrb5.v7/config" + "gopkg.in/jcmturner/gokrb5.v7/keytab" + "gopkg.in/jcmturner/gokrb5.v7/spnego" +) + +type Client struct { + spClient *spnego.Client +} + +func NewClient(config *Config, httpClient *http.Client, esurl string) (*Client, error) { + var krbClient *krbclient.Client + krbConf, err := krbconfig.Load(config.ConfigPath) + if err != nil { + return nil, fmt.Errorf("error creating Kerberos client: %+v", err) + } + + switch config.AuthType { + case authKeytab: + kTab, err := keytab.Load(config.KeyTabPath) + if err != nil { + return nil, fmt.Errorf("cannot load keytab file %s: %+v", config.KeyTabPath, err) + } + krbClient = krbclient.NewClientWithKeytab(config.Username, config.Realm, kTab, krbConf) + case authPassword: + krbClient = krbclient.NewClientWithPassword(config.Username, config.Realm, config.Password, krbConf) + default: + return nil, InvalidAuthType + } + + return &Client{ + spClient: spnego.NewClient(krbClient, httpClient, ""), + }, nil +} + +func (c *Client) Do(req *http.Request) (*http.Response, error) { + return c.spClient.Do(req) +} + +func (c *Client) CloseIdleConnections() { + c.spClient.CloseIdleConnections() +} diff --git a/libbeat/common/transport/kerberos/config.go b/libbeat/common/transport/kerberos/config.go index fe2450fa639..42b779485fe 100644 --- a/libbeat/common/transport/kerberos/config.go +++ b/libbeat/common/transport/kerberos/config.go @@ -17,33 +17,44 @@ package kerberos -import "fmt" +import ( + "errors" + "fmt" +) type AuthType uint const ( - AUTH_PASSWORD = 1 - AUTH_KEYTAB = 2 + authPassword = 1 + authKeytab = 2 - authPassword = "password" - authKeytabStr = "keytab" + authPasswordStr = "password" + authKeytabStr = "keytab" ) var ( + InvalidAuthType = errors.New("invalid authentication type") + authTypes = map[string]AuthType{ - authPassword: AUTH_PASSWORD, - authKeytabStr: AUTH_KEYTAB, + authPasswordStr: authPassword, + authKeytabStr: authKeytab, } ) type Config struct { + Enabled *bool `config:"enabled" yaml:"enabled,omitempty"` AuthType AuthType `config:"auth_type" validate:"required"` KeyTabPath string `config:"keytab"` - ConfigPath string `config:"config_path"` + ConfigPath string `config:"config_path" validate:"required"` ServiceName string `config:"service_name"` Username string `config:"username"` Password string `config:"password"` - Realm string `config:"realm"` + Realm string `config:"realm" validate:"required"` +} + +// IsEnabled returns true if the `enable` field is set to true in the yaml. +func (c *Config) IsEnabled() bool { + return c != nil && (c.Enabled == nil || *c.Enabled) } // Unpack validates and unpack "auth_type" config option @@ -59,19 +70,21 @@ func (t *AuthType) Unpack(value string) error { } func (c *Config) Validate() error { - if c.AuthType == AUTH_PASSWORD { + switch c.AuthType { + case authPassword: if c.Username == "" { return fmt.Errorf("password authentication is selected for Kerberos, but username is not configured") } if c.Password == "" { return fmt.Errorf("password authentication is selected for Kerberos, but password is not configured") } - } - if c.AuthType == AUTH_KEYTAB { + case authKeytab: if c.KeyTabPath == "" { return fmt.Errorf("keytab authentication is selected for Kerberos, but path to keytab is not configured") } + default: + return InvalidAuthType } return nil diff --git a/libbeat/common/transport/tlscommon/types.go b/libbeat/common/transport/tlscommon/types.go index 3fb96712b16..93cdf95464e 100644 --- a/libbeat/common/transport/tlscommon/types.go +++ b/libbeat/common/transport/tlscommon/types.go @@ -65,6 +65,10 @@ var tlsCipherSuites = map[string]tlsCipherSuite{ "RSA-AES-128-GCM-SHA256": tlsCipherSuite(tls.TLS_RSA_WITH_AES_128_GCM_SHA256), "RSA-AES-256-CBC-SHA": tlsCipherSuite(tls.TLS_RSA_WITH_AES_256_CBC_SHA), "RSA-AES-256-GCM-SHA384": tlsCipherSuite(tls.TLS_RSA_WITH_AES_256_GCM_SHA384), + + "TLS-AES-128-GCM-SHA256": tlsCipherSuite(tls.TLS_AES_128_GCM_SHA256), + "TLS-AES-256-GCM-SHA384": tlsCipherSuite(tls.TLS_AES_256_GCM_SHA384), + "TLS-CHACHA20-POLY1305-SHA256": tlsCipherSuite(tls.TLS_CHACHA20_POLY1305_SHA256), } var tlsCipherSuitesInverse = make(map[tlsCipherSuite]string, len(tlsCipherSuites)) diff --git a/libbeat/common/transport/tlscommon/versions.go b/libbeat/common/transport/tlscommon/versions.go index 3ab3dd5a8f0..a589f0af3cd 100644 --- a/libbeat/common/transport/tlscommon/versions.go +++ b/libbeat/common/transport/tlscommon/versions.go @@ -23,12 +23,20 @@ import "fmt" type TLSVersion uint16 func (v TLSVersion) String() string { - if s, ok := tlsProtocolVersionsInverse[v]; ok { - return s + if details := v.Details(); details != nil { + return details.Combined } return "unknown" } +// Details returns a a ProtocolAndVersions struct containing detailed version metadata. +func (v TLSVersion) Details() *TLSVersionDetails { + if found, ok := tlsInverseLookup[v]; ok { + return &found + } + return nil +} + //Unpack transforms the string into a constant. func (v *TLSVersion) Unpack(s string) error { version, found := tlsProtocolVersions[s] diff --git a/libbeat/common/transport/tlscommon/versions_default.go b/libbeat/common/transport/tlscommon/versions_default.go index 057c5c59cd4..77eff7375eb 100644 --- a/libbeat/common/transport/tlscommon/versions_default.go +++ b/libbeat/common/transport/tlscommon/versions_default.go @@ -19,7 +19,9 @@ package tlscommon -import "crypto/tls" +import ( + "crypto/tls" +) // Define all the possible TLS version. const ( @@ -61,10 +63,22 @@ var tlsProtocolVersions = map[string]TLSVersion{ "TLSv1.3": TLSVersion13, } -var tlsProtocolVersionsInverse = map[TLSVersion]string{ - TLSVersionSSL30: "SSLv3", - TLSVersion10: "TLSv1.0", - TLSVersion11: "TLSv1.1", - TLSVersion12: "TLSv1.2", - TLSVersion13: "TLSv1.3", +// Intended for ECS's tls.version_protocol_field, which does not include +// numeric version and should be lower case +type TLSVersionDetails struct { + Version string + Protocol string + Combined string +} + +func (pv TLSVersionDetails) String() string { + return pv.Combined +} + +var tlsInverseLookup = map[TLSVersion]TLSVersionDetails{ + TLSVersionSSL30: TLSVersionDetails{Version: "3.0", Protocol: "ssl", Combined: "SSLv3"}, + TLSVersion10: TLSVersionDetails{Version: "1.0", Protocol: "tls", Combined: "TLSv1.0"}, + TLSVersion11: TLSVersionDetails{Version: "1.1", Protocol: "tls", Combined: "TLSv1.1"}, + TLSVersion12: TLSVersionDetails{Version: "1.2", Protocol: "tls", Combined: "TLSv1.2"}, + TLSVersion13: TLSVersionDetails{Version: "1.3", Protocol: "tls", Combined: "TLSv1.3"}, } diff --git a/libbeat/common/transport/tlscommon/versions_test.go b/libbeat/common/transport/tlscommon/versions_test.go new file mode 100644 index 00000000000..b1251109b05 --- /dev/null +++ b/libbeat/common/transport/tlscommon/versions_test.go @@ -0,0 +1,77 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package tlscommon + +import ( + "crypto/tls" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestTLSVersion(t *testing.T) { + // These tests are a bit verbose, but given the sensitivity to changes here, it's not a bad idea. + tests := []struct { + name string + v uint16 + want *TLSVersionDetails + }{ + { + "unknown", + 0x0, + nil, + }, + { + "SSLv3", + tls.VersionSSL30, + &TLSVersionDetails{Version: "3.0", Protocol: "ssl", Combined: "SSLv3"}, + }, + { + "TLSv1.0", + tls.VersionTLS10, + &TLSVersionDetails{Version: "1.0", Protocol: "tls", Combined: "TLSv1.0"}, + }, + { + "TLSv1.1", + tls.VersionTLS11, + &TLSVersionDetails{Version: "1.1", Protocol: "tls", Combined: "TLSv1.1"}, + }, + { + "TLSv1.2", + tls.VersionTLS12, + &TLSVersionDetails{Version: "1.2", Protocol: "tls", Combined: "TLSv1.2"}, + }, + { + "TLSv1.3", + tls.VersionTLS13, + &TLSVersionDetails{Version: "1.3", Protocol: "tls", Combined: "TLSv1.3"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tv := TLSVersion(tt.v) + require.Equal(t, tt.want, tv.Details()) + if tt.want == nil { + require.Equal(t, tt.want, tv.Details()) + require.Equal(t, tt.name, "unknown") + } else { + require.Equal(t, tt.name, tv.String()) + } + }) + } +} diff --git a/libbeat/dashboards/kibana_loader.go b/libbeat/dashboards/kibana_loader.go index 93dd0e5dc0e..1733f94750c 100644 --- a/libbeat/dashboards/kibana_loader.go +++ b/libbeat/dashboards/kibana_loader.go @@ -25,6 +25,9 @@ import ( "net/url" "time" + "github.com/joeshaw/multierror" + "github.com/pkg/errors" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/kibana" "github.com/elastic/beats/v7/libbeat/logp" @@ -101,11 +104,18 @@ func (loader KibanaLoader) ImportIndexFile(file string) error { // ImportIndex imports the passed index pattern to Kibana func (loader KibanaLoader) ImportIndex(pattern common.MapStr) error { + var errs multierror.Errors + params := url.Values{} params.Set("force", "true") //overwrite the existing dashboards - indexContent := ReplaceIndexInIndexPattern(loader.config.Index, pattern) - return loader.client.ImportJSON(importAPI, params, indexContent) + if err := ReplaceIndexInIndexPattern(loader.config.Index, pattern); err != nil { + errs = append(errs, errors.Wrapf(err, "error setting index '%s' in index pattern", loader.config.Index)) + } + if err := loader.client.ImportJSON(importAPI, params, pattern); err != nil { + errs = append(errs, errors.Wrap(err, "error loading index pattern")) + } + return errs.Err() } // ImportDashboard imports the dashboard file diff --git a/libbeat/dashboards/modify_json.go b/libbeat/dashboards/modify_json.go index c8b1c79da6b..2e0c48e38b0 100644 --- a/libbeat/dashboards/modify_json.go +++ b/libbeat/dashboards/modify_json.go @@ -22,6 +22,8 @@ import ( "encoding/json" "fmt" + "github.com/pkg/errors" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -41,32 +43,50 @@ type JSONFormat struct { Objects []JSONObject `json:"objects"` } -func ReplaceIndexInIndexPattern(index string, content common.MapStr) common.MapStr { +func ReplaceIndexInIndexPattern(index string, content common.MapStr) (err error) { if index == "" { - return content + return nil } - objects, ok := content["objects"].([]interface{}) + list, ok := content["objects"] if !ok { - return content + return errors.New("empty index pattern") } - // change index pattern name - for i, object := range objects { - objectMap, ok := object.(map[string]interface{}) - if !ok { - continue + updateObject := func(obj common.MapStr) { + // This uses Put instead of DeepUpdate to avoid modifying types for + // inner objects. (DeepUpdate will replace maps with MapStr). + obj.Put("id", index) + // Only overwrite title if it exists. + if _, err := obj.GetValue("attributes.title"); err == nil { + obj.Put("attributes.title", index) } + } - objectMap["id"] = index - if attributes, ok := objectMap["attributes"].(map[string]interface{}); ok { - attributes["title"] = index + switch v := list.(type) { + case []interface{}: + for _, objIf := range v { + switch obj := objIf.(type) { + case common.MapStr: + updateObject(obj) + case map[string]interface{}: + updateObject(obj) + default: + return errors.Errorf("index pattern object has unexpected type %T", v) + } } - objects[i] = objectMap + case []map[string]interface{}: + for _, obj := range v { + updateObject(obj) + } + case []common.MapStr: + for _, obj := range v { + updateObject(obj) + } + default: + return errors.Errorf("index pattern objects have unexpected type %T", v) } - content["objects"] = objects - - return content + return nil } func replaceIndexInSearchObject(index string, savedObject string) (string, error) { diff --git a/libbeat/dashboards/modify_json_test.go b/libbeat/dashboards/modify_json_test.go index 08fedac4df3..a7424414b53 100644 --- a/libbeat/dashboards/modify_json_test.go +++ b/libbeat/dashboards/modify_json_test.go @@ -111,3 +111,137 @@ func TestReplaceIndexInDashboardObject(t *testing.T) { assert.Equal(t, test.expected, result) } } + +func TestReplaceIndexInIndexPattern(t *testing.T) { + // Test that replacing of index name in index pattern works no matter + // what the inner types are (MapStr, map[string]interface{} or interface{}). + // Also ensures that the inner types are not modified after replacement. + tests := []struct { + title string + input common.MapStr + index string + expected common.MapStr + }{ + { + title: "Replace in []interface(map).map", + input: common.MapStr{"objects": []interface{}{map[string]interface{}{ + "id": "phonybeat-*", + "type": "index-pattern", + "attributes": map[string]interface{}{ + "title": "phonybeat-*", + "timeFieldName": "@timestamp", + }}}}, + index: "otherindex-*", + expected: common.MapStr{"objects": []interface{}{map[string]interface{}{ + "id": "otherindex-*", + "type": "index-pattern", + "attributes": map[string]interface{}{ + "title": "otherindex-*", + "timeFieldName": "@timestamp", + }}}}, + }, + { + title: "Replace in []interface(map).mapstr", + input: common.MapStr{"objects": []interface{}{map[string]interface{}{ + "id": "phonybeat-*", + "type": "index-pattern", + "attributes": common.MapStr{ + "title": "phonybeat-*", + "timeFieldName": "@timestamp", + }}}}, + index: "otherindex-*", + expected: common.MapStr{"objects": []interface{}{map[string]interface{}{ + "id": "otherindex-*", + "type": "index-pattern", + "attributes": common.MapStr{ + "title": "otherindex-*", + "timeFieldName": "@timestamp", + }}}}, + }, + { + title: "Replace in []map.mapstr", + input: common.MapStr{"objects": []map[string]interface{}{{ + "id": "phonybeat-*", + "type": "index-pattern", + "attributes": common.MapStr{ + "title": "phonybeat-*", + "timeFieldName": "@timestamp", + }}}}, + index: "otherindex-*", + expected: common.MapStr{"objects": []map[string]interface{}{{ + "id": "otherindex-*", + "type": "index-pattern", + "attributes": common.MapStr{ + "title": "otherindex-*", + "timeFieldName": "@timestamp", + }}}}, + }, + { + title: "Replace in []mapstr.mapstr", + input: common.MapStr{"objects": []common.MapStr{{ + "id": "phonybeat-*", + "type": "index-pattern", + "attributes": common.MapStr{ + "title": "phonybeat-*", + "timeFieldName": "@timestamp", + }}}}, + index: "otherindex-*", + expected: common.MapStr{"objects": []common.MapStr{{ + "id": "otherindex-*", + "type": "index-pattern", + "attributes": common.MapStr{ + "title": "otherindex-*", + "timeFieldName": "@timestamp", + }}}}, + }, + { + title: "Replace in []mapstr.interface(mapstr)", + input: common.MapStr{"objects": []common.MapStr{{ + "id": "phonybeat-*", + "type": "index-pattern", + "attributes": interface{}(common.MapStr{ + "title": "phonybeat-*", + "timeFieldName": "@timestamp", + })}}}, + index: "otherindex-*", + expected: common.MapStr{"objects": []common.MapStr{{ + "id": "otherindex-*", + "type": "index-pattern", + "attributes": interface{}(common.MapStr{ + "title": "otherindex-*", + "timeFieldName": "@timestamp", + })}}}, + }, + { + title: "Do not create missing attributes", + input: common.MapStr{"objects": []common.MapStr{{ + "id": "phonybeat-*", + "type": "index-pattern", + }}}, + index: "otherindex-*", + expected: common.MapStr{"objects": []common.MapStr{{ + "id": "otherindex-*", + "type": "index-pattern", + }}}, + }, + { + title: "Create missing id", + input: common.MapStr{"objects": []common.MapStr{{ + "type": "index-pattern", + }}}, + index: "otherindex-*", + expected: common.MapStr{"objects": []common.MapStr{{ + "id": "otherindex-*", + "type": "index-pattern", + }}}, + }, + } + + for _, test := range tests { + t.Run(test.title, func(t *testing.T) { + err := ReplaceIndexInIndexPattern(test.index, test.input) + assert.NoError(t, err) + assert.Equal(t, test.expected, test.input) + }) + } +} diff --git a/libbeat/docker-compose.yml b/libbeat/docker-compose.yml index e2c759cda81..be4c0be7dfa 100644 --- a/libbeat/docker-compose.yml +++ b/libbeat/docker-compose.yml @@ -5,7 +5,6 @@ services: depends_on: - proxy_dep environment: - - LIBBEAT_PATH=/go/src/github.com/elastic/beats/libbeat - BEAT_STRICT_PERMS=false - REDIS_HOST=redis - REDIS_PORT=6379 @@ -27,6 +26,7 @@ services: - ES_MONITORING_HOST=elasticsearch_monitoring - ES_MONITORING_PORT=9200 - ES_HOST_SSL=elasticsearchssl + - ES_KERBEROS_HOST=elasticsearch_kerberos.elastic - ES_PORT_SSL=9200 - ES_SUPERUSER_USER=admin - ES_SUPERUSER_PASS=changeme @@ -42,14 +42,15 @@ services: proxy_dep: image: busybox depends_on: - elasticsearch: { condition: service_healthy } - elasticsearch_monitoring: { condition: service_healthy } - elasticsearchssl: { condition: service_healthy } - logstash: { condition: service_healthy } - kafka: { condition: service_healthy } - redis: { condition: service_healthy } - sredis: { condition: service_healthy } - kibana: { condition: service_healthy } + elasticsearch: { condition: service_healthy } + elasticsearch_kerberos.elastic: { condition: service_healthy } + elasticsearch_monitoring: { condition: service_healthy } + elasticsearchssl: { condition: service_healthy } + logstash: { condition: service_healthy } + kafka: { condition: service_healthy } + redis: { condition: service_healthy } + sredis: { condition: service_healthy } + kibana: { condition: service_healthy } healthcheck: interval: 1s retries: 1200 @@ -128,6 +129,35 @@ services: environment: - ADVERTISED_HOST=kafka + elasticsearch_kerberos.elastic: + build: ${ES_BEATS}/testing/environments/docker/elasticsearch_kerberos + healthcheck: + test: bash -c "/healthcheck.sh" + retries: 1200 + interval: 5s + start_period: 60s + environment: + - "TERM=linux" + - "ELASTIC_PASSWORD=changeme" + - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Djava.security.krb5.conf=/etc/krb5.conf" + - "network.host=" + - "transport.host=127.0.0.1" + - "http.host=0.0.0.0" + - "xpack.security.enabled=true" + - "indices.id_field_data.enabled=true" + - "xpack.license.self_generated.type=trial" + - "xpack.security.authc.realms.kerberos.ELASTIC.order=1" + - "xpack.security.authc.realms.kerberos.ELASTIC.keytab.path=/usr/share/elasticsearch/config/HTTP_elasticsearch_kerberos.elastic.keytab" + hostname: elasticsearch_kerberos.elastic + volumes: + # This is needed otherwise there won't be enough entropy to generate a new kerberos realm + - /dev/urandom:/dev/random + ports: + - 1088 + - 1749 + - 9200 + command: bash -c "/start.sh" + kibana: extends: file: ${ES_BEATS}/testing/environments/${TESTING_ENVIRONMENT}.yml diff --git a/libbeat/docs/processors-list.asciidoc b/libbeat/docs/processors-list.asciidoc index 169657aecc4..5dd95e2e3d5 100644 --- a/libbeat/docs/processors-list.asciidoc +++ b/libbeat/docs/processors-list.asciidoc @@ -95,12 +95,12 @@ endif::[] ifndef::no_timestamp_processor[] * <> endif::[] -ifndef::no_truncate_fields_processor[] -* <> -endif::[] ifndef::no_translate_sid_processor[] * <> endif::[] +ifndef::no_truncate_fields_processor[] +* <> +endif::[] ifndef::no_urldecode_processor[] * <> endif::[] @@ -201,12 +201,12 @@ endif::[] ifndef::no_timestamp_processor[] include::{libbeat-processors-dir}/timestamp/docs/timestamp.asciidoc[] endif::[] -ifndef::no_truncate_fields_processor[] -include::{libbeat-processors-dir}/actions/docs/truncate_fields.asciidoc[] -endif::[] ifndef::no_translate_sid_processor[] include::{libbeat-processors-dir}/translate_sid/docs/translate_sid.asciidoc[] endif::[] +ifndef::no_truncate_fields_processor[] +include::{libbeat-processors-dir}/actions/docs/truncate_fields.asciidoc[] +endif::[] ifndef::no_urldecode_processor[] include::{libbeat-processors-dir}/urldecode/docs/urldecode.asciidoc[] endif::[] diff --git a/libbeat/docs/processors-using.asciidoc b/libbeat/docs/processors-using.asciidoc index 81080dda5cb..eb5f391a558 100644 --- a/libbeat/docs/processors-using.asciidoc +++ b/libbeat/docs/processors-using.asciidoc @@ -8,15 +8,15 @@ optional condition, and a set of parameters: [source,yaml] ------ processors: -- : - when: - - + - : + when: + + -- : - when: - - + - : + when: + + ... ------ @@ -38,20 +38,20 @@ executed based on a single condition. [source,yaml] ---- processors: -- if: - - then: <1> - - : - - - : - - ... - else: <2> - - : - - - : - - ... + - if: + + then: <1> + - : + + - : + + ... + else: <2> + - : + + - : + + ... ---- <1> `then` must contain a single processor or a list of one or more processors to execute when the condition evaluates to true. @@ -99,10 +99,10 @@ ifeval::["{beatname_lc}"=="filebeat"] ------ - type: processors: - - : - when: - - + - : + when: + + ... ------ + @@ -116,10 +116,10 @@ ifeval::["{beatname_lc}"=="metricbeat"] - module: metricsets: [""] processors: - - : - when: - - + - : + when: + + ---- endif::[] ifeval::["{beatname_lc}"=="auditbeat"] @@ -129,10 +129,10 @@ ifeval::["{beatname_lc}"=="auditbeat"] auditbeat.modules: - module: processors: - - : - when: - - + - : + when: + + ---- endif::[] ifeval::["{beatname_lc}"=="packetbeat"] @@ -142,10 +142,10 @@ ifeval::["{beatname_lc}"=="packetbeat"] packetbeat.protocols: - type: processors: - - : - when: - - + - : + when: + + ---- * Under `packetbeat.flows`. The processor is applied to the data in @@ -155,10 +155,10 @@ packetbeat.protocols: ---- packetbeat.flows: processors: - - : - when: - - + - : + when: + + ---- endif::[] ifeval::["{beatname_lc}"=="heartbeat"] @@ -168,10 +168,10 @@ ifeval::["{beatname_lc}"=="heartbeat"] heartbeat.monitors: - type: processors: - - : - when: - - + - : + when: + + ---- endif::[] ifeval::["{beatname_lc}"=="winlogbeat"] @@ -181,10 +181,10 @@ ifeval::["{beatname_lc}"=="winlogbeat"] winlogbeat.event_logs: - name: processors: - - : - when: - - + - : + when: + + ---- endif::[] @@ -285,8 +285,8 @@ comparing the `http.response.code` field with 400. [source,yaml] ------ range: - http.response.code: - gte: 400 + http.response.code: + gte: 400 ------ This can also be written as: @@ -294,7 +294,7 @@ This can also be written as: [source,yaml] ---- range: - http.response.code.gte: 400 + http.response.code.gte: 400 ---- The following condition checks if the CPU usage in percentage has a value @@ -303,8 +303,8 @@ between 0.5 and 0.8. [source,yaml] ------ range: - system.cpu.user.pct.gte: 0.5 - system.cpu.user.pct.lt: 0.8 + system.cpu.user.pct.gte: 0.5 + system.cpu.user.pct.lt: 0.8 ------ [float] @@ -339,7 +339,7 @@ private address space. [source,yaml] ---- network: - source.ip: private + source.ip: private ---- This condition returns true if the `destination.ip` value is within the @@ -348,7 +348,7 @@ IPv4 range of `192.168.1.0` - `192.168.1.255`. [source,yaml] ---- network: - destination.ip: '192.168.1.0/24' + destination.ip: '192.168.1.0/24' ---- And this condition returns true when `destination.ip` is within any of the given @@ -357,7 +357,7 @@ subnets. [source,yaml] ---- network: - destination.ip: ['192.168.1.0/24', '10.0.0.0/8', loopback] + destination.ip: ['192.168.1.0/24', '10.0.0.0/8', loopback] ---- [float] @@ -438,8 +438,8 @@ To configure a condition like ` OR AND `: [source,yaml] ------ or: - - - - and: + - + - and: - - diff --git a/libbeat/docs/release-notes/breaking/breaking-7.8.asciidoc b/libbeat/docs/release-notes/breaking/breaking-7.8.asciidoc new file mode 100644 index 00000000000..c94a45b7603 --- /dev/null +++ b/libbeat/docs/release-notes/breaking/breaking-7.8.asciidoc @@ -0,0 +1,25 @@ +[[breaking-changes-7.8]] + +=== Breaking changes in 7.8 +++++ +7.8 +++++ + +{see-relnotes} + +//NOTE: The notable-breaking-changes tagged regions are re-used in the +//Installation and Upgrade Guide + +//tag::notable-breaking-changes[] +[float] + +==== APM Instrumentation + +Libbeat includes the Elastic APM Agent for instrumenting the publishing pipeline. +Currently the Elasticsearch output is instrumented. APM can be enabled simply with +setting the `ELASTIC_APM_ACTIVE` environment variable to `true` when starting the beat. +To make tracing possible, the `Publish` method of the `Client` interface takes a +`Context` object as first argument. That `Context` is intended for propagating +request-scoped values, not for cancellation. + +// end::notable-breaking-changes[] diff --git a/libbeat/docs/release-notes/breaking/breaking.asciidoc b/libbeat/docs/release-notes/breaking/breaking.asciidoc index 51e58e6ba9d..5bd5d2ad03d 100644 --- a/libbeat/docs/release-notes/breaking/breaking.asciidoc +++ b/libbeat/docs/release-notes/breaking/breaking.asciidoc @@ -10,6 +10,7 @@ changes, but there are breaking changes between major versions (e.g. 6.x to 7.x) is not recommended. See the following topics for a description of breaking changes: +* <> * <> @@ -27,6 +28,8 @@ See the following topics for a description of breaking changes: * <> +include::breaking-7.8.asciidoc[] + include::breaking-7.7.asciidoc[] include::breaking-7.6.asciidoc[] diff --git a/libbeat/docs/release-notes/highlights/highlights-7.7.0.asciidoc b/libbeat/docs/release-notes/highlights/highlights-7.7.0.asciidoc index 51fe50c30a9..959bbea91f5 100644 --- a/libbeat/docs/release-notes/highlights/highlights-7.7.0.asciidoc +++ b/libbeat/docs/release-notes/highlights/highlights-7.7.0.asciidoc @@ -7,8 +7,8 @@ Each release of {beats} brings new features and product improvements. Following are the most notable features and enhancements in 7.7. -For a complete list of related highlights, see the -https://www.elastic.co/blog/elastic-observability-7-6-0-released[Observability 7.7 release blog]. +//For a complete list of related highlights, see the +//https://www.elastic.co/blog/elastic-observability-7-7-0-released[Observability 7.7 release blog]. For a list of bug fixes and other changes, see the {beats} <> and <>. @@ -18,9 +18,135 @@ For a list of bug fixes and other changes, see the {beats} // tag::notable-highlights[] -//[float] -//==== highlight +[float] +[role="xpack"] +==== Azure Kubernetes and container monitoring -//Description +We've enhanced the {metricbeat} Azure module with three new metricsets +for monitoring Microsoft Azure container services: +{metricbeat-ref}/metricbeat-metricset-azure-container_instance.html[`container_instance`], +{metricbeat-ref}/metricbeat-metricset-azure-container_registry.html[`container_registry`], and +{metricbeat-ref}/metricbeat-metricset-azure-container_service.html[`container_service`]. +These metricsets collect metrics from the following services: + +* Azure Kubernetes Service +* Azure Container Instances +* Azure Container Registry + +Each metricset comes with a dashboard that makes it easy to get started +monitoring Azure containers. + +[float] +[role="xpack"] +==== AWS VPCs, Lambdas, and DynamoDB monitoring + +In the {metricbeat} AWS module, we've added support for monitoring +mission-critical services in the Amazon VPC ecosystem: + +* The {metricbeat-ref}/metricbeat-metricset-aws-natgateway.html[`natgateway`] +metricset enables you to monitor NAT gateway services to gain a +better perspective on how web applications or services are performing. +* The {metricbeat-ref}/metricbeat-metricset-aws-natgateway.html[`transitgateway`] +metricset collects metrics sent to CloudWatch by VPC when requests are flowing +through the gateway.  +* The {metricbeat-ref}/metricbeat-metricset-aws-vpn.html[`vpn`] metricset +enables you to monitor VPN tunnels. VPN metric data is automatically sent to +CloudWatch as it becomes available. + +Also new in this release, the +{metricbeat-ref}/metricbeat-metricset-aws-lambda.html[`lambda`] metricset monitors +Lambda functions across multiple accounts and regions. The metricset collects +metrics such as total invocations, errors, duration, throttles, dead-letter queue +errors, and iterator age for stream-based invocations. You can use these metrics +to configure alerts to respond to events such as changes in performance and +error rates. + +We’ve also added the +{metricbeat-ref}/metricbeat-metricset-aws-dynamodb.html[`dynamodb`] metricset to +monitor AWS DynamoDB instances. This metricset collects metrics, such as request +latency, transaction conflicts, provisioned and consumed capacity, and many +others.   
 + +For Amazon Aurora users, we've enhanced the +{metricbeat-ref}/metricbeat-metricset-aws-rds.html[`rds`] metricset to collect +metrics about your Aurora instances. + +[float] +[role="xpack"] +==== Google Cloud Platform (GCP) Pub/Sub and Load Balancer monitoring + +We've enhanced the {metricbeat} Google Cloud Platform module with support +for monitoring additional services: + +* The {metricbeat-ref}/metricbeat-metricset-googlecloud-pubsub.html[`pubsub`] +metricset connects to the Stackdriver API and collects metrics for topics, +subscriptions, and snapshots used by a specified account.  +* The {metricbeat-ref}/metricbeat-metricset-googlecloud-loadbalancing.html[`loadbalancing`] +metricset captures load balancing performance metrics for HTTP(S), TCP, and UDP +applications. + +[float] +[role="xpack"] +==== Pivotal Cloud Foundry (PCF) monitoring + +We continue to expand coverage of container platforms by adding support for +Pivotal Cloud Foundry. + +The new {metricbeat} +{metricbeat-ref}/metricbeat-module-cloudfoundry.html[Cloudfoundry module] +connects to the Cloud Foundry API and pulls container, counter, and value +metrics from it. These metrics are stored in `cloudfoundry.container`, +`cloudfoundry.counter` and `cloudfoundry.value` metricsets. + +In {filebeat}, the new +{filebeat-ref}/filebeat-input-cloudfoundry.html[`cloudfoundry`] input collects +http access logs, container logs, and error logs from Cloud Foundry. + +To learn how to run {beats} on Cloud Foundry, see: + +* {metricbeat-ref}/running-on-cloudfoundry.html[Run {metricbeat} on Cloud Foundry] +* {filebeat-ref}/running-on-cloudfoundry.html[Run {filebeat} on Cloud Foundry] + +[float] +[role="xpack"] +==== IBM MQ monitoring + +Prior to this release, we offered support in {filebeat} for collecting and +parsing queue manager error logs from IBM MQ. + +In this release, we’ve added the missing piece: metrics. The new {metricbeat} +{metricbeat-ref}/metricbeat-module-ibmmq.html[IBM MQ module] pulls status +information for the Queue Manager, which is responsible for maintaining queues +and ensuring that messages in the queues reach their destination. + +[float] +[role="xpack"] +==== Redis Enterprise monitoring + +In addition to our existing Redis module, which focuses on the open source +version of the database, we’ve added the new {metricbeat} +{metricbeat-ref}/metricbeat-module-redisenterprise.html[Redis Enterprise] module +to monitor features such as nodes and proxies in a Redis cluster. + +[float] +[role="xpack"] +==== Istio monitoring + +For Istio users, we've introduced the {metricbeat} +{metricbeat-ref}/metricbeat-module-istio.html[Istio module] to +collect metrics about service traffic (in, out, and within a service mesh), +control-plane metrics for Istio Pilot, Galley, Mixer components, and much +more. + +[float] +==== ECS field improvements in {filebeat} + +The {ecs-ref}/index.html[Elastic Common Schema] (ECS) defines a common set of +fields to be used when storing event data in {es}. + +In 7.7, we've improved ECS field mappings in numerous {filebeat} modules, +making it easier for you to analyze, visualize, and correlate data across +events. For a list of affected modules, see the +{beats-ref}/release-notes.html[Release Notes] for 7.7.0. // end::notable-highlights[] diff --git a/libbeat/docs/shared-autodiscover.asciidoc b/libbeat/docs/shared-autodiscover.asciidoc index a3911a65490..5279b6f3ea0 100644 --- a/libbeat/docs/shared-autodiscover.asciidoc +++ b/libbeat/docs/shared-autodiscover.asciidoc @@ -124,6 +124,7 @@ running configuration for a container, 60s by default. ======================================= endif::[] + [float] ===== Kubernetes @@ -241,7 +242,6 @@ running configuration for a container, 60s by default. include_labels: ["nodelabel2"] ------------------------------------------------------------------------------------- - include::../../{beatname_lc}/docs/autodiscover-kubernetes-config.asciidoc[] [float] diff --git a/libbeat/docs/shared-deduplication.asciidoc b/libbeat/docs/shared-deduplication.asciidoc index 997f12c488b..eec45caaf97 100644 --- a/libbeat/docs/shared-deduplication.asciidoc +++ b/libbeat/docs/shared-deduplication.asciidoc @@ -73,11 +73,11 @@ from the JSON string and stores it in the `@metadata._id` field: [source,yaml] ---- processors: - - decode_json_fields: - document_id: "myid" - fields: ["message"] - max_depth: 1 - target: "" + - decode_json_fields: + document_id: "myid" + fields: ["message"] + max_depth: 1 + target: "" ---- + The resulting document ID is `100`. diff --git a/libbeat/docs/shared-kerberos-config.asciidoc b/libbeat/docs/shared-kerberos-config.asciidoc new file mode 100644 index 00000000000..7accd6f7df9 --- /dev/null +++ b/libbeat/docs/shared-kerberos-config.asciidoc @@ -0,0 +1,85 @@ +[[configuration-kerberos]] +== Configure Kerberos + +You can specify Kerberos options with any output or input that supports Kerberos, like {es} and Kafka. + +The following encryption types are supported: + +* aes128-cts-hmac-sha1-96 +* aes128-cts-hmac-sha256-128 +* aes256-cts-hmac-sha1-96 +* aes256-cts-hmac-sha384-192 +* des3-cbc-sha1-kd +* rc4-hmac + +Example output config with Kerberos password based authentication: + +[source,yaml] +---- +output.elasticsearch.hosts: ["http://my-elasticsearch.elastic.co:9200"] +output.elasticsearch.kerberos.auth_type: password +output.elasticsearch.kerberos.username: "elastic" +output.elasticsearch.kerberos.password: "changeme" +output.elasticsearch.kerberos.config_path: "/etc/krb5.conf" +output.elasticsearch.kerberos.realm: "ELASTIC.CO" +---- + +The service principal name for the Elasticsearch instance is contructed from these options. Based on this configuration +it is going to be `HTTP/my-elasticsearch.elastic.co@ELASTIC.CO`. + +[float] +=== Configuration options + +You can specify the following options in the `kerberos` section of the +{beatname_lc}.yml+ config file: + +[float] +==== `enabled` + +The `enabled` setting can be used to enable the kerberos configuration by setting +it to `false`. The default value is `true`. + +NOTE: Kerberos settings are disabled if either `enabled` is set to `false` or the +`kerberos` section is missing. + +[float] +==== `auth_type` + +There are two options to authenticate with Kerberos KDC: `password` and `keytab`. + +`password` expects the principal name and its password. When choosing `keytab`, you +have to specify a princial name and a path to a keytab. The keytab must contain +the keys of the selected principal. Otherwise, authentication will fail. + +[float] +==== `config_path` + +You need to set the path to the `krb5.conf`, so +{beatname_lc} can find the Kerberos KDC to +retrieve a ticket. + +[float] +==== `username` + +Name of the principal used to connect to the output. + +[float] +==== `password` + +If you configured `password` for `auth_type`, you have to provide a password +for the selected principal. + +[float] +==== `keytab` + +If you configured `keytab` for `auth_type`, you have to provide the path to the +keytab of the selected principal. + +[float] +==== `service_name` + +This option can only be configured for Kafka. It is the name of the Kafka service, usually `kafka`. + +[float] +==== `realm` + +Name of the realm where the output resides. + diff --git a/libbeat/esleg/eslegclient/bulkapi.go b/libbeat/esleg/eslegclient/bulkapi.go index 86b518eeea1..ae7ea92f8ba 100644 --- a/libbeat/esleg/eslegclient/bulkapi.go +++ b/libbeat/esleg/eslegclient/bulkapi.go @@ -19,6 +19,7 @@ package eslegclient import ( "bytes" + "context" "encoding/json" "errors" "io" @@ -26,6 +27,9 @@ import ( "net/http" "strings" + "go.elastic.co/apm" + "go.elastic.co/apm/module/apmhttp" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" ) @@ -59,6 +63,7 @@ type BulkResult json.RawMessage // Bulk performs many index/delete operations in a single API call. // Implements: http://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html func (conn *Connection) Bulk( + ctx context.Context, index, docType string, params map[string]string, body []interface{}, ) (int, BulkResult, error) { @@ -69,13 +74,16 @@ func (conn *Connection) Bulk( enc := conn.Encoder enc.Reset() if err := bulkEncode(conn.log, enc, body); err != nil { + apm.CaptureError(ctx, err).Send() return 0, nil, err } requ, err := newBulkRequest(conn.URL, index, docType, params, enc) if err != nil { + apm.CaptureError(ctx, err).Send() return 0, nil, err } + requ.requ = apmhttp.RequestWithContext(ctx, requ.requ) return conn.sendBulkRequest(requ) } diff --git a/libbeat/esleg/eslegclient/bulkapi_integration_test.go b/libbeat/esleg/eslegclient/bulkapi_integration_test.go index 49bb7497d56..d2201b6e8bd 100644 --- a/libbeat/esleg/eslegclient/bulkapi_integration_test.go +++ b/libbeat/esleg/eslegclient/bulkapi_integration_test.go @@ -20,6 +20,7 @@ package eslegclient import ( + "context" "fmt" "os" "testing" @@ -53,7 +54,7 @@ func TestBulk(t *testing.T) { params := map[string]string{ "refresh": "true", } - _, _, err := client.Bulk(index, "", params, body) + _, _, err := client.Bulk(context.Background(), index, "", params, body) if err != nil { t.Fatalf("Bulk() returned error: %s", err) } @@ -86,7 +87,7 @@ func TestEmptyBulk(t *testing.T) { params := map[string]string{ "refresh": "true", } - _, resp, err := client.Bulk(index, "", params, body) + _, resp, err := client.Bulk(context.Background(), index, "", params, body) if err != nil { t.Fatalf("Bulk() returned error: %s", err) } @@ -150,7 +151,7 @@ func TestBulkMoreOperations(t *testing.T) { params := map[string]string{ "refresh": "true", } - _, resp, err := client.Bulk(index, "", params, body) + _, resp, err := client.Bulk(context.Background(), index, "", params, body) if err != nil { t.Fatalf("Bulk() returned error: %s [%s]", err, resp) } diff --git a/libbeat/esleg/eslegclient/bulkapi_mock_test.go b/libbeat/esleg/eslegclient/bulkapi_mock_test.go index 1fbd53d9425..ded3e53c95c 100644 --- a/libbeat/esleg/eslegclient/bulkapi_mock_test.go +++ b/libbeat/esleg/eslegclient/bulkapi_mock_test.go @@ -20,6 +20,7 @@ package eslegclient import ( + "context" "fmt" "net/http" "os" @@ -60,7 +61,7 @@ func TestOneHostSuccessResp_Bulk(t *testing.T) { params := map[string]string{ "refresh": "true", } - _, _, err := client.Bulk(index, "type1", params, body) + _, _, err := client.Bulk(context.Background(), index, "type1", params, body) if err != nil { t.Errorf("Bulk() returns error: %s", err) } @@ -96,7 +97,7 @@ func TestOneHost500Resp_Bulk(t *testing.T) { params := map[string]string{ "refresh": "true", } - _, _, err := client.Bulk(index, "type1", params, body) + _, _, err := client.Bulk(context.Background(), index, "type1", params, body) if err == nil { t.Errorf("Bulk() should return error.") } @@ -136,7 +137,7 @@ func TestOneHost503Resp_Bulk(t *testing.T) { params := map[string]string{ "refresh": "true", } - _, _, err := client.Bulk(index, "type1", params, body) + _, _, err := client.Bulk(context.Background(), index, "type1", params, body) if err == nil { t.Errorf("Bulk() should return error.") } diff --git a/libbeat/esleg/eslegclient/config.go b/libbeat/esleg/eslegclient/config.go index 5c171a4eb2b..d9a299d68c7 100644 --- a/libbeat/esleg/eslegclient/config.go +++ b/libbeat/esleg/eslegclient/config.go @@ -22,6 +22,7 @@ import ( "time" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/transport/kerberos" "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) @@ -32,7 +33,8 @@ type config struct { Params map[string]string `config:"parameters"` Headers map[string]string `config:"headers"` - TLS *tlscommon.Config `config:"ssl"` + TLS *tlscommon.Config `config:"ssl"` + Kerberos *kerberos.Config `config:"kerberos"` ProxyURL string `config:"proxy_url"` ProxyDisable bool `config:"proxy_disable"` diff --git a/libbeat/esleg/eslegclient/connection.go b/libbeat/esleg/eslegclient/connection.go index e1c20f795bd..138c9ab3c83 100644 --- a/libbeat/esleg/eslegclient/connection.go +++ b/libbeat/esleg/eslegclient/connection.go @@ -26,19 +26,27 @@ import ( "net/url" "time" + "go.elastic.co/apm/module/apmelasticsearch" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/transport" + "github.com/elastic/beats/v7/libbeat/common/transport/kerberos" "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/libbeat/testing" ) +type esHTTPClient interface { + Do(req *http.Request) (resp *http.Response, err error) + CloseIdleConnections() +} + // Connection manages the connection for a given client. type Connection struct { ConnectionSettings Encoder BodyEncoder - HTTP *http.Client + HTTP esHTTPClient version common.Version log *logp.Logger @@ -55,7 +63,8 @@ type ConnectionSettings struct { APIKey string Headers map[string]string - TLS *tlscommon.TLSConfig + TLS *tlscommon.TLSConfig + Kerberos *kerberos.Config OnConnectCallback func() error Observer transport.IOStatser @@ -120,20 +129,41 @@ func NewConnection(s ConnectionSettings) (*Connection, error) { } } - return &Connection{ - ConnectionSettings: s, - HTTP: &http.Client{ + var httpClient esHTTPClient + // when dropping the legacy client in favour of the official Go client, it should be instrumented + // eg, like in https://github.com/elastic/apm-server/blob/7.7/elasticsearch/client.go + httpClient = &http.Client{ + Transport: apmelasticsearch.WrapRoundTripper(&http.Transport{ + Dial: dialer.Dial, + DialTLS: tlsDialer.Dial, + TLSClientConfig: s.TLS.ToConfig(), + Proxy: proxy, + IdleConnTimeout: s.IdleConnTimeout, + }), + Timeout: s.Timeout, + } + + if s.Kerberos.IsEnabled() { + c := &http.Client{ Transport: &http.Transport{ Dial: dialer.Dial, - DialTLS: tlsDialer.Dial, - TLSClientConfig: s.TLS.ToConfig(), Proxy: proxy, IdleConnTimeout: s.IdleConnTimeout, }, Timeout: s.Timeout, - }, - Encoder: encoder, - log: logp.NewLogger("esclientleg"), + } + httpClient, err = kerberos.NewClient(s.Kerberos, c, s.URL) + if err != nil { + return nil, err + } + logp.Info("kerberos client created") + } + + return &Connection{ + ConnectionSettings: s, + HTTP: httpClient, + Encoder: encoder, + log: logp.NewLogger("esclientleg"), }, nil } @@ -190,6 +220,7 @@ func NewClients(cfg *common.Config) ([]Connection, error) { Proxy: proxyURL, ProxyDisable: config.ProxyDisable, TLS: tlsConfig, + Kerberos: config.Kerberos, Username: config.Username, Password: config.Password, APIKey: config.APIKey, diff --git a/libbeat/esleg/eslegtest/util.go b/libbeat/esleg/eslegtest/util.go index 8da334dc3a4..28f33fde2dc 100644 --- a/libbeat/esleg/eslegtest/util.go +++ b/libbeat/esleg/eslegtest/util.go @@ -64,6 +64,11 @@ func GetEsHost() string { return getEnv("ES_HOST", ElasticsearchDefaultHost) } +// GetEsKerberosHost returns the Elasticsearch testing host. +func GetEsKerberosHost() string { + return getEnv("ES_KERBEROS_HOST", ElasticsearchDefaultHost) +} + // getEsPort returns the Elasticsearch testing port. func getEsPort() string { return getEnv("ES_PORT", ElasticsearchDefaultPort) diff --git a/libbeat/magefile.go b/libbeat/magefile.go index e5a22799b6d..67e84e63be3 100644 --- a/libbeat/magefile.go +++ b/libbeat/magefile.go @@ -49,5 +49,5 @@ func Fields() error { // Config generates example and reference configuration for libbeat. func Config() error { - return devtools.Config(devtools.ShortConfigType|devtools.ReferenceConfigType, devtools.ConfigFileParams{}, ".") + return devtools.Config(devtools.ShortConfigType|devtools.ReferenceConfigType, devtools.DefaultConfigFileParams(), ".") } diff --git a/libbeat/mapping/field.go b/libbeat/mapping/field.go index 7b2ba52e618..79f771bb7dd 100644 --- a/libbeat/mapping/field.go +++ b/libbeat/mapping/field.go @@ -124,7 +124,7 @@ func (f *Field) Validate() error { func (f *Field) validateType() error { switch strings.ToLower(f.Type) { - case "text", "keyword": + case "text", "keyword", "wildcard": return stringType.validate(f.Format) case "long", "integer", "short", "byte", "double", "float", "half_float", "scaled_float": return numberType.validate(f.Format) diff --git a/libbeat/monitoring/report/elasticsearch/client.go b/libbeat/monitoring/report/elasticsearch/client.go index 9e8469ab547..fb83a2e636b 100644 --- a/libbeat/monitoring/report/elasticsearch/client.go +++ b/libbeat/monitoring/report/elasticsearch/client.go @@ -18,11 +18,14 @@ package elasticsearch import ( + "context" "encoding/json" "fmt" "net/http" "time" + "go.elastic.co/apm" + "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" @@ -103,7 +106,7 @@ func (c *publishClient) Close() error { return c.es.Close() } -func (c *publishClient) Publish(batch publisher.Batch) error { +func (c *publishClient) Publish(ctx context.Context, batch publisher.Batch) error { events := batch.Events() var failed []publisher.Event var reason error @@ -141,7 +144,7 @@ func (c *publishClient) Publish(batch publisher.Batch) error { case report.FormatXPackMonitoringBulk: err = c.publishXPackBulk(params, event, typ) case report.FormatBulk: - err = c.publishBulk(event, typ) + err = c.publishBulk(ctx, event, typ) } if err != nil { @@ -186,7 +189,7 @@ func (c *publishClient) publishXPackBulk(params map[string]string, event publish return err } -func (c *publishClient) publishBulk(event publisher.Event, typ string) error { +func (c *publishClient) publishBulk(ctx context.Context, event publisher.Event, typ string) error { meta := common.MapStr{ "_index": getMonitoringIndexName(), "_routing": nil, @@ -233,8 +236,9 @@ func (c *publishClient) publishBulk(event publisher.Event, typ string) error { // Currently one request per event is sent. Reason is that each event can contain different // interval params and X-Pack requires to send the interval param. - _, result, err := c.es.Bulk(getMonitoringIndexName(), "", nil, bulk[:]) + _, result, err := c.es.Bulk(ctx, getMonitoringIndexName(), "", nil, bulk[:]) if err != nil { + apm.CaptureError(ctx, fmt.Errorf("failed to perform any bulk index operations: %w", err)).Send() return err } diff --git a/libbeat/monitoring/report/report.go b/libbeat/monitoring/report/report.go index e6812515af9..0f79af4e874 100644 --- a/libbeat/monitoring/report/report.go +++ b/libbeat/monitoring/report/report.go @@ -21,6 +21,8 @@ import ( "errors" "fmt" + errw "github.com/pkg/errors" + "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" ) @@ -59,6 +61,10 @@ type Reporter interface { type ReporterFactory func(beat.Info, Settings, *common.Config) (Reporter, error) +type hostsCfg struct { + Hosts []string `config:"hosts"` +} + var ( defaultConfig = config{} @@ -111,9 +117,7 @@ func getReporterConfig( // merge reporter config with output config if both are present if outCfg := outputs.Config(); outputs.Name() == name && outCfg != nil { // require monitoring to not configure any hosts if output is configured: - hosts := struct { - Hosts []string `config:"hosts"` - }{} + hosts := hostsCfg{} rc.Unpack(&hosts) if settings.Format == FormatXPackMonitoringBulk && len(hosts.Hosts) > 0 { @@ -127,6 +131,13 @@ func getReporterConfig( if err != nil { return "", nil, err } + + // Make sure hosts from reporter configuration get precedence over hosts + // from output configuration + if err := mergeHosts(merged, outCfg, rc); err != nil { + return "", nil, err + } + rc = merged } @@ -155,3 +166,44 @@ func collectSubObject(cfg *common.Config) *common.Config { } return out } + +func mergeHosts(merged, outCfg, reporterCfg *common.Config) error { + if merged == nil { + merged = common.NewConfig() + } + + outputHosts := hostsCfg{} + if outCfg != nil { + if err := outCfg.Unpack(&outputHosts); err != nil { + return errw.Wrap(err, "unable to parse hosts from output config") + } + } + + reporterHosts := hostsCfg{} + if reporterCfg != nil { + if err := reporterCfg.Unpack(&reporterHosts); err != nil { + return errw.Wrap(err, "unable to parse hosts from reporter config") + } + } + + if len(outputHosts.Hosts) == 0 && len(reporterHosts.Hosts) == 0 { + return nil + } + + // Give precedence to reporter hosts over output hosts + var newHostsCfg *common.Config + var err error + if len(reporterHosts.Hosts) > 0 { + newHostsCfg, err = common.NewConfigFrom(reporterHosts.Hosts) + } else { + newHostsCfg, err = common.NewConfigFrom(outputHosts.Hosts) + } + if err != nil { + return errw.Wrap(err, "unable to make config from new hosts") + } + + if err := merged.SetChild("hosts", -1, newHostsCfg); err != nil { + return errw.Wrap(err, "unable to set new hosts into merged config") + } + return nil +} diff --git a/libbeat/monitoring/report/report_test.go b/libbeat/monitoring/report/report_test.go new file mode 100644 index 00000000000..45b0dadc83f --- /dev/null +++ b/libbeat/monitoring/report/report_test.go @@ -0,0 +1,78 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package report + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/libbeat/common" +) + +func TestMergeHosts(t *testing.T) { + tests := map[string]struct { + outCfg *common.Config + reporterCfg *common.Config + expectedCfg *common.Config + }{ + "no_hosts": { + expectedCfg: newConfigWithHosts(), + }, + "only_reporter_hosts": { + reporterCfg: newConfigWithHosts("r1", "r2"), + expectedCfg: newConfigWithHosts("r1", "r2"), + }, + "only_output_hosts": { + outCfg: newConfigWithHosts("o1", "o2"), + expectedCfg: newConfigWithHosts("o1", "o2"), + }, + "equal_hosts": { + outCfg: newConfigWithHosts("o1", "o2"), + reporterCfg: newConfigWithHosts("r1", "r2"), + expectedCfg: newConfigWithHosts("r1", "r2"), + }, + "more_output_hosts": { + outCfg: newConfigWithHosts("o1", "o2"), + reporterCfg: newConfigWithHosts("r1"), + expectedCfg: newConfigWithHosts("r1"), + }, + "more_reporter_hosts": { + outCfg: newConfigWithHosts("o1"), + reporterCfg: newConfigWithHosts("r1", "r2"), + expectedCfg: newConfigWithHosts("r1", "r2"), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + mergedCfg := common.MustNewConfigFrom(map[string]interface{}{}) + err := mergeHosts(mergedCfg, test.outCfg, test.reporterCfg) + require.NoError(t, err) + + require.Equal(t, test.expectedCfg, mergedCfg) + }) + } +} + +func newConfigWithHosts(hosts ...string) *common.Config { + if len(hosts) == 0 { + return common.MustNewConfigFrom(map[string][]string{}) + } + return common.MustNewConfigFrom(map[string][]string{"hosts": hosts}) +} diff --git a/libbeat/outputs/backoff.go b/libbeat/outputs/backoff.go index 256b8029b09..5c1ece2e5db 100644 --- a/libbeat/outputs/backoff.go +++ b/libbeat/outputs/backoff.go @@ -18,6 +18,7 @@ package outputs import ( + "context" "errors" "time" @@ -56,8 +57,8 @@ func (b *backoffClient) Close() error { return err } -func (b *backoffClient) Publish(batch publisher.Batch) error { - err := b.client.Publish(batch) +func (b *backoffClient) Publish(ctx context.Context, batch publisher.Batch) error { + err := b.client.Publish(ctx, batch) if err != nil { b.client.Close() } diff --git a/libbeat/outputs/console/console.go b/libbeat/outputs/console/console.go index 79aee6957d6..bbce8f449a9 100644 --- a/libbeat/outputs/console/console.go +++ b/libbeat/outputs/console/console.go @@ -19,6 +19,7 @@ package console import ( "bufio" + "context" "fmt" "os" "runtime" @@ -102,7 +103,7 @@ func newConsole(index string, observer outputs.Observer, codec codec.Codec) (*co } func (c *console) Close() error { return nil } -func (c *console) Publish(batch publisher.Batch) error { +func (c *console) Publish(_ context.Context, batch publisher.Batch) error { st := c.observer events := batch.Events() st.NewBatch(len(events)) diff --git a/libbeat/outputs/console/console_test.go b/libbeat/outputs/console/console_test.go index 29201beee54..a8e85601a89 100644 --- a/libbeat/outputs/console/console_test.go +++ b/libbeat/outputs/console/console_test.go @@ -21,6 +21,7 @@ package console import ( "bytes" + "context" "io" "os" "testing" @@ -130,7 +131,7 @@ func run(codec codec.Codec, batches ...publisher.Batch) (string, error) { return withStdout(func() { c, _ := newConsole("test", outputs.NewNilObserver(), codec) for _, b := range batches { - c.Publish(b) + c.Publish(context.Background(), b) } }) } diff --git a/libbeat/outputs/elasticsearch/client.go b/libbeat/outputs/elasticsearch/client.go index 2969c0f057b..4e9c8875b31 100644 --- a/libbeat/outputs/elasticsearch/client.go +++ b/libbeat/outputs/elasticsearch/client.go @@ -18,12 +18,15 @@ package elasticsearch import ( + "context" "encoding/base64" "errors" "fmt" "net/http" "time" + "go.elastic.co/apm" + "github.com/elastic/beats/v7/libbeat/testing" "github.com/elastic/beats/v7/libbeat/beat" @@ -84,6 +87,7 @@ func NewClient( APIKey: base64.StdEncoding.EncodeToString([]byte(s.APIKey)), Headers: s.Headers, TLS: s.TLS, + Kerberos: s.Kerberos, Proxy: s.Proxy, ProxyDisable: s.ProxyDisable, Parameters: s.Parameters, @@ -150,12 +154,13 @@ func (client *Client) Clone() *Client { // empty. ProxyDisable: client.conn.Proxy == nil, TLS: client.conn.TLS, + Kerberos: client.conn.Kerberos, Username: client.conn.Username, Password: client.conn.Password, APIKey: client.conn.APIKey, Parameters: nil, // XXX: do not pass params? Headers: client.conn.Headers, - Timeout: client.conn.HTTP.Timeout, + Timeout: client.conn.Timeout, CompressionLevel: client.conn.CompressionLevel, OnConnectCallback: nil, Observer: nil, @@ -169,9 +174,9 @@ func (client *Client) Clone() *Client { return c } -func (client *Client) Publish(batch publisher.Batch) error { +func (client *Client) Publish(ctx context.Context, batch publisher.Batch) error { events := batch.Events() - rest, err := client.publishEvents(events) + rest, err := client.publishEvents(ctx, events) if len(rest) == 0 { batch.ACK() } else { @@ -183,9 +188,9 @@ func (client *Client) Publish(batch publisher.Batch) error { // PublishEvents sends all events to elasticsearch. On error a slice with all // events not published or confirmed to be processed by elasticsearch will be // returned. The input slice backing memory will be reused by return the value. -func (client *Client) publishEvents( - data []publisher.Event, -) ([]publisher.Event, error) { +func (client *Client) publishEvents(ctx context.Context, data []publisher.Event) ([]publisher.Event, error) { + span, ctx := apm.StartSpan(ctx, "publishEvents", "output") + defer span.End() begin := time.Now() st := client.observer @@ -200,8 +205,10 @@ func (client *Client) publishEvents( // encode events into bulk request buffer, dropping failed elements from // events slice origCount := len(data) + span.Context.SetLabel("events_original", origCount) data, bulkItems := bulkEncodePublishRequest(client.log, client.conn.GetVersion(), client.index, client.pipeline, data) newCount := len(data) + span.Context.SetLabel("events_encoded", newCount) if st != nil && origCount > newCount { st.Dropped(origCount - newCount) } @@ -209,14 +216,18 @@ func (client *Client) publishEvents( return nil, nil } - status, result, sendErr := client.conn.Bulk("", "", nil, bulkItems) + status, result, sendErr := client.conn.Bulk(ctx, "", "", nil, bulkItems) if sendErr != nil { - client.log.Errorf("Failed to perform any bulk index operations: %s", sendErr) + err := apm.CaptureError(ctx, fmt.Errorf("failed to perform any bulk index operations: %w", sendErr)) + err.Send() + client.log.Error(err) return data, sendErr } + pubCount := len(data) + span.Context.SetLabel("events_published", pubCount) client.log.Debugf("PublishEvents: %d events have been published to elasticsearch in %v.", - len(data), + pubCount, time.Now().Sub(begin)) // check response for transient errors @@ -230,6 +241,7 @@ func (client *Client) publishEvents( } failed := len(failedEvents) + span.Context.SetLabel("events_failed", failed) if st := client.observer; st != nil { dropped := stats.nonIndexable duplicates := stats.duplicates diff --git a/libbeat/outputs/elasticsearch/client_integration_test.go b/libbeat/outputs/elasticsearch/client_integration_test.go index 1e01b757da0..9abbbe39873 100644 --- a/libbeat/outputs/elasticsearch/client_integration_test.go +++ b/libbeat/outputs/elasticsearch/client_integration_test.go @@ -21,6 +21,7 @@ package elasticsearch import ( "context" + "fmt" "io/ioutil" "math/rand" "net/http" @@ -29,6 +30,8 @@ import ( "testing" "time" + "go.elastic.co/apm/apmtest" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -43,9 +46,39 @@ import ( func TestClientPublishEvent(t *testing.T) { index := "beat-int-pub-single-event" - output, client := connectTestEs(t, map[string]interface{}{ + cfg := map[string]interface{}{ "index": index, - }) + } + + testPublishEvent(t, index, cfg) +} + +func TestClientPublishEventKerberosAware(t *testing.T) { + err := setupRoleMapping(t, eslegtest.GetEsKerberosHost()) + if err != nil { + t.Fatal(err) + } + + index := "beat-int-pub-single-event-behind-kerb" + cfg := map[string]interface{}{ + "hosts": eslegtest.GetEsKerberosHost(), + "index": index, + "username": "", + "password": "", + "kerberos": map[string]interface{}{ + "auth_type": "password", + "config_path": "testdata/krb5.conf", + "username": eslegtest.GetUser(), + "password": eslegtest.GetPass(), + "realm": "ELASTIC", + }, + } + + testPublishEvent(t, index, cfg) +} + +func testPublishEvent(t *testing.T, index string, cfg map[string]interface{}) { + output, client := connectTestEs(t, cfg) // drop old index preparing test client.conn.Delete(index, "", "", nil) @@ -58,7 +91,7 @@ func TestClientPublishEvent(t *testing.T) { }, }) - err := output.Publish(batch) + err := output.Publish(context.Background(), batch) if err != nil { t.Fatal(err) } @@ -96,7 +129,7 @@ func TestClientPublishEventWithPipeline(t *testing.T) { } publish := func(event beat.Event) { - err := output.Publish(outest.NewBatch(event)) + err := output.Publish(context.Background(), outest.NewBatch(event)) if err != nil { t.Fatal(err) } @@ -177,7 +210,7 @@ func TestClientBulkPublishEventsWithPipeline(t *testing.T) { } publish := func(events ...beat.Event) { - err := output.Publish(outest.NewBatch(events...)) + err := output.Publish(context.Background(), outest.NewBatch(events...)) if err != nil { t.Fatal(err) } @@ -241,6 +274,46 @@ func TestClientBulkPublishEventsWithPipeline(t *testing.T) { assert.Equal(t, 1, getCount("testfield:0")) // no pipeline } +func TestClientPublishTracer(t *testing.T) { + index := "beat-apm-tracer-test" + output, client := connectTestEs(t, map[string]interface{}{ + "index": index, + }) + + client.conn.Delete(index, "", "", nil) + + batch := outest.NewBatch(beat.Event{ + Timestamp: time.Now(), + Fields: common.MapStr{ + "message": "Hello world", + }, + }) + + tx, spans, _ := apmtest.WithTransaction(func(ctx context.Context) { + err := output.Publish(ctx, batch) + if err != nil { + t.Fatal(err) + } + }) + require.Len(t, spans, 2) + + // get spans in reverse order + firstSpan := spans[1] + + assert.Equal(t, "publishEvents", firstSpan.Name) + assert.Equal(t, "output", firstSpan.Type) + assert.Equal(t, [8]byte(firstSpan.TransactionID), [8]byte(tx.ID)) + assert.True(t, len(firstSpan.Context.Tags) > 0, "no tags found") + + secondSpan := spans[0] + assert.Contains(t, secondSpan.Name, "POST") + assert.Equal(t, "db", secondSpan.Type) + assert.Equal(t, "elasticsearch", secondSpan.Subtype) + assert.Equal(t, [8]byte(secondSpan.ParentID), [8]byte(firstSpan.ID)) + assert.Equal(t, [8]byte(secondSpan.TransactionID), [8]byte(tx.ID)) + assert.Equal(t, "/_bulk", secondSpan.Context.HTTP.URL.Path) +} + func connectTestEs(t *testing.T, cfg interface{}) (outputs.Client, *Client) { config, err := common.NewConfigFrom(map[string]interface{}{ "hosts": eslegtest.GetEsHost(), @@ -281,6 +354,33 @@ func connectTestEs(t *testing.T, cfg interface{}) (outputs.Client, *Client) { return client, client } +// setupRoleMapping sets up role mapping for the Kerberos user beats@ELASTIC +func setupRoleMapping(t *testing.T, host string) error { + _, client := connectTestEs(t, map[string]interface{}{ + "hosts": host, + "username": "elastic", + "password": "changeme", + }) + + roleMappingURL := client.conn.URL + "/_security/role_mapping/kerbrolemapping" + + status, _, err := client.conn.RequestURL("POST", roleMappingURL, map[string]interface{}{ + "roles": []string{"superuser"}, + "enabled": true, + "rules": map[string]interface{}{ + "field": map[string]interface{}{ + "username": "beats@ELASTIC", + }, + }, + }) + + if status >= 300 { + return fmt.Errorf("non-2xx return code: %d", status) + } + + return err +} + func randomClient(grp outputs.Group) outputs.NetworkClient { L := len(grp.Clients) if L == 0 { diff --git a/libbeat/outputs/elasticsearch/client_test.go b/libbeat/outputs/elasticsearch/client_test.go index d69849dabab..5219d052987 100644 --- a/libbeat/outputs/elasticsearch/client_test.go +++ b/libbeat/outputs/elasticsearch/client_test.go @@ -20,6 +20,7 @@ package elasticsearch import ( + "context" "fmt" "net/http" "net/http/httptest" @@ -242,7 +243,7 @@ func TestClientWithHeaders(t *testing.T) { }} batch := outest.NewBatch(event, event, event) - err = client.Publish(batch) + err = client.Publish(context.Background(), batch) assert.NoError(t, err) assert.Equal(t, 2, requestCount) } diff --git a/libbeat/outputs/elasticsearch/config.go b/libbeat/outputs/elasticsearch/config.go index 499bba2eeff..d094f005df5 100644 --- a/libbeat/outputs/elasticsearch/config.go +++ b/libbeat/outputs/elasticsearch/config.go @@ -22,6 +22,7 @@ import ( "time" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/transport/kerberos" "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) @@ -39,6 +40,7 @@ type elasticsearchConfig struct { CompressionLevel int `config:"compression_level" validate:"min=0, max=9"` EscapeHTML bool `config:"escape_html"` TLS *tlscommon.Config `config:"ssl"` + Kerberos *kerberos.Config `config:"kerberos"` BulkMaxSize int `config:"bulk_max_size"` MaxRetries int `config:"max_retries"` Timeout time.Duration `config:"timeout"` @@ -69,6 +71,7 @@ var ( CompressionLevel: 0, EscapeHTML: false, TLS: nil, + Kerberos: nil, LoadBalance: true, Backoff: Backoff{ Init: 1 * time.Second, diff --git a/libbeat/outputs/elasticsearch/docs/elasticsearch.asciidoc b/libbeat/outputs/elasticsearch/docs/elasticsearch.asciidoc index c36e6b24163..254349f39bc 100644 --- a/libbeat/outputs/elasticsearch/docs/elasticsearch.asciidoc +++ b/libbeat/outputs/elasticsearch/docs/elasticsearch.asciidoc @@ -676,3 +676,11 @@ for HTTPS-based connections. If the `ssl` section is missing, the host CAs are u Elasticsearch. See <> for more information. + +===== `kebreros` + +Configuration options for Kerberos authentication. + +See <> for more information. + +include::{libbeat-dir}/shared-kerberos-config.asciidoc[] diff --git a/libbeat/outputs/elasticsearch/elasticsearch.go b/libbeat/outputs/elasticsearch/elasticsearch.go index b6c3bd797a9..512b74895ea 100644 --- a/libbeat/outputs/elasticsearch/elasticsearch.go +++ b/libbeat/outputs/elasticsearch/elasticsearch.go @@ -97,6 +97,7 @@ func makeES( Proxy: proxyURL, ProxyDisable: config.ProxyDisable, TLS: tlsConfig, + Kerberos: config.Kerberos, Username: config.Username, Password: config.Password, APIKey: config.APIKey, diff --git a/libbeat/outputs/elasticsearch/testdata/krb5.conf b/libbeat/outputs/elasticsearch/testdata/krb5.conf new file mode 100644 index 00000000000..355145f315f --- /dev/null +++ b/libbeat/outputs/elasticsearch/testdata/krb5.conf @@ -0,0 +1,43 @@ +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[libdefaults] + default_realm = ELASTIC + dns_canonicalize_hostname = false + dns_lookup_kdc = false + dns_lookup_realm = false + dns_uri_lookup = false + forwardable = true + ignore_acceptor_hostname = true + rdns = false + default_tgs_enctypes = aes128-cts-hmac-sha1-96 + default_tkt_enctypes = aes128-cts-hmac-sha1-96 + permitted_enctypes = aes128-cts-hmac-sha1-96 + udp_preference_limit = 1 + kdc_timeout = 3000 + +[realms] + ELASTIC = { + kdc = elasticsearch_kerberos.elastic:1088 + admin_server = elasticsearch_kerberos.elastic:1749 + default_domain = elastic + } + +[domain_realm] + .elastic = ELASTIC + elastic = ELASTIC + diff --git a/libbeat/outputs/failover.go b/libbeat/outputs/failover.go index b388a58a61f..f64720a7895 100644 --- a/libbeat/outputs/failover.go +++ b/libbeat/outputs/failover.go @@ -18,6 +18,7 @@ package outputs import ( + "context" "errors" "fmt" "math/rand" @@ -91,12 +92,12 @@ func (f *failoverClient) Close() error { return f.clients[f.active].Close() } -func (f *failoverClient) Publish(batch publisher.Batch) error { +func (f *failoverClient) Publish(ctx context.Context, batch publisher.Batch) error { if f.active < 0 { batch.Retry() return errNoActiveConnection } - return f.clients[f.active].Publish(batch) + return f.clients[f.active].Publish(ctx, batch) } func (f *failoverClient) Test(d testing.Driver) { diff --git a/libbeat/outputs/fileout/file.go b/libbeat/outputs/fileout/file.go index c3f5d3c5e4e..2c2f5216294 100644 --- a/libbeat/outputs/fileout/file.go +++ b/libbeat/outputs/fileout/file.go @@ -18,6 +18,7 @@ package fileout import ( + "context" "os" "path/filepath" @@ -109,9 +110,7 @@ func (out *fileOutput) Close() error { return out.rotator.Close() } -func (out *fileOutput) Publish( - batch publisher.Batch, -) error { +func (out *fileOutput) Publish(_ context.Context, batch publisher.Batch) error { defer batch.ACK() st := out.observer diff --git a/libbeat/outputs/kafka/client.go b/libbeat/outputs/kafka/client.go index 99e41bd1a8b..c785f9e729f 100644 --- a/libbeat/outputs/kafka/client.go +++ b/libbeat/outputs/kafka/client.go @@ -18,6 +18,7 @@ package kafka import ( + "context" "errors" "fmt" "strings" @@ -126,7 +127,7 @@ func (c *client) Close() error { return nil } -func (c *client) Publish(batch publisher.Batch) error { +func (c *client) Publish(_ context.Context, batch publisher.Batch) error { events := batch.Events() c.observer.NewBatch(len(events)) diff --git a/libbeat/outputs/kafka/config.go b/libbeat/outputs/kafka/config.go index d2b645f075d..3174646da4a 100644 --- a/libbeat/outputs/kafka/config.go +++ b/libbeat/outputs/kafka/config.go @@ -216,7 +216,7 @@ func newSaramaConfig(log *logp.Logger, config *kafkaConfig) (*sarama.Config, err k.Net.TLS.Config = tls.BuildModuleConfig("") } - if config.Kerberos != nil { + if config.Kerberos.IsEnabled() { cfgwarn.Beta("Kerberos authentication for Kafka is beta.") k.Net.SASL.Enable = true diff --git a/libbeat/outputs/kafka/kafka_integration_test.go b/libbeat/outputs/kafka/kafka_integration_test.go index 58d03d1c1e7..af46aa65c73 100644 --- a/libbeat/outputs/kafka/kafka_integration_test.go +++ b/libbeat/outputs/kafka/kafka_integration_test.go @@ -20,6 +20,7 @@ package kafka import ( + "context" "encoding/json" "fmt" "math/rand" @@ -220,7 +221,7 @@ func TestKafkaPublish(t *testing.T) { } wg.Add(1) - output.Publish(batch) + output.Publish(context.Background(), batch) } // wait for all published batches to be ACKed diff --git a/libbeat/outputs/logstash/async.go b/libbeat/outputs/logstash/async.go index bcbbbdbc428..f196d137b88 100644 --- a/libbeat/outputs/logstash/async.go +++ b/libbeat/outputs/logstash/async.go @@ -18,6 +18,7 @@ package logstash import ( + "context" "errors" "net" "sync" @@ -134,7 +135,7 @@ func (c *asyncClient) Close() error { return c.Client.Close() } -func (c *asyncClient) Publish(batch publisher.Batch) error { +func (c *asyncClient) Publish(_ context.Context, batch publisher.Batch) error { st := c.observer events := batch.Events() st.NewBatch(len(events)) diff --git a/libbeat/outputs/logstash/async_test.go b/libbeat/outputs/logstash/async_test.go index 5e6e416a0b4..04d97d8c40b 100644 --- a/libbeat/outputs/logstash/async_test.go +++ b/libbeat/outputs/logstash/async_test.go @@ -20,6 +20,7 @@ package logstash import ( + "context" "sync" "testing" "time" @@ -85,7 +86,7 @@ func newAsyncTestDriver(client outputs.NetworkClient) *testAsyncDriver { case driverCmdClose: driver.client.Close() case driverCmdPublish: - err := driver.client.Publish(cmd.batch) + err := driver.client.Publish(context.Background(), cmd.batch) driver.returns = append(driver.returns, testClientReturn{cmd.batch, err}) } } diff --git a/libbeat/outputs/logstash/logstash_integration_test.go b/libbeat/outputs/logstash/logstash_integration_test.go index 6e7e3693dc3..c0969358dfb 100644 --- a/libbeat/outputs/logstash/logstash_integration_test.go +++ b/libbeat/outputs/logstash/logstash_integration_test.go @@ -20,6 +20,7 @@ package logstash import ( + "context" "encoding/json" "fmt" "os" @@ -301,7 +302,7 @@ func testSendMessageViaLogstash(t *testing.T, name string, tls bool) { }, }, ) - ls.Publish(batch) + ls.Publish(context.Background(), batch) // wait for logstash event flush + elasticsearch waitUntilTrue(5*time.Second, checkIndex(ls, 1)) @@ -546,7 +547,7 @@ func checkEvent(t *testing.T, ls, es map[string]interface{}) { } func (t *testOutputer) PublishEvent(event beat.Event) { - t.Publish(outest.NewBatch(event)) + t.Publish(context.Background(), outest.NewBatch(event)) } func (t *testOutputer) BulkPublish(events []beat.Event) bool { @@ -560,7 +561,7 @@ func (t *testOutputer) BulkPublish(events []beat.Event) bool { wg.Done() } - t.Publish(batch) + t.Publish(context.Background(), batch) wg.Wait() return ok } diff --git a/libbeat/outputs/logstash/logstash_test.go b/libbeat/outputs/logstash/logstash_test.go index 06d15567ec5..7b8adeb8f43 100644 --- a/libbeat/outputs/logstash/logstash_test.go +++ b/libbeat/outputs/logstash/logstash_test.go @@ -18,6 +18,7 @@ package logstash import ( + "context" "fmt" "os" "testing" @@ -126,7 +127,7 @@ func testConnectionType( batch.OnSignal = func(_ outest.BatchSignal) { close(sig) } - err = output.Publish(batch) + err = output.Publish(context.Background(), batch) t.Log("wait signal") <-sig diff --git a/libbeat/outputs/logstash/sync.go b/libbeat/outputs/logstash/sync.go index d13740d37f8..22e133db906 100644 --- a/libbeat/outputs/logstash/sync.go +++ b/libbeat/outputs/logstash/sync.go @@ -18,6 +18,7 @@ package logstash import ( + "context" "time" "github.com/elastic/beats/v7/libbeat/beat" @@ -101,7 +102,7 @@ func (c *syncClient) reconnect() error { return c.Client.Connect() } -func (c *syncClient) Publish(batch publisher.Batch) error { +func (c *syncClient) Publish(_ context.Context, batch publisher.Batch) error { events := batch.Events() st := c.observer diff --git a/libbeat/outputs/logstash/sync_test.go b/libbeat/outputs/logstash/sync_test.go index af90cfa130d..3ba9e682232 100644 --- a/libbeat/outputs/logstash/sync_test.go +++ b/libbeat/outputs/logstash/sync_test.go @@ -20,6 +20,7 @@ package logstash import ( + "context" "sync" "testing" "time" @@ -99,7 +100,7 @@ func newClientTestDriver(client outputs.NetworkClient) *testSyncDriver { case driverCmdClose: driver.client.Close() case driverCmdPublish: - err := driver.client.Publish(cmd.batch) + err := driver.client.Publish(context.Background(), cmd.batch) driver.returns = append(driver.returns, testClientReturn{cmd.batch, err}) } } diff --git a/libbeat/outputs/outputs.go b/libbeat/outputs/outputs.go index c6808321ce7..0fdf4d9407b 100644 --- a/libbeat/outputs/outputs.go +++ b/libbeat/outputs/outputs.go @@ -21,6 +21,8 @@ package outputs import ( + "context" + "github.com/elastic/beats/v7/libbeat/publisher" ) @@ -34,7 +36,8 @@ type Client interface { // Using Retry/Cancelled a client can return a batch of unprocessed events to // the publisher pipeline. The publisher pipeline (if configured by the output // factory) will take care of retrying/dropping events. - Publish(publisher.Batch) error + // Context is intended for carrying request-scoped values, not for cancellation. + Publish(context.Context, publisher.Batch) error // String identifies the client type and endpoint. String() string diff --git a/libbeat/outputs/redis/backoff.go b/libbeat/outputs/redis/backoff.go index 30107df90fa..41f448ca318 100644 --- a/libbeat/outputs/redis/backoff.go +++ b/libbeat/outputs/redis/backoff.go @@ -18,6 +18,7 @@ package redis import ( + "context" "time" "github.com/garyburd/redigo/redis" @@ -78,8 +79,8 @@ func (b *backoffClient) Close() error { return err } -func (b *backoffClient) Publish(batch publisher.Batch) error { - err := b.client.Publish(batch) +func (b *backoffClient) Publish(ctx context.Context, batch publisher.Batch) error { + err := b.client.Publish(ctx, batch) if err != nil { b.client.Close() b.updateFailReason(err) diff --git a/libbeat/outputs/redis/client.go b/libbeat/outputs/redis/client.go index fbaa40f4d3e..70e316cba3f 100644 --- a/libbeat/outputs/redis/client.go +++ b/libbeat/outputs/redis/client.go @@ -18,6 +18,7 @@ package redis import ( + "context" "errors" "regexp" "strconv" @@ -134,7 +135,7 @@ func (c *client) Close() error { return c.Client.Close() } -func (c *client) Publish(batch publisher.Batch) error { +func (c *client) Publish(_ context.Context, batch publisher.Batch) error { if c == nil { panic("no client") } diff --git a/libbeat/outputs/redis/redis_integration_test.go b/libbeat/outputs/redis/redis_integration_test.go index 66c3375246a..25189fa9008 100644 --- a/libbeat/outputs/redis/redis_integration_test.go +++ b/libbeat/outputs/redis/redis_integration_test.go @@ -20,6 +20,7 @@ package redis import ( + "context" "encoding/json" "fmt" "os" @@ -348,7 +349,7 @@ func sendTestEvents(out outputs.Client, batches, N int) error { } batch := outest.NewBatch(events...) - err := out.Publish(batch) + err := out.Publish(context.Background(), batch) if err != nil { return err } diff --git a/libbeat/processors/actions/docs/add_fields.asciidoc b/libbeat/processors/actions/docs/add_fields.asciidoc index dcf54ef842b..b133d2115c8 100644 --- a/libbeat/processors/actions/docs/add_fields.asciidoc +++ b/libbeat/processors/actions/docs/add_fields.asciidoc @@ -21,11 +21,11 @@ For example, this configuration: [source,yaml] ------------------------------------------------------------------------------ processors: -- add_fields: - target: project - fields: - name: myproject - id: '574734885120952459' + - add_fields: + target: project + fields: + name: myproject + id: '574734885120952459' ------------------------------------------------------------------------------ Adds these fields to any event: diff --git a/libbeat/processors/actions/docs/add_labels.asciidoc b/libbeat/processors/actions/docs/add_labels.asciidoc index fca768228bf..9e87ea05088 100644 --- a/libbeat/processors/actions/docs/add_labels.asciidoc +++ b/libbeat/processors/actions/docs/add_labels.asciidoc @@ -18,16 +18,16 @@ For example, this configuration: [source,yaml] ------------------------------------------------------------------------------ processors: -- add_labels: - labels: - number: 1 - with.dots: test - nested: - with.dots: nested - array: - - do - - re - - with.field: mi + - add_labels: + labels: + number: 1 + with.dots: test + nested: + with.dots: nested + array: + - do + - re + - with.field: mi ------------------------------------------------------------------------------ Adds these fields to every event: diff --git a/libbeat/processors/actions/docs/add_tags.asciidoc b/libbeat/processors/actions/docs/add_tags.asciidoc index c6015e749f5..e338d246668 100644 --- a/libbeat/processors/actions/docs/add_tags.asciidoc +++ b/libbeat/processors/actions/docs/add_tags.asciidoc @@ -17,9 +17,9 @@ For example, this configuration: [source,yaml] ------------------------------------------------------------------------------ processors: -- add_tags: - tags: [web, production] - target: "environment" + - add_tags: + tags: [web, production] + target: "environment" ------------------------------------------------------------------------------ Adds the environment field to every event: diff --git a/libbeat/processors/actions/docs/copy_fields.asciidoc b/libbeat/processors/actions/docs/copy_fields.asciidoc index ae3816bbce9..c958d3c6e82 100644 --- a/libbeat/processors/actions/docs/copy_fields.asciidoc +++ b/libbeat/processors/actions/docs/copy_fields.asciidoc @@ -20,12 +20,12 @@ For example, this configuration: [source,yaml] ------------------------------------------------------------------------------ processors: -- copy_fields: - fields: - - from: message - to: event.original - fail_on_error: false - ignore_missing: true + - copy_fields: + fields: + - from: message + to: event.original + fail_on_error: false + ignore_missing: true ------------------------------------------------------------------------------ Copies the original `message` field to `event.original`: diff --git a/libbeat/processors/actions/docs/decode_base64_field.asciidoc b/libbeat/processors/actions/docs/decode_base64_field.asciidoc index 17912cca1b7..a0c413006ef 100644 --- a/libbeat/processors/actions/docs/decode_base64_field.asciidoc +++ b/libbeat/processors/actions/docs/decode_base64_field.asciidoc @@ -15,12 +15,12 @@ processor to drop the field and then rename the field. [source,yaml] ------- processors: -- decode_base64_field: - field: - from: "field1" - to: "field2" - ignore_missing: false - fail_on_error: true + - decode_base64_field: + field: + from: "field1" + to: "field2" + ignore_missing: false + fail_on_error: true ------- In the example above: diff --git a/libbeat/processors/actions/docs/decode_json_fields.asciidoc b/libbeat/processors/actions/docs/decode_json_fields.asciidoc index 6c57dac73ba..c5aa15c2a3c 100644 --- a/libbeat/processors/actions/docs/decode_json_fields.asciidoc +++ b/libbeat/processors/actions/docs/decode_json_fields.asciidoc @@ -11,13 +11,13 @@ replaces the strings with valid JSON objects. [source,yaml] ----------------------------------------------------- processors: - - decode_json_fields: - fields: ["field1", "field2", ...] - process_array: false - max_depth: 1 - target: "" - overwrite_keys: false - add_error_key: true + - decode_json_fields: + fields: ["field1", "field2", ...] + process_array: false + max_depth: 1 + target: "" + overwrite_keys: false + add_error_key: true ----------------------------------------------------- The `decode_json_fields` processor has the following configuration settings: diff --git a/libbeat/processors/actions/docs/decompress_gzip_field.asciidoc b/libbeat/processors/actions/docs/decompress_gzip_field.asciidoc index 44ba25f7888..102244fd29b 100644 --- a/libbeat/processors/actions/docs/decompress_gzip_field.asciidoc +++ b/libbeat/processors/actions/docs/decompress_gzip_field.asciidoc @@ -15,12 +15,12 @@ processor to drop the field and then rename the field. [source,yaml] ------- processors: -- decompress_gzip_field: - field: - from: "field1" - to: "field2" - ignore_missing: false - fail_on_error: true + - decompress_gzip_field: + field: + from: "field1" + to: "field2" + ignore_missing: false + fail_on_error: true ------- In the example above: diff --git a/libbeat/processors/actions/docs/drop_event.asciidoc b/libbeat/processors/actions/docs/drop_event.asciidoc index 01016fc6290..5dba8ec24bb 100644 --- a/libbeat/processors/actions/docs/drop_event.asciidoc +++ b/libbeat/processors/actions/docs/drop_event.asciidoc @@ -12,8 +12,8 @@ are dropped. [source,yaml] ------ processors: - - drop_event: - when: + - drop_event: + when: condition ------ diff --git a/libbeat/processors/actions/docs/drop_fields.asciidoc b/libbeat/processors/actions/docs/drop_fields.asciidoc index 865da6549fb..9e543ea376a 100644 --- a/libbeat/processors/actions/docs/drop_fields.asciidoc +++ b/libbeat/processors/actions/docs/drop_fields.asciidoc @@ -13,11 +13,11 @@ be dropped, even if they show up in the `drop_fields` list. [source,yaml] ----------------------------------------------------- processors: - - drop_fields: - when: + - drop_fields: + when: condition - fields: ["field1", "field2", ...] - ignore_missing: false + fields: ["field1", "field2", ...] + ignore_missing: false ----------------------------------------------------- See <> for a list of supported conditions. diff --git a/libbeat/processors/actions/docs/include_fields.asciidoc b/libbeat/processors/actions/docs/include_fields.asciidoc index 9fbc620dfbd..86b2c70e6ff 100644 --- a/libbeat/processors/actions/docs/include_fields.asciidoc +++ b/libbeat/processors/actions/docs/include_fields.asciidoc @@ -13,10 +13,10 @@ always exported, even if they are not defined in the `include_fields` list. [source,yaml] ------- processors: - - include_fields: - when: + - include_fields: + when: condition - fields: ["field1", "field2", ...] + fields: ["field1", "field2", ...] ------- See <> for a list of supported conditions. diff --git a/libbeat/processors/actions/docs/rename.asciidoc b/libbeat/processors/actions/docs/rename.asciidoc index 81d098c7d2b..29d1495a8d2 100644 --- a/libbeat/processors/actions/docs/rename.asciidoc +++ b/libbeat/processors/actions/docs/rename.asciidoc @@ -25,12 +25,12 @@ before assigning values. [source,yaml] ------- processors: -- rename: - fields: - - from: "a.g" - to: "e.d" - ignore_missing: false - fail_on_error: true + - rename: + fields: + - from: "a.g" + to: "e.d" + ignore_missing: false + fail_on_error: true ------- The `rename` processor has the following configuration settings: diff --git a/libbeat/processors/actions/docs/replace.asciidoc b/libbeat/processors/actions/docs/replace.asciidoc new file mode 100644 index 00000000000..b833e2e84bb --- /dev/null +++ b/libbeat/processors/actions/docs/replace.asciidoc @@ -0,0 +1,49 @@ +[[replace-fields]] +=== Replace fields from events + +++++ +replace +++++ + +The `replace` processor takes a list of fields to replace the field value +matching a pattern with replacement string. Under the `fields` key, each entry +contains a `field: field-name`, `pattern: regex-pattern` and +`replacement: replacement-string`, where: + +* `field` is the original field name +* `pattern` is regex pattern to match field's value +* `replacement` is the replacement string to use for updating the field's value + +The `replace` processor cannot be used to replace value with a completely new value. + +TIP: You can replace field value to truncate part of field value or replace +it with a new string. It can also be used for masking PII information. + +Following example will change path from /usr/bin to /usr/local/bin + +[source,yaml] +------- +processors: + - replace: + fields: + - field: "file.path" + pattern: "/usr/" + replacement: "/usr/local/" + ignore_missing: false + fail_on_error: true +------- + +The `replace` processor has following configuration settings: + +`ignore_missing`:: (Optional) If set to true, no error is logged in case a specifiedfield +is missing. Default is `false`. + +`fail_on_error`:: (Optional) If set to true, in case of an error the replacement of +field values is stopped and the original event is returned. If set to false, replacement +continues even if an error occurs during replacement. Default is `true`. + +See <> for a list of supported conditions. + +You can specify multiple `ignore_missing` processors under the `processors` +section. + diff --git a/libbeat/processors/actions/docs/truncate_fields.asciidoc b/libbeat/processors/actions/docs/truncate_fields.asciidoc index a9726818672..58ddc6ed3da 100644 --- a/libbeat/processors/actions/docs/truncate_fields.asciidoc +++ b/libbeat/processors/actions/docs/truncate_fields.asciidoc @@ -23,10 +23,10 @@ For example, this configuration truncates the field named `message` to 5 charact [source,yaml] ------------------------------------------------------------------------------ processors: -- truncate_fields: - fields: - - message - max_characters: 5 - fail_on_error: false - ignore_missing: true + - truncate_fields: + fields: + - message + max_characters: 5 + fail_on_error: false + ignore_missing: true ------------------------------------------------------------------------------ diff --git a/libbeat/processors/actions/replace.go b/libbeat/processors/actions/replace.go new file mode 100644 index 00000000000..37245817050 --- /dev/null +++ b/libbeat/processors/actions/replace.go @@ -0,0 +1,118 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package actions + +import ( + "fmt" + "regexp" + + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" + "github.com/elastic/beats/v7/libbeat/processors/checks" + jsprocessor "github.com/elastic/beats/v7/libbeat/processors/script/javascript/module/processor" +) + +type replaceString struct { + config replaceStringConfig +} + +type replaceStringConfig struct { + Fields []replaceConfig `config:"fields"` + IgnoreMissing bool `config:"ignore_missing"` + FailOnError bool `config:"fail_on_error"` +} + +type replaceConfig struct { + Field string `config:"field"` + Pattern *regexp.Regexp `config:"pattern"` + Replacement string `config:"replacement"` +} + +func init() { + processors.RegisterPlugin("replace", + checks.ConfigChecked(NewReplaceString, + checks.RequireFields("fields"))) + + jsprocessor.RegisterPlugin("Replace", NewReplaceString) +} + +// NewReplaceString returns a new replace processor. +func NewReplaceString(c *common.Config) (processors.Processor, error) { + config := replaceStringConfig{ + IgnoreMissing: false, + FailOnError: true, + } + err := c.Unpack(&config) + if err != nil { + return nil, fmt.Errorf("failed to unpack the replace configuration: %s", err) + } + + f := &replaceString{ + config: config, + } + return f, nil +} + +func (f *replaceString) Run(event *beat.Event) (*beat.Event, error) { + var backup common.MapStr + // Creates a copy of the event to revert in case of failure + if f.config.FailOnError { + backup = event.Fields.Clone() + } + + for _, field := range f.config.Fields { + err := f.replaceField(field.Field, field.Pattern, field.Replacement, event.Fields) + if err != nil { + errMsg := fmt.Errorf("Failed to replace fields in processor: %s", err) + logp.Debug("replace", errMsg.Error()) + if f.config.FailOnError { + event.Fields = backup + event.PutValue("error.message", errMsg.Error()) + return event, err + } + } + } + + return event, nil +} + +func (f *replaceString) replaceField(field string, pattern *regexp.Regexp, replacement string, fields common.MapStr) error { + currentValue, err := fields.GetValue(field) + if err != nil { + // Ignore ErrKeyNotFound errors + if f.config.IgnoreMissing && errors.Cause(err) == common.ErrKeyNotFound { + return nil + } + return fmt.Errorf("could not fetch value for key: %s, Error: %s", field, err) + } + + updatedString := pattern.ReplaceAllString(currentValue.(string), replacement) + _, err = fields.Put(field, updatedString) + if err != nil { + return fmt.Errorf("could not put value: %s: %v, %v", replacement, currentValue, err) + } + return nil +} + +func (f *replaceString) String() string { + return "replace=" + fmt.Sprintf("%+v", f.config.Fields) +} diff --git a/libbeat/processors/actions/replace_test.go b/libbeat/processors/actions/replace_test.go new file mode 100644 index 00000000000..e54d16c5012 --- /dev/null +++ b/libbeat/processors/actions/replace_test.go @@ -0,0 +1,248 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package actions + +import ( + "reflect" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" +) + +func TestReplaceRun(t *testing.T) { + var tests = []struct { + description string + Fields []replaceConfig + IgnoreMissing bool + FailOnError bool + Input common.MapStr + Output common.MapStr + error bool + }{ + { + description: "simple field replacing", + Fields: []replaceConfig{ + { + Field: "f", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + }, + }, + Input: common.MapStr{ + "f": "abc", + }, + Output: common.MapStr{ + "f": "bbc", + }, + error: false, + IgnoreMissing: false, + FailOnError: true, + }, + { + description: "Add one more hierarchy to event", + Fields: []replaceConfig{ + { + Field: "f.b", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + }, + }, + Input: common.MapStr{ + "f": common.MapStr{ + "b": "abc", + }, + }, + Output: common.MapStr{ + "f": common.MapStr{ + "b": "bbc", + }, + }, + error: false, + IgnoreMissing: false, + FailOnError: true, + }, + { + description: "replace two fields at the same time.", + Fields: []replaceConfig{ + { + Field: "f", + Pattern: regexp.MustCompile(`a.*c`), + Replacement: "cab", + }, + { + Field: "g", + Pattern: regexp.MustCompile(`ef`), + Replacement: "oor", + }, + }, + Input: common.MapStr{ + "f": "abbbc", + "g": "def", + }, + Output: common.MapStr{ + "f": "cab", + "g": "door", + }, + error: false, + IgnoreMissing: false, + FailOnError: true, + }, + { + description: "test missing fields", + Fields: []replaceConfig{ + { + Field: "f", + Pattern: regexp.MustCompile(`abc`), + Replacement: "xyz", + }, + { + Field: "g", + Pattern: regexp.MustCompile(`def`), + Replacement: "", + }, + }, + Input: common.MapStr{ + "m": "abc", + "n": "def", + }, + Output: common.MapStr{ + "m": "abc", + "n": "def", + "error": common.MapStr{ + "message": "Failed to replace fields in processor: could not fetch value for key: f, Error: key not found", + }, + }, + error: true, + IgnoreMissing: false, + FailOnError: true, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + f := &replaceString{ + config: replaceStringConfig{ + Fields: test.Fields, + IgnoreMissing: test.IgnoreMissing, + FailOnError: test.FailOnError, + }, + } + event := &beat.Event{ + Fields: test.Input, + } + + newEvent, err := f.Run(event) + if !test.error { + assert.Nil(t, err) + } else { + assert.NotNil(t, err) + } + + assert.True(t, reflect.DeepEqual(newEvent.Fields, test.Output)) + }) + } +} + +func TestReplaceField(t *testing.T) { + var tests = []struct { + Field string + Pattern *regexp.Regexp + Replacement string + ignoreMissing bool + failOnError bool + Input common.MapStr + Output common.MapStr + error bool + description string + }{ + { + description: "replace part of field value with another string", + Field: "f", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + Input: common.MapStr{ + "f": "abc", + }, + Output: common.MapStr{ + "f": "bbc", + }, + error: false, + failOnError: true, + ignoreMissing: false, + }, + { + description: "Add hierarchy to event and replace", + Field: "f.b", + Pattern: regexp.MustCompile(`a`), + Replacement: "b", + Input: common.MapStr{ + "f": common.MapStr{ + "b": "abc", + }, + }, + Output: common.MapStr{ + "f": common.MapStr{ + "b": "bbc", + }, + }, + error: false, + ignoreMissing: false, + failOnError: true, + }, + { + description: "try replacing value of missing fields in event", + Field: "f", + Pattern: regexp.MustCompile(`abc`), + Replacement: "xyz", + Input: common.MapStr{ + "m": "abc", + "n": "def", + }, + Output: common.MapStr{ + "m": "abc", + "n": "def", + }, + error: true, + ignoreMissing: false, + failOnError: true, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + f := &replaceString{ + config: replaceStringConfig{ + IgnoreMissing: test.ignoreMissing, + FailOnError: test.failOnError, + }, + } + + err := f.replaceField(test.Field, test.Pattern, test.Replacement, test.Input) + if err != nil { + assert.Equal(t, test.error, true) + } + + assert.True(t, reflect.DeepEqual(test.Input, test.Output)) + }) + } +} diff --git a/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc b/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc index d17fd326b87..9a5fcfcbf91 100644 --- a/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc +++ b/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc @@ -28,7 +28,7 @@ The simple configuration below enables the processor. [source,yaml] ------------------------------------------------------------------------------- processors: -- add_cloud_metadata: ~ + - add_cloud_metadata: ~ ------------------------------------------------------------------------------- The `add_cloud_metadata` processor has three optional configuration settings. diff --git a/libbeat/processors/add_docker_metadata/docs/add_docker_metadata.asciidoc b/libbeat/processors/add_docker_metadata/docs/add_docker_metadata.asciidoc index aa1229ff7cb..801437a4624 100644 --- a/libbeat/processors/add_docker_metadata/docs/add_docker_metadata.asciidoc +++ b/libbeat/processors/add_docker_metadata/docs/add_docker_metadata.asciidoc @@ -34,20 +34,20 @@ running as non-root inside the container. [source,yaml] ------------------------------------------------------------------------------- processors: -- add_docker_metadata: - host: "unix:///var/run/docker.sock" - #match_fields: ["system.process.cgroup.id"] - #match_pids: ["process.pid", "process.ppid"] - #match_source: true - #match_source_index: 4 - #match_short_id: true - #cleanup_timeout: 60 - #labels.dedot: false - # To connect to Docker over TLS you must specify a client and CA certificate. - #ssl: - # certificate_authority: "/etc/pki/root/ca.pem" - # certificate: "/etc/pki/client/cert.pem" - # key: "/etc/pki/client/cert.key" + - add_docker_metadata: + host: "unix:///var/run/docker.sock" + #match_fields: ["system.process.cgroup.id"] + #match_pids: ["process.pid", "process.ppid"] + #match_source: true + #match_source_index: 4 + #match_short_id: true + #cleanup_timeout: 60 + #labels.dedot: false + # To connect to Docker over TLS you must specify a client and CA certificate. + #ssl: + # certificate_authority: "/etc/pki/root/ca.pem" + # certificate: "/etc/pki/client/cert.pem" + # key: "/etc/pki/client/cert.key" ------------------------------------------------------------------------------- It has the following settings: diff --git a/libbeat/processors/add_host_metadata/docs/add_host_metadata.asciidoc b/libbeat/processors/add_host_metadata/docs/add_host_metadata.asciidoc index 4a651548ff6..0c71f10d200 100644 --- a/libbeat/processors/add_host_metadata/docs/add_host_metadata.asciidoc +++ b/libbeat/processors/add_host_metadata/docs/add_host_metadata.asciidoc @@ -8,16 +8,16 @@ [source,yaml] ------------------------------------------------------------------------------- processors: -- add_host_metadata: - cache.ttl: 5m - geo: - name: nyc-dc1-rack1 - location: 40.7128, -74.0060 - continent_name: North America - country_iso_code: US - region_name: New York - region_iso_code: NY - city_name: New York + - add_host_metadata: + cache.ttl: 5m + geo: + name: nyc-dc1-rack1 + location: 40.7128, -74.0060 + continent_name: North America + country_iso_code: US + region_name: New York + region_iso_code: NY + city_name: New York ------------------------------------------------------------------------------- It has the following settings: diff --git a/libbeat/processors/add_id/docs/add_id.asciidoc b/libbeat/processors/add_id/docs/add_id.asciidoc index a68d3e96123..1a051d6b29a 100644 --- a/libbeat/processors/add_id/docs/add_id.asciidoc +++ b/libbeat/processors/add_id/docs/add_id.asciidoc @@ -10,7 +10,7 @@ The `add_id` processor generates a unique ID for an event. [source,yaml] ----------------------------------------------------- processors: - - add_id: ~ + - add_id: ~ ----------------------------------------------------- The following settings are supported: diff --git a/libbeat/processors/add_kubernetes_metadata/docs/add_kubernetes_metadata.asciidoc b/libbeat/processors/add_kubernetes_metadata/docs/add_kubernetes_metadata.asciidoc index a46120a281c..a862ba42262 100644 --- a/libbeat/processors/add_kubernetes_metadata/docs/add_kubernetes_metadata.asciidoc +++ b/libbeat/processors/add_kubernetes_metadata/docs/add_kubernetes_metadata.asciidoc @@ -56,7 +56,7 @@ Kubernetes. [source,yaml] ------------------------------------------------------------------------------- processors: -- add_kubernetes_metadata: + - add_kubernetes_metadata: ------------------------------------------------------------------------------- The configuration below enables the processor on a Beat running as a process on @@ -65,11 +65,11 @@ the Kubernetes node. [source,yaml] ------------------------------------------------------------------------------- processors: -- add_kubernetes_metadata: - host: - # If kube_config is not set, KUBECONFIG environment variable will be checked - # and if not present it will fall back to InCluster - kube_config: ${HOME}/.kube/config + - add_kubernetes_metadata: + host: + # If kube_config is not set, KUBECONFIG environment variable will be checked + # and if not present it will fall back to InCluster + kube_config: ${HOME}/.kube/config ------------------------------------------------------------------------------- The configuration below has the default indexers and matchers disabled and @@ -78,18 +78,18 @@ enables ones that the user is interested in. [source,yaml] ------------------------------------------------------------------------------- processors: -- add_kubernetes_metadata: - host: - # If kube_config is not set, KUBECONFIG environment variable will be checked - # and if not present it will fall back to InCluster - kube_config: ~/.kube/config - default_indexers.enabled: false - default_matchers.enabled: false - indexers: - - ip_port: - matchers: - - fields: - lookup_fields: ["metricset.host"] + - add_kubernetes_metadata: + host: + # If kube_config is not set, KUBECONFIG environment variable will be checked + # and if not present it will fall back to InCluster + kube_config: ~/.kube/config + default_indexers.enabled: false + default_matchers.enabled: false + indexers: + - ip_port: + matchers: + - fields: + lookup_fields: ["metricset.host"] ------------------------------------------------------------------------------- The `add_kubernetes_metadata` processor has the following configuration settings: diff --git a/libbeat/processors/add_locale/docs/add_locale.asciidoc b/libbeat/processors/add_locale/docs/add_locale.asciidoc index 82c502cf0ac..a1a0455a69f 100644 --- a/libbeat/processors/add_locale/docs/add_locale.asciidoc +++ b/libbeat/processors/add_locale/docs/add_locale.asciidoc @@ -16,7 +16,7 @@ The configuration below enables the processor with the default settings. [source,yaml] ------------------------------------------------------------------------------- processors: -- add_locale: ~ + - add_locale: ~ ------------------------------------------------------------------------------- This configuration enables the processor and configures it to add the time zone @@ -25,8 +25,8 @@ abbreviation to events. [source,yaml] ------------------------------------------------------------------------------- processors: -- add_locale: - format: abbreviation + - add_locale: + format: abbreviation ------------------------------------------------------------------------------- NOTE: Please note that `add_locale` differentiates between daylight savings diff --git a/libbeat/processors/add_observer_metadata/docs/add_observer_metadata.asciidoc b/libbeat/processors/add_observer_metadata/docs/add_observer_metadata.asciidoc index 701e90741e0..ae98fa39dc5 100644 --- a/libbeat/processors/add_observer_metadata/docs/add_observer_metadata.asciidoc +++ b/libbeat/processors/add_observer_metadata/docs/add_observer_metadata.asciidoc @@ -10,16 +10,16 @@ beta[] [source,yaml] ------------------------------------------------------------------------------- processors: -- add_observer_metadata: - cache.ttl: 5m - geo: - name: nyc-dc1-rack1 - location: 40.7128, -74.0060 - continent_name: North America - country_iso_code: US - region_name: New York - region_iso_code: NY - city_name: New York + - add_observer_metadata: + cache.ttl: 5m + geo: + name: nyc-dc1-rack1 + location: 40.7128, -74.0060 + continent_name: North America + country_iso_code: US + region_name: New York + region_iso_code: NY + city_name: New York ------------------------------------------------------------------------------- It has the following settings: diff --git a/libbeat/processors/add_process_metadata/docs/add_process_metadata.asciidoc b/libbeat/processors/add_process_metadata/docs/add_process_metadata.asciidoc index 36acc68a407..3066107a009 100644 --- a/libbeat/processors/add_process_metadata/docs/add_process_metadata.asciidoc +++ b/libbeat/processors/add_process_metadata/docs/add_process_metadata.asciidoc @@ -11,9 +11,9 @@ processes, identified by their process ID (PID). [source,yaml] ------------------------------------------------------------------------------- processors: -- add_process_metadata: - match_pids: [system.process.ppid] - target: system.process.parent + - add_process_metadata: + match_pids: [system.process.ppid] + target: system.process.parent ------------------------------------------------------------------------------- The fields added to the event look as follows: diff --git a/libbeat/processors/decode_csv_fields/docs/decode_csv_fields.asciidoc b/libbeat/processors/decode_csv_fields/docs/decode_csv_fields.asciidoc index ead8551bca3..53cc3787543 100644 --- a/libbeat/processors/decode_csv_fields/docs/decode_csv_fields.asciidoc +++ b/libbeat/processors/decode_csv_fields/docs/decode_csv_fields.asciidoc @@ -14,14 +14,14 @@ This processor is available for Filebeat and Journalbeat. [source,yaml] ----------------------------------------------------- processors: - - decode_csv_fields: - fields: + - decode_csv_fields: + fields: message: decoded.csv - separator: "," - ignore_missing: false - overwrite_keys: true - trim_leading_space: false - fail_on_error: true + separator: "," + ignore_missing: false + overwrite_keys: true + trim_leading_space: false + fail_on_error: true ----------------------------------------------------- The `decode_csv_fields` has the following settings: diff --git a/libbeat/processors/dissect/const.go b/libbeat/processors/dissect/const.go index 610f27ec50b..aa0349cf82d 100644 --- a/libbeat/processors/dissect/const.go +++ b/libbeat/processors/dissect/const.go @@ -28,8 +28,8 @@ var ( // ` %{key}, %{key/2}` // into: // [["", "key" ], [", ", "key/2"]] - delimiterRE = regexp.MustCompile("(?s)(.*?)%\\{([^}]*?)}") - suffixRE = regexp.MustCompile("(.+?)(/(\\d{1,2}))?(->)?$") + ordinalIndicator = "/" + fixedLengthIndicator = "#" skipFieldPrefix = "?" appendFieldPrefix = "+" @@ -39,6 +39,14 @@ var ( greedySuffix = "->" pointerFieldPrefix = "*" + numberRE = "\\d{1,2}" + + delimiterRE = regexp.MustCompile("(?s)(.*?)%\\{([^}]*?)}") + suffixRE = regexp.MustCompile("(.+?)" + // group 1 for key name + "(" + ordinalIndicator + "(" + numberRE + ")" + ")?" + // group 2, 3 for ordinal + "(" + fixedLengthIndicator + "(" + numberRE + ")" + ")?" + // group 4, 5 for fixed length + "(" + greedySuffix + ")?$") // group 6 for greedy + defaultJoinString = " " errParsingFailure = errors.New("parsing failure") diff --git a/libbeat/processors/dissect/dissect.go b/libbeat/processors/dissect/dissect.go index c9093c476f8..406027adfa3 100644 --- a/libbeat/processors/dissect/dissect.go +++ b/libbeat/processors/dissect/dissect.go @@ -89,12 +89,27 @@ func (d *Dissector) extract(s string) (positions, error) { // move through all the other delimiters, until we have consumed all of them. for dl.Next() != nil { start = offset - end = dl.Next().IndexOf(s, offset) - if end == -1 { - return nil, fmt.Errorf( - "could not find delimiter: `%s` in remaining: `%s`, (offset: %d)", - dl.Delimiter(), s[offset:], offset, - ) + + // corresponding field of the delimiter + field := d.parser.fields[d.parser.fieldsIdMap[i]] + + // for fixed-length field, just step the same size of its length + if field.IsFixedLength() { + end = offset + field.Length() + if end > len(s) { + return nil, fmt.Errorf( + "field length is grater than string length: remaining: `%s`, (offset: %d), field: %s", + s[offset:], offset, field, + ) + } + } else { + end = dl.Next().IndexOf(s, offset) + if end == -1 { + return nil, fmt.Errorf( + "could not find delimiter: `%s` in remaining: `%s`, (offset: %d)", + dl.Delimiter(), s[offset:], offset, + ) + } } offset = end @@ -118,6 +133,13 @@ func (d *Dissector) extract(s string) (positions, error) { dl = dl.Next() } + field := d.parser.fields[d.parser.fieldsIdMap[i]] + + if field.IsFixedLength() && offset+field.Length() != len(s) { + return nil, fmt.Errorf("last fixed length key `%s` (length: %d) does not fit into remaining: `%s`, (offset: %d)", + field, field.Length(), s, offset, + ) + } // If we have remaining contents and have not captured all the requested fields if offset < len(s) && i < len(d.parser.fields) { positions[i] = position{start: offset, end: len(s)} diff --git a/libbeat/processors/dissect/docs/dissect.asciidoc b/libbeat/processors/dissect/docs/dissect.asciidoc index c5f1e566793..e11d8ed50b9 100644 --- a/libbeat/processors/dissect/docs/dissect.asciidoc +++ b/libbeat/processors/dissect/docs/dissect.asciidoc @@ -30,7 +30,7 @@ an error; you need to either drop or rename the key before using dissect. For tokenization to be successful, all keys must be found and extracted, if one of them cannot be found an error will be logged and no modification is done on the original event. -NOTE: A key can contain any characters except reserved suffix or prefix modifiers: `/`,`&`, `+` +NOTE: A key can contain any characters except reserved suffix or prefix modifiers: `/`,`&`, `+`, `#` and `?`. See <> for a list of supported conditions. diff --git a/libbeat/processors/dissect/field.go b/libbeat/processors/dissect/field.go index eae6ba7cdf7..bb92db0c18f 100644 --- a/libbeat/processors/dissect/field.go +++ b/libbeat/processors/dissect/field.go @@ -27,17 +27,20 @@ type field interface { MarkGreedy() IsGreedy() bool Ordinal() int + Length() int Key() string ID() int Apply(b string, m Map) String() string IsSaveable() bool + IsFixedLength() bool } type baseField struct { id int key string ordinal int + length int greedy bool } @@ -53,6 +56,10 @@ func (f baseField) Ordinal() int { return f.ordinal } +func (f baseField) Length() int { + return f.length +} + func (f baseField) Key() string { return f.key } @@ -65,6 +72,10 @@ func (f baseField) IsSaveable() bool { return true } +func (f baseField) IsFixedLength() bool { + return f.length > 0 +} + func (f baseField) String() string { return fmt.Sprintf("field: %s, ordinal: %d, greedy: %v", f.key, f.ordinal, f.IsGreedy()) } @@ -193,7 +204,7 @@ func newField(id int, rawKey string, previous delimiter) (field, error) { return newSkipField(id), nil } - key, ordinal, greedy := extractKeyParts(rawKey) + key, ordinal, length, greedy := extractKeyParts(rawKey) // Conflicting prefix used. if strings.HasPrefix(key, appendIndirectPrefix) { @@ -205,81 +216,88 @@ func newField(id int, rawKey string, previous delimiter) (field, error) { } if strings.HasPrefix(key, skipFieldPrefix) { - return newNamedSkipField(id, key[1:]), nil + return newNamedSkipField(id, key[1:], length), nil } if strings.HasPrefix(key, pointerFieldPrefix) { - return newPointerField(id, key[1:]), nil + return newPointerField(id, key[1:], length), nil } if strings.HasPrefix(key, appendFieldPrefix) { - return newAppendField(id, key[1:], ordinal, greedy, previous), nil + return newAppendField(id, key[1:], ordinal, length, greedy, previous), nil } if strings.HasPrefix(key, indirectFieldPrefix) { - return newIndirectField(id, key[1:]), nil + return newIndirectField(id, key[1:], length), nil } - - return newNormalField(id, key, ordinal, greedy), nil + return newNormalField(id, key, ordinal, length, greedy), nil } func newSkipField(id int) skipField { return skipField{baseField{id: id}} } -func newNamedSkipField(id int, key string) namedSkipField { +func newNamedSkipField(id int, key string, length int) namedSkipField { return namedSkipField{ - baseField{id: id, key: key}, + baseField{id: id, key: key, length: length}, } } -func newPointerField(id int, key string) pointerField { +func newPointerField(id int, key string, length int) pointerField { return pointerField{ - baseField{id: id, key: key}, + baseField{id: id, key: key, length: length}, } } -func newAppendField(id int, key string, ordinal int, greedy bool, previous delimiter) appendField { +func newAppendField(id int, key string, ordinal int, length int, greedy bool, previous delimiter) appendField { return appendField{ baseField: baseField{ id: id, key: key, ordinal: ordinal, + length: length, greedy: greedy, }, previous: previous, } } -func newIndirectField(id int, key string) indirectField { +func newIndirectField(id int, key string, length int) indirectField { return indirectField{ baseField{ - id: id, - key: key, + id: id, + key: key, + length: length, }, } } -func newNormalField(id int, key string, ordinal int, greedy bool) normalField { +func newNormalField(id int, key string, ordinal int, length int, greedy bool) normalField { return normalField{ baseField{ id: id, key: key, ordinal: ordinal, + length: length, greedy: greedy, }, } } -func extractKeyParts(rawKey string) (key string, ordinal int, greedy bool) { +func extractKeyParts(rawKey string) (key string, ordinal int, length int, greedy bool) { m := suffixRE.FindAllStringSubmatch(rawKey, -1) if m[0][3] != "" { ordinal, _ = strconv.Atoi(m[0][3]) } - if strings.EqualFold(greedySuffix, m[0][4]) { + if m[0][5] != "" { + length, _ = strconv.Atoi(m[0][5]) + } + + if strings.EqualFold(greedySuffix, m[0][6]) { greedy = true } - return m[0][1], ordinal, greedy + + return m[0][1], ordinal, length, greedy } diff --git a/libbeat/processors/dissect/parser.go b/libbeat/processors/dissect/parser.go index 35c4c48028c..73f42917f7f 100644 --- a/libbeat/processors/dissect/parser.go +++ b/libbeat/processors/dissect/parser.go @@ -26,6 +26,7 @@ import ( type parser struct { delimiters []delimiter fields []field + fieldsIdMap map[int]int referenceFields []field } @@ -81,6 +82,10 @@ func newParser(tokenizer string) (*parser, error) { sort.Slice(fields, func(i, j int) bool { return fields[i].Ordinal() < fields[j].Ordinal() }) + fieldsIdMap := make(map[int]int) + for i, f := range fields { + fieldsIdMap[f.ID()] = i + } // List of fields needed for indirection but don't need to appear in the final event. var referenceFields []field @@ -93,6 +98,7 @@ func newParser(tokenizer string) (*parser, error) { return &parser{ delimiters: delimiters, fields: fields, + fieldsIdMap: fieldsIdMap, referenceFields: referenceFields, }, nil } diff --git a/libbeat/processors/dissect/testdata/dissect_tests.json b/libbeat/processors/dissect/testdata/dissect_tests.json index 35b7ad61a33..6c2b642f969 100644 --- a/libbeat/processors/dissect/testdata/dissect_tests.json +++ b/libbeat/processors/dissect/testdata/dissect_tests.json @@ -230,5 +230,63 @@ }, "skip": false, "fail": false + }, + { + "name": "simple fixed length", + "tok": "%{class#1}%{month#2}%{day#2}", + "msg": "A0118", + "expected": { + "class": "A", + "month": "01", + "day": "18" + }, + "skip": false, + "fail": false + }, + { + "name": "simple ordered and fixed length field", + "tok": "%{+key/3#1}%{+key/1#1} %{+key/2}", + "msg": "12 3", + "expected": { + "key": "2 3 1" + }, + "skip": false, + "fail": false + }, + { + "name": "simple padding and fixed length field", + "tok": "%{+key/3#1}%{+key/1#1->} %{+key/2}", + "msg": "12 3", + "expected": { + "key": "2 3 1" + }, + "skip": false, + "fail": false + }, + { + "name": "mixed pointer and indirect and fixed length", + "tok": "%{*key#5}%{\u0026key#5}", + "msg": "helloworld", + "expected": { + "hello": "world" + }, + "skip": false, + "fail": false + }, + { + "name": "fails when there is remaining string after the fixed-length key", + "tok": "%{class#1}%{month#2}%{day#2}", + "msg": "A0118 ", + "expected": null, + "skip": false, + "fail": true + }, + { + "name": "fails when there is no enough string for the fixed-length key", + "tok": "%{key#10}", + "msg": "foobar", + "expected": null, + "skip": false, + "fail": true } -] +] \ No newline at end of file diff --git a/libbeat/processors/dissect/validate_test.go b/libbeat/processors/dissect/validate_test.go index 8d575a655f6..dd19b688355 100644 --- a/libbeat/processors/dissect/validate_test.go +++ b/libbeat/processors/dissect/validate_test.go @@ -32,16 +32,16 @@ func TestValidate(t *testing.T) { { name: "when we find reference field for all indirect field", p: &parser{ - fields: []field{newIndirectField(1, "hello"), newNormalField(0, "hola", 1, false)}, - referenceFields: []field{newPointerField(2, "hello")}, + fields: []field{newIndirectField(1, "hello", 0), newNormalField(0, "hola", 1, 0, false)}, + referenceFields: []field{newPointerField(2, "hello", 0)}, }, expectError: false, }, { name: "when we cannot find all the reference field for all indirect field", p: &parser{ - fields: []field{newIndirectField(1, "hello"), newNormalField(0, "hola", 1, false)}, - referenceFields: []field{newPointerField(2, "okhello")}, + fields: []field{newIndirectField(1, "hello", 0), newNormalField(0, "hola", 1, 0, false)}, + referenceFields: []field{newPointerField(2, "okhello", 0)}, }, expectError: true, }, diff --git a/libbeat/processors/dns/docs/dns.asciidoc b/libbeat/processors/dns/docs/dns.asciidoc index a6f92101033..b75fb8bf87a 100644 --- a/libbeat/processors/dns/docs/dns.asciidoc +++ b/libbeat/processors/dns/docs/dns.asciidoc @@ -30,11 +30,11 @@ in two fields. [source,yaml] ---- processors: -- dns: - type: reverse - fields: - source.ip: source.hostname - destination.ip: destination.hostname + - dns: + type: reverse + fields: + source.ip: source.hostname + destination.ip: destination.hostname ---- Next is a configuration example showing all options. diff --git a/libbeat/processors/extract_array/docs/extract_array.asciidoc b/libbeat/processors/extract_array/docs/extract_array.asciidoc index e30bf5c3d3c..0384a05dcca 100644 --- a/libbeat/processors/extract_array/docs/extract_array.asciidoc +++ b/libbeat/processors/extract_array/docs/extract_array.asciidoc @@ -15,9 +15,9 @@ the `my_array` field, `destination.ip` with the second element, and [source,yaml] ----------------------------------------------------- processors: - - extract_array: - field: my_array - mappings: + - extract_array: + field: my_array + mappings: source.ip: 0 destination.ip: 1 network.transport: 2 diff --git a/libbeat/processors/fingerprint/docs/fingerprint.asciidoc b/libbeat/processors/fingerprint/docs/fingerprint.asciidoc index 442fcfd8f7d..75509020c0a 100644 --- a/libbeat/processors/fingerprint/docs/fingerprint.asciidoc +++ b/libbeat/processors/fingerprint/docs/fingerprint.asciidoc @@ -11,8 +11,8 @@ specified subset of its fields. [source,yaml] ----------------------------------------------------- processors: - - fingerprint: - fields: ["field1", "field2", ...] + - fingerprint: + fields: ["field1", "field2", ...] ----------------------------------------------------- The following settings are supported: diff --git a/libbeat/processors/registered_domain/docs/registered_domain.asciidoc b/libbeat/processors/registered_domain/docs/registered_domain.asciidoc index 6e257954233..784811dca95 100644 --- a/libbeat/processors/registered_domain/docs/registered_domain.asciidoc +++ b/libbeat/processors/registered_domain/docs/registered_domain.asciidoc @@ -16,11 +16,11 @@ This processor uses the Mozilla Public Suffix list to determine the value. [source,yaml] ---- processors: -- registered_domain: - field: dns.question.name - target_field: dns.question.registered_domain - ignore_missing: true - ignore_failure: true + - registered_domain: + field: dns.question.name + target_field: dns.question.registered_domain + ignore_missing: true + ignore_failure: true ---- The `registered_domain` processor has the following configuration settings: diff --git a/libbeat/processors/script/docs/script.asciidoc b/libbeat/processors/script/docs/script.asciidoc index 2b89cbe3c8c..106a98a001b 100644 --- a/libbeat/processors/script/docs/script.asciidoc +++ b/libbeat/processors/script/docs/script.asciidoc @@ -16,13 +16,13 @@ file or by pointing the processor at external file(s). [source,yaml] ---- processors: -- script: - lang: javascript - id: my_filter - source: > - function process(event) { - event.Tag("js"); - } + - script: + lang: javascript + id: my_filter + source: > + function process(event) { + event.Tag("js"); + } ---- This loads `filter.js` from disk. @@ -30,10 +30,10 @@ This loads `filter.js` from disk. [source,yaml] ---- processors: -- script: - lang: javascript - id: my_filter - file: ${path.config}/filter.js + - script: + lang: javascript + id: my_filter + file: ${path.config}/filter.js ---- Parameters can be passed to the script by adding `params` to the config. @@ -43,21 +43,21 @@ code must define a `register(params)` function to receive the parameters. [source,yaml] ---- processors: -- script: - lang: javascript - id: my_filter - params: - threshold: 15 - source: > - var params = {threshold: 42}; - function register(scriptParams) { - params = scriptParams; - } - function process(event) { - if (event.Get("severity") < params.threshold) { - event.Cancel(); - } - } + - script: + lang: javascript + id: my_filter + params: + threshold: 15 + source: > + var params = {threshold: 42}; + function register(scriptParams) { + params = scriptParams; + } + function process(event) { + if (event.Get("severity") < params.threshold) { + event.Cancel(); + } + } ---- If the script defines a `test()` function it will be invoked when the processor diff --git a/libbeat/processors/timestamp/docs/timestamp.asciidoc b/libbeat/processors/timestamp/docs/timestamp.asciidoc index a8d569a9d9c..83421c584a7 100644 --- a/libbeat/processors/timestamp/docs/timestamp.asciidoc +++ b/libbeat/processors/timestamp/docs/timestamp.asciidoc @@ -41,7 +41,7 @@ If a layout does not contain a year then the current year in the specified | `field` | yes | | Source field containing the time to be parsed. | | `target_field` | no | @timestamp | Target field for the parsed time value. The target value is always written as UTC. | | `layouts` | yes | | Timestamp layouts that define the expected time value format. In addition layouts, `UNIX` and `UNIX_MS` are accepted. | -| `timezone` | no | UTC | Timezone (e.g. America/New_York) to use when parsing a timestamp not containing a timezone. | +| `timezone` | no | UTC | Time zone (e.g. America/New_York) to use when parsing a timestamp not containing a time zone. | | `ignore_missing` | no | false | Ignore errors when the source field is missing. | | `ignore_failure` | no | false | Ignore all errors produced by the processor. | | `test` | no | | A list of timestamps that must parse successfully when loading the processor. | @@ -56,14 +56,14 @@ parse with this configuration. [source,yaml] ---- processors: -- timestamp: - field: start_time - layouts: - - '2006-01-02T15:04:05Z' - - '2006-01-02T15:04:05.999Z' - test: - - '2019-06-22T16:33:51Z' - - '2019-11-18T04:59:51.123Z' -- drop_fields: - fields: [start_time] + - timestamp: + field: start_time + layouts: + - '2006-01-02T15:04:05Z' + - '2006-01-02T15:04:05.999Z' + test: + - '2019-06-22T16:33:51Z' + - '2019-11-18T04:59:51.123Z' + - drop_fields: + fields: [start_time] ---- diff --git a/libbeat/processors/translate_sid/docs/translate_sid.asciidoc b/libbeat/processors/translate_sid/docs/translate_sid.asciidoc index 68f7fd2542d..9878e0667c0 100644 --- a/libbeat/processors/translate_sid/docs/translate_sid.asciidoc +++ b/libbeat/processors/translate_sid/docs/translate_sid.asciidoc @@ -1,6 +1,10 @@ [[processor-translate-sid]] === Translate SID +++++ +translate_sid +++++ + beta[] The `translate_sid` processor translates a Windows security identifier (SID) @@ -19,12 +23,12 @@ unless `ignore_failure` is set. [source,yaml] ---- processors: -- translate_sid: - field: winlog.event_data.MemberSid - account_name_target: user.name - domain_target: user.domain - ignore_missing: true - ignore_failure: true + - translate_sid: + field: winlog.event_data.MemberSid + account_name_target: user.name + domain_target: user.domain + ignore_missing: true + ignore_failure: true ---- The `translate_sid` processor has the following configuration settings: diff --git a/libbeat/processors/urldecode/docs/urldecode.asciidoc b/libbeat/processors/urldecode/docs/urldecode.asciidoc index 6a544749d2c..427090627bf 100644 --- a/libbeat/processors/urldecode/docs/urldecode.asciidoc +++ b/libbeat/processors/urldecode/docs/urldecode.asciidoc @@ -14,12 +14,12 @@ key, each entry contains a `from: source-field` and a `to: target-field` pair, w [source,yaml] ------- processors: -- urldecode: - fields: - - from: "field1" - to: "field2" - ignore_missing: false - fail_on_error: true + - urldecode: + fields: + - from: "field1" + to: "field2" + ignore_missing: false + fail_on_error: true ------- In the example above: diff --git a/libbeat/publisher/pipeline/controller.go b/libbeat/publisher/pipeline/controller.go index 837a70eab77..f703f28f685 100644 --- a/libbeat/publisher/pipeline/controller.go +++ b/libbeat/publisher/pipeline/controller.go @@ -105,7 +105,7 @@ func (c *outputController) Set(outGrp outputs.Group) { clients := outGrp.Clients worker := make([]outputWorker, len(clients)) for i, client := range clients { - worker[i] = makeClientWorker(c.observer, c.workQueue, client) + worker[i] = makeClientWorker(c.observer, c.workQueue, client, c.monitors.Tracer) } grp := &outputGroup{ workQueue: c.workQueue, diff --git a/libbeat/publisher/pipeline/module.go b/libbeat/publisher/pipeline/module.go index 6522bb72120..591d6d9a278 100644 --- a/libbeat/publisher/pipeline/module.go +++ b/libbeat/publisher/pipeline/module.go @@ -21,6 +21,8 @@ import ( "flag" "fmt" + "go.elastic.co/apm" + "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" @@ -43,6 +45,7 @@ type Monitors struct { Metrics *monitoring.Registry Telemetry *monitoring.Registry Logger *logp.Logger + Tracer *apm.Tracer } // OutputFactory is used by the publisher pipeline to create an output instance. diff --git a/libbeat/publisher/pipeline/nilpipeline.go b/libbeat/publisher/pipeline/nilpipeline.go index f32785a8d22..cf1b276db91 100644 --- a/libbeat/publisher/pipeline/nilpipeline.go +++ b/libbeat/publisher/pipeline/nilpipeline.go @@ -17,7 +17,9 @@ package pipeline -import "github.com/elastic/beats/v7/libbeat/beat" +import ( + "github.com/elastic/beats/v7/libbeat/beat" +) type nilPipeline struct{} diff --git a/libbeat/publisher/pipeline/output.go b/libbeat/publisher/pipeline/output.go index fa2ce73a28c..ffc5acfa6ad 100644 --- a/libbeat/publisher/pipeline/output.go +++ b/libbeat/publisher/pipeline/output.go @@ -18,6 +18,13 @@ package pipeline import ( + "context" + "fmt" + + "github.com/elastic/beats/v7/libbeat/publisher" + + "go.elastic.co/apm" + "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/libbeat/outputs" ) @@ -43,9 +50,11 @@ type netClientWorker struct { batchSize int batchSizer func() int logger *logp.Logger + + tracer *apm.Tracer } -func makeClientWorker(observer outputObserver, qu workQueue, client outputs.Client) outputWorker { +func makeClientWorker(observer outputObserver, qu workQueue, client outputs.Client, tracer *apm.Tracer) outputWorker { w := worker{ observer: observer, qu: qu, @@ -62,6 +71,7 @@ func makeClientWorker(observer outputObserver, qu workQueue, client outputs.Clie worker: w, client: nc, logger: logp.NewLogger("publisher_pipeline_output"), + tracer: tracer, } } else { c = &clientWorker{worker: w, client: client} @@ -94,8 +104,7 @@ func (w *clientWorker) run() { continue } w.observer.outBatchSend(len(batch.Events())) - - if err := w.client.Publish(batch); err != nil { + if err := w.client.Publish(context.TODO(), batch); err != nil { return } } @@ -150,11 +159,28 @@ func (w *netClientWorker) run() { continue } - if err := w.client.Publish(batch); err != nil { - w.logger.Errorf("Failed to publish events: %v", err) - // on error return to connect loop + if err := w.publishBatch(batch); err != nil { connected = false } } } } + +func (w *netClientWorker) publishBatch(batch publisher.Batch) error { + ctx := context.Background() + if w.tracer != nil { + tx := w.tracer.StartTransaction("publish", "output") + defer tx.End() + tx.Context.SetLabel("worker", "netclient") + ctx = apm.ContextWithTransaction(ctx, tx) + } + err := w.client.Publish(ctx, batch) + if err != nil { + err = fmt.Errorf("failed to publish events: %w", err) + apm.CaptureError(ctx, err).Send() + w.logger.Error(err) + // on error return to connect loop + return err + } + return nil +} diff --git a/libbeat/publisher/pipeline/output_test.go b/libbeat/publisher/pipeline/output_test.go index 5f471ddf396..36c138a6d01 100644 --- a/libbeat/publisher/pipeline/output_test.go +++ b/libbeat/publisher/pipeline/output_test.go @@ -24,6 +24,8 @@ import ( "testing/quick" "time" + "go.elastic.co/apm/apmtest" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/common/atomic" @@ -58,7 +60,7 @@ func TestMakeClientWorker(t *testing.T) { client := ctor(publishFn) - worker := makeClientWorker(nilObserver, wqu, client) + worker := makeClientWorker(nilObserver, wqu, client, nil) defer worker.Close() for i := uint(0); i < numBatches; i++ { @@ -137,7 +139,7 @@ func TestReplaceClientWorker(t *testing.T) { } client := ctor(blockingPublishFn) - worker := makeClientWorker(nilObserver, wqu, client) + worker := makeClientWorker(nilObserver, wqu, client, nil) // Allow the worker to make *some* progress before we close it timeout := 10 * time.Second @@ -162,7 +164,7 @@ func TestReplaceClientWorker(t *testing.T) { } client = ctor(countingPublishFn) - makeClientWorker(nilObserver, wqu, client) + makeClientWorker(nilObserver, wqu, client, nil) wg.Wait() // Make sure that all events have eventually been published @@ -178,3 +180,52 @@ func TestReplaceClientWorker(t *testing.T) { }) } } + +func TestMakeClientTracer(t *testing.T) { + seedPRNG(t) + + numBatches := 10 + numEvents := atomic.MakeUint(0) + + wqu := makeWorkQueue() + retryer := newRetryer(logp.NewLogger("test"), nilObserver, wqu, nil) + defer retryer.close() + + var published atomic.Uint + publishFn := func(batch publisher.Batch) error { + published.Add(uint(len(batch.Events()))) + return nil + } + + client := newMockNetworkClient(publishFn) + + recorder := apmtest.NewRecordingTracer() + defer recorder.Close() + + worker := makeClientWorker(nilObserver, wqu, client, recorder.Tracer) + defer worker.Close() + + for i := 0; i < numBatches; i++ { + batch := randomBatch(10, 15).withRetryer(retryer) + numEvents.Add(uint(len(batch.Events()))) + wqu <- batch + } + + // Give some time for events to be published + timeout := 10 * time.Second + + // Make sure that all events have eventually been published + matches := waitUntilTrue(timeout, func() bool { + return numEvents == published + }) + if !matches { + t.Errorf("expected %d events, got %d", numEvents, published) + } + recorder.Flush(nil) + + apmEvents := recorder.Payloads() + transactions := apmEvents.Transactions + if len(transactions) != numBatches { + t.Errorf("expected %d traces, got %d", numBatches, len(transactions)) + } +} diff --git a/libbeat/publisher/pipeline/stress/out.go b/libbeat/publisher/pipeline/stress/out.go index 692d62f98ab..00afb1ac74e 100644 --- a/libbeat/publisher/pipeline/stress/out.go +++ b/libbeat/publisher/pipeline/stress/out.go @@ -18,6 +18,7 @@ package stress import ( + "context" "math/rand" "time" @@ -70,7 +71,7 @@ func makeTestOutput(_ outputs.IndexManager, beat beat.Info, observer outputs.Obs func (*testOutput) Close() error { return nil } -func (t *testOutput) Publish(batch publisher.Batch) error { +func (t *testOutput) Publish(_ context.Context, batch publisher.Batch) error { config := &t.config n := len(batch.Events()) diff --git a/libbeat/publisher/pipeline/testing.go b/libbeat/publisher/pipeline/testing.go index 1d5c2b908ff..5534c0ce3b4 100644 --- a/libbeat/publisher/pipeline/testing.go +++ b/libbeat/publisher/pipeline/testing.go @@ -18,6 +18,7 @@ package pipeline import ( + "context" "flag" "math/rand" "sync" @@ -45,7 +46,7 @@ type mockClient struct { func (c *mockClient) String() string { return "mock_client" } func (c *mockClient) Close() error { return nil } -func (c *mockClient) Publish(batch publisher.Batch) error { +func (c *mockClient) Publish(_ context.Context, batch publisher.Batch) error { return c.publishFn(batch) } @@ -96,11 +97,14 @@ func (b *mockBatch) Events() []publisher.Event { return b.events } -func (b *mockBatch) ACK() { signalFn(b.onACK) } -func (b *mockBatch) Drop() { signalFn(b.onDrop) } -func (b *mockBatch) Retry() { signalFn(b.onRetry) } -func (b *mockBatch) Cancelled() { signalFn(b.onCancelled) } -func (b *mockBatch) RetryEvents(events []publisher.Event) { b.updateEvents(events); signalFn(b.onRetry) } +func (b *mockBatch) ACK() { signalFn(b.onACK) } +func (b *mockBatch) Drop() { signalFn(b.onDrop) } +func (b *mockBatch) Retry() { signalFn(b.onRetry) } +func (b *mockBatch) Cancelled() { signalFn(b.onCancelled) } +func (b *mockBatch) RetryEvents(events []publisher.Event) { + b.updateEvents(events) + signalFn(b.onRetry) +} func (b *mockBatch) reduceTTL() bool { if b.onReduceTTL != nil { diff --git a/libbeat/publisher/pipetool/pipetool.go b/libbeat/publisher/pipetool/pipetool.go new file mode 100644 index 00000000000..a05ec6e6022 --- /dev/null +++ b/libbeat/publisher/pipetool/pipetool.go @@ -0,0 +1,91 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package pipetool + +import "github.com/elastic/beats/v7/libbeat/beat" + +// connectEditPipeline modifies the client configuration using edit before calling +// edit. +type connectEditPipeline struct { + parent beat.PipelineConnector + edit ConfigEditor +} + +// ConfigEditor modifies the client configuration before connecting to a Pipeline. +type ConfigEditor func(beat.ClientConfig) (beat.ClientConfig, error) + +func (p *connectEditPipeline) Connect() (beat.Client, error) { + return p.ConnectWith(beat.ClientConfig{}) +} + +func (p *connectEditPipeline) ConnectWith(cfg beat.ClientConfig) (beat.Client, error) { + cfg, err := p.edit(cfg) + if err != nil { + return nil, err + } + return p.parent.ConnectWith(cfg) +} + +// wrapClientPipeline applies edit to the beat.Client returned by Connect and ConnectWith. +// The edit function can wrap the client to add additional functionality to clients +// that connect to the pipeline. +type wrapClientPipeline struct { + parent beat.PipelineConnector + wrapper ClientWrapper +} + +// ClientWrapper allows client instances to be wrapped. +type ClientWrapper func(beat.Client) beat.Client + +func (p *wrapClientPipeline) Connect() (beat.Client, error) { + return p.ConnectWith(beat.ClientConfig{}) +} + +func (p *wrapClientPipeline) ConnectWith(cfg beat.ClientConfig) (beat.Client, error) { + client, err := p.parent.ConnectWith(cfg) + if err == nil { + client = p.wrapper(client) + } + return client, err +} + +// WithClientConfigEdit creates a pipeline connector, that allows the +// beat.ClientConfig to be modified before connecting to the underlying +// pipeline. +// The edit function is applied before calling Connect or ConnectWith. +func WithClientConfigEdit(pipeline beat.PipelineConnector, edit ConfigEditor) beat.PipelineConnector { + return &connectEditPipeline{parent: pipeline, edit: edit} +} + +// WithDefaultGuarantee sets the default sending guarantee to `mode` if the +// beat.ClientConfig does not set the mode explicitly. +func WithDefaultGuarantees(pipeline beat.PipelineConnector, mode beat.PublishMode) beat.PipelineConnector { + return WithClientConfigEdit(pipeline, func(cfg beat.ClientConfig) (beat.ClientConfig, error) { + if cfg.PublishMode == beat.DefaultGuarantees { + cfg.PublishMode = mode + } + return cfg, nil + }) +} + +// WithClientWrapper calls wrap on beat.Client instance, after a successful +// call to `pipeline.Connect` or `pipeline.ConnectWith`. The wrap function can +// wrap the client to provide additional functionality. +func WithClientWrapper(pipeline beat.PipelineConnector, wrap ClientWrapper) beat.PipelineConnector { + return &wrapClientPipeline{parent: pipeline, wrapper: wrap} +} diff --git a/libbeat/publisher/processing/default.go b/libbeat/publisher/processing/default.go index dea3fe88fd8..cf99e03d4d3 100644 --- a/libbeat/publisher/processing/default.go +++ b/libbeat/publisher/processing/default.go @@ -76,14 +76,14 @@ type builtinModifier func(beat.Info) common.MapStr // MakeDefaultBeatSupport automatically adds the `ecs.version`, `host.name` and `agent.X` fields // to each event. func MakeDefaultBeatSupport(normalize bool) SupportFactory { - return MakeDefaultSupport(normalize, WithECS, WithHost, WithBeatMeta("agent")) + return MakeDefaultSupport(normalize, WithECS, WithHost, WithAgentMeta()) } // MakeDefaultObserverSupport creates a new SupportFactory based on NewDefaultSupport. // MakeDefaultObserverSupport automatically adds the `ecs.version` and `observer.X` fields // to each event. func MakeDefaultObserverSupport(normalize bool) SupportFactory { - return MakeDefaultSupport(normalize, WithECS, WithBeatMeta("observer")) + return MakeDefaultSupport(normalize, WithECS, WithObserverMeta()) } // MakeDefaultSupport creates a new SupportFactory for use with the publisher pipeline. @@ -139,21 +139,42 @@ var WithHost modifier = builtinModifier(func(info beat.Info) common.MapStr { } }) -// WithBeatMeta adds beat meta information as builtin fields to a processing pipeline. -// The `key` parameter defines the field to be used. -func WithBeatMeta(key string) modifier { +// WithAgentMeta adds agent meta information as builtin fields to a processing +// pipeline. +func WithAgentMeta() modifier { return builtinModifier(func(info beat.Info) common.MapStr { metadata := common.MapStr{ - "type": info.Beat, "ephemeral_id": info.EphemeralID.String(), - "hostname": info.Hostname, "id": info.ID.String(), + "name": info.Hostname, + "type": info.Beat, + "version": info.Version, + // hostname is deprecated. To be removed for 8.0. It's not in ECS. + // See https://github.com/elastic/beats/issues/16377. + "hostname": info.Hostname, + } + if info.Name != "" { + metadata["name"] = info.Name + } + return common.MapStr{"agent": metadata} + }) +} + +// WithObserverMeta adds beat meta information as builtin fields to a processing +// pipeline. +func WithObserverMeta() modifier { + return builtinModifier(func(info beat.Info) common.MapStr { + metadata := common.MapStr{ + "type": info.Beat, // Per ECS this is not a valid type value. + "ephemeral_id": info.EphemeralID.String(), // Not in ECS. + "hostname": info.Hostname, + "id": info.ID.String(), // Not in ECS. "version": info.Version, } if info.Name != info.Hostname { metadata.Put("name", info.Name) } - return common.MapStr{key: metadata} + return common.MapStr{"observer": metadata} }) } @@ -248,6 +269,12 @@ func (b *builder) Create(cfg beat.ProcessingConfig, drop bool) (beat.Processor, needsCopy := b.alwaysCopy || localProcessors != nil || b.processors != nil builtin := b.builtinMeta + if cfg.DisableHost { + tmp := builtin.Clone() + tmp.Delete("host") + builtin = tmp + } + var clientFields common.MapStr for _, mod := range b.modifiers { m := mod.ClientFields(b.info, cfg) diff --git a/libbeat/publisher/processing/default_test.go b/libbeat/publisher/processing/default_test.go index ba6a6042993..967119fcd39 100644 --- a/libbeat/publisher/processing/default_test.go +++ b/libbeat/publisher/processing/default_test.go @@ -184,6 +184,7 @@ func TestProcessorsConfigs(t *testing.T) { "agent": common.MapStr{ "ephemeral_id": "123e4567-e89b-12d3-a456-426655440000", "hostname": "test.host.name", + "name": "test.host.name", "id": "123e4567-e89b-12d3-a456-426655440001", "type": "test", "version": "0.1", diff --git a/libbeat/scripts/Makefile b/libbeat/scripts/Makefile index 97df237f194..f634e9fd40f 100755 --- a/libbeat/scripts/Makefile +++ b/libbeat/scripts/Makefile @@ -128,7 +128,7 @@ endif # # Includes # -include $(ES_BEATS)/dev-tools/make/mage.mk +include $(ES_BEATS)/dev-tools/make/mage-install.mk ### BUILDING ### @@ -204,7 +204,8 @@ prepare-tests: .PHONY: unit-tests unit-tests: ## @testing Runs the unit tests with coverage. Race is not enabled for unit tests because tests run much slower. unit-tests: prepare-tests - $(COVERAGE_TOOL) $(RACE) -coverprofile=${COVERAGE_DIR}/unit.cov ${GOPACKAGES} + GOFLAGS="${INSTALL_FLAG}" \ + $(COVERAGE_TOOL) $(RACE) -coverprofile=${COVERAGE_DIR}/unit.cov ${GOPACKAGES} .PHONY: unit unit: ## @testing Runs the unit tests without coverage reports. @@ -214,7 +215,8 @@ unit: ## @testing Runs the unit tests without coverage reports. integration-tests: ## @testing Run integration tests. Unit tests are run as part of the integration tests. integration-tests: prepare-tests mage rm -f docker-compose.yml.lock - $(COVERAGE_TOOL) -tags=integration $(RACE) -coverprofile=${COVERAGE_DIR}/integration.cov ${GOPACKAGES} + GOFLAGS="${INSTALL_FLAG}" \ + $(COVERAGE_TOOL) -tags=integration $(RACE) -coverprofile=${COVERAGE_DIR}/integration.cov ${GOPACKAGES} .PHONY: integration-tests-environment integration-tests-environment: ## @testing Runs the integration inside a virtual environment. This can be run on any docker-machine (local, remote) @@ -228,7 +230,7 @@ integration-tests-environment: prepare-tests build-image -e RACE_DETECTOR=$(RACE_DETECTOR) \ -e DOCKER_COMPOSE_PROJECT_NAME=${DOCKER_COMPOSE_PROJECT_NAME} \ -e TEST_ENVIRONMENT=${TEST_ENVIRONMENT} \ - -e BEATS_DOCKER_INTEGRATION_TEST_ENV=${BEATS_DOCKER_INTEGRATION_TEST_ENV} \ + -e BEATS_INSIDE_INTEGRATION_TEST_ENV=${BEATS_INSIDE_INTEGRATION_TEST_ENV} \ -e GOFLAGS=${INSTALL_FLAG} \ beat make integration-tests @@ -359,7 +361,7 @@ update: python-env fields collect config ## @build Update expects the most recen ifneq ($(shell [[ $(BEAT_NAME) == libbeat || $(BEAT_NAME) == metricbeat ]] && echo true ),true) mkdir -p include - go run ${ES_BEATS}/dev-tools/cmd/asset/asset.go -license $(LICENSE) -pkg include -in fields.yml -out include/fields.go $(BEAT_NAME) + go run ${INSTALL_FLAG} ${ES_BEATS}/dev-tools/cmd/asset/asset.go -license $(LICENSE) -pkg include -in fields.yml -out include/fields.go $(BEAT_NAME) endif ifneq ($(shell [[ $(BEAT_NAME) == libbeat || $(BEAT_NAME) == metricbeat ]] && echo true ),true) diff --git a/libbeat/scripts/generate_fields_docs.py b/libbeat/scripts/generate_fields_docs.py index b52285f49e0..ecedb17d7b6 100644 --- a/libbeat/scripts/generate_fields_docs.py +++ b/libbeat/scripts/generate_fields_docs.py @@ -6,6 +6,8 @@ def document_fields(output, section, sections, path): + if "skipdocs" in section: + return if "anchor" in section: output.write("[[exported-fields-{}]]\n".format(section["anchor"])) @@ -136,7 +138,8 @@ def fields_to_asciidoc(input, output, beat): if "anchor" not in section: section["anchor"] = section["key"] - output.write("* <>\n".format(section["anchor"])) + if "skipdocs" not in section: + output.write("* <>\n".format(section["anchor"])) output.write("\n--\n") # Sort alphabetically by key diff --git a/libbeat/tests/system/beat/beat.py b/libbeat/tests/system/beat/beat.py index fa8e0ad3cb4..9d0bf471635 100644 --- a/libbeat/tests/system/beat/beat.py +++ b/libbeat/tests/system/beat/beat.py @@ -586,7 +586,7 @@ def extract_fields(doc_list, name): # TODO: Make fields_doc path more generic to work with beat-generator. If it can't find file # "fields.yml" you should run "make update" on metricbeat folder - with open(fields_doc, "r") as f: + with open(fields_doc, "r", encoding="utf_8") as f: path = os.path.abspath(os.path.dirname(__file__) + "../../../../fields.yml") if not os.path.isfile(path): path = os.path.abspath(os.path.dirname(__file__) + "../../../../_meta/fields.common.yml") diff --git a/libbeat/tests/system/requirements.txt b/libbeat/tests/system/requirements.txt index f979754ed33..d2aa5c3889b 100644 --- a/libbeat/tests/system/requirements.txt +++ b/libbeat/tests/system/requirements.txt @@ -31,5 +31,6 @@ parameterized==0.7.0 jsondiff==1.1.2 semver==2.8.1 stomp.py==4.1.22 +ordered-set==3.1.1 deepdiff==4.2.0 kafka-python==1.4.3 diff --git a/metricbeat/Makefile b/metricbeat/Makefile index c558416eb44..7a05a566775 100644 --- a/metricbeat/Makefile +++ b/metricbeat/Makefile @@ -1,83 +1,8 @@ -# Name can be overwritten, as Metricbeat is also a library -BEAT_NAME?=metricbeat -BEAT_TITLE?=Metricbeat -SYSTEM_TESTS?=true -TEST_ENVIRONMENT?=true -BEATS_DOCKER_INTEGRATION_TEST_ENV?=true -ES_BEATS?=.. +ES_BEATS ?= .. -# Metricbeat can only be cross-compiled on platforms not requiring CGO. -GOX_OS=netbsd linux windows -GOX_FLAGS=-arch="amd64 386 arm ppc64 ppc64le" - -DOCS_BRANCH=$(shell grep doc-branch ../libbeat/docs/version.asciidoc | cut -c 14-) - -include ${ES_BEATS}/libbeat/scripts/Makefile - -# Collects all module dashboards -.PHONY: kibana -kibana: mage - @mage dashboards - -# Collects all module docs -.PHONY: collect-docs -collect-docs: - mage CollectAll - -# Collects all module configs -.PHONY: configs -configs: python-env - @mkdir -p _meta - @cp ${ES_BEATS}/metricbeat/_meta/common.yml _meta/beat.yml - @cat ${ES_BEATS}/metricbeat/_meta/setup.yml >> _meta/beat.yml - @cat ${ES_BEATS}/metricbeat/_meta/common.reference.yml > _meta/beat.reference.yml - @${PYTHON_ENV_EXE} ${ES_BEATS}/script/config_collector.py --beat ${BEAT_NAME} --full $(PWD) >> _meta/beat.reference.yml - @rm -rf modules.d - mage config - @chmod go-w modules.d/* - @# Enable system by default: - @if [ -f modules.d/system.yml.disabled ]; then mv modules.d/system.yml.disabled modules.d/system.yml; fi - -# Generates imports for all modules and metricsets -.PHONY: imports -imports: - @mkdir -p include - mage imports - -# Runs all collection steps and updates afterwards -.PHONY: collect -collect: assets collect-docs configs kibana imports +include $(ES_BEATS)/dev-tools/make/mage.mk # Creates a new metricset. Requires the params MODULE and METRICSET .PHONY: create-metricset -create-metricset: python-env - @${PYTHON_ENV_EXE} ${ES_BEATS}/metricbeat/scripts/create_metricset.py --path=$(PWD) --es_beats=$(ES_BEATS) --module=$(MODULE) --metricset=$(METRICSET) - -# Generates the data.json example documents -.PHONY: generate-json -generate-json: build-image - ${DOCKER_COMPOSE} run beat go test -tags=integration github.com/elastic/beats/metricbeat/module/... -data - -.PHONY: run-module -run-module: ## @testing Runs the given module with exposing the port. Needs $MODULE and $PORT as param -run-module: - ${DOCKER_COMPOSE} build ${MODULE} - ${DOCKER_COMPOSE} run -p ${PORT}:${PORT} ${MODULE} - -.PHONY: test-module -test-module: ## @testing Tests the given module. Needs $MODULE as param an run-module must be started first. -test-module: python-env update metricbeat.test - go test -tags=integration ${BEAT_PATH}/module/${MODULE}/... -v - . ${PYTHON_ENV}/bin/activate && INTEGRATION_TESTS=1 nosetests module/${MODULE} - -.PHONY: assets -assets: - go run ${INSTALL_FLAG} ${ES_BEATS}/metricbeat/scripts/assets/assets.go ${ES_BEATS}/metricbeat/module - mkdir -p include/fields - go run ${INSTALL_FLAG} ${ES_BEATS}/libbeat/scripts/cmd/global_fields/main.go -es_beats_path ${ES_BEATS} -beat_path ${PWD} | go run ${ES_BEATS}/dev-tools/cmd/asset/asset.go -license ${LICENSE} -out ./include/fields/fields.go -pkg include -priority asset.LibbeatFieldsPri ${ES_BEATS}/libbeat/fields.yml $(BEAT_NAME) - -.PHONY: integration-tests -integration-tests: ## @testing Run golang integration tests. -integration-tests: prepare-tests mage - rm -f docker-compose.yml.lock - mage goIntegTest +create-metricset: + mage createMetricset diff --git a/metricbeat/_meta/beat.docker.yml b/metricbeat/_meta/config/beat.docker.yml.tmpl similarity index 98% rename from metricbeat/_meta/beat.docker.yml rename to metricbeat/_meta/config/beat.docker.yml.tmpl index 16b19a866dd..10c25e6a5cc 100644 --- a/metricbeat/_meta/beat.docker.yml +++ b/metricbeat/_meta/config/beat.docker.yml.tmpl @@ -1,4 +1,3 @@ metricbeat.config.modules: path: ${path.config}/modules.d/*.yml reload.enabled: false - diff --git a/metricbeat/_meta/config/beat.reference.yml.tmpl b/metricbeat/_meta/config/beat.reference.yml.tmpl new file mode 100644 index 00000000000..7d00457d5c6 --- /dev/null +++ b/metricbeat/_meta/config/beat.reference.yml.tmpl @@ -0,0 +1,2 @@ +{{template "header.reference.yml.tmpl" .}} +{{template "config.modules.yml.tmpl" .}} diff --git a/metricbeat/_meta/config/beat.yml.tmpl b/metricbeat/_meta/config/beat.yml.tmpl new file mode 100644 index 00000000000..f34d7c55515 --- /dev/null +++ b/metricbeat/_meta/config/beat.yml.tmpl @@ -0,0 +1,3 @@ +{{template "header.yml.tmpl" .}} +{{template "metricbeat.config.modules.yml.tmpl" .}} +{{template "setup.template.yml.tmpl" .}} diff --git a/metricbeat/_meta/common.reference.yml b/metricbeat/_meta/config/header.reference.yml.tmpl similarity index 100% rename from metricbeat/_meta/common.reference.yml rename to metricbeat/_meta/config/header.reference.yml.tmpl diff --git a/metricbeat/_meta/common.yml b/metricbeat/_meta/config/header.yml.tmpl similarity index 100% rename from metricbeat/_meta/common.yml rename to metricbeat/_meta/config/header.yml.tmpl diff --git a/metricbeat/_meta/config/metricbeat.config.modules.yml.tmpl b/metricbeat/_meta/config/metricbeat.config.modules.yml.tmpl new file mode 100644 index 00000000000..e616bc21b63 --- /dev/null +++ b/metricbeat/_meta/config/metricbeat.config.modules.yml.tmpl @@ -0,0 +1,11 @@ +{{header "Modules configuration"}} + +metricbeat.config.modules: + # Glob pattern for configuration loading + path: ${path.config}/modules.d/*.yml + + # Set to true to enable config reloading + reload.enabled: false + + # Period on which files under path should be checked for changes + #reload.period: 10s diff --git a/metricbeat/_meta/config/setup.template.yml.tmpl b/metricbeat/_meta/config/setup.template.yml.tmpl new file mode 100644 index 00000000000..2b8d28f2a30 --- /dev/null +++ b/metricbeat/_meta/config/setup.template.yml.tmpl @@ -0,0 +1,6 @@ +{{header "Elasticsearch template setting"}} + +setup.template.settings: + index.number_of_shards: 1 + index.codec: best_compression + #_source.enabled: false diff --git a/metricbeat/_meta/setup.yml b/metricbeat/_meta/setup.yml deleted file mode 100644 index 337067373f1..00000000000 --- a/metricbeat/_meta/setup.yml +++ /dev/null @@ -1,19 +0,0 @@ - -#========================== Modules configuration ============================ - -metricbeat.config.modules: - # Glob pattern for configuration loading - path: ${path.config}/modules.d/*.yml - - # Set to true to enable config reloading - reload.enabled: false - - # Period on which files under path should be checked for changes - #reload.period: 10s - -#==================== Elasticsearch template setting ========================== - -setup.template.settings: - index.number_of_shards: 1 - index.codec: best_compression - #_source.enabled: false diff --git a/metricbeat/autodiscover/builder/hints/metrics.go b/metricbeat/autodiscover/builder/hints/metrics.go index 3e859b23d11..1647fb9fbc7 100644 --- a/metricbeat/autodiscover/builder/hints/metrics.go +++ b/metricbeat/autodiscover/builder/hints/metrics.go @@ -84,6 +84,7 @@ func (m *metricHints) CreateConfig(event bus.Event) []*common.Config { } modulesConfig := m.getModules(hints) + // here we handle raw configs if provided if modulesConfig != nil { configs := []*common.Config{} for _, cfg := range modulesConfig { @@ -93,7 +94,7 @@ func (m *metricHints) CreateConfig(event bus.Event) []*common.Config { } logp.Debug("hints.builder", "generated config %+v", configs) // Apply information in event to the template to generate the final config - return template.ApplyConfigTemplate(event, configs) + return template.ApplyConfigTemplate(event, configs, false) } @@ -154,7 +155,7 @@ func (m *metricHints) CreateConfig(event bus.Event) []*common.Config { // Apply information in event to the template to generate the final config // This especially helps in a scenario where endpoints are configured as: // co.elastic.metrics/hosts= "${data.host}:9090" - return template.ApplyConfigTemplate(event, config) + return template.ApplyConfigTemplate(event, config, false) } func (m *metricHints) getModule(hints common.MapStr) string { diff --git a/metricbeat/autodiscover/builder/hints/metrics_test.go b/metricbeat/autodiscover/builder/hints/metrics_test.go index f3f9d5a5200..4b3f7e0430b 100644 --- a/metricbeat/autodiscover/builder/hints/metrics_test.go +++ b/metricbeat/autodiscover/builder/hints/metrics_test.go @@ -18,13 +18,17 @@ package hints import ( + "os" + "path/filepath" "sort" "testing" + "github.com/docker/docker/pkg/ioutils" "github.com/stretchr/testify/assert" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/metricbeat/mb" ) @@ -324,6 +328,78 @@ func TestGenerateHints(t *testing.T) { } } +func TestGenerateHintsDoesNotAccessKeystore(t *testing.T) { + path := getTemporaryKeystoreFile() + defer os.Remove(path) + // store the secret + keystore := createAnExistingKeystore(path, "stored_secret") + os.Setenv("PASSWORD", "env_secret") + + tests := []struct { + message string + event bus.Event + len int + result common.MapStr + }{ + { + message: "Module, namespace, host hint should return valid config", + event: bus.Event{ + "host": "1.2.3.4", + "port": 9090, + "hints": common.MapStr{ + "metrics": common.MapStr{ + "module": "mockmoduledefaults", + "hosts": "${data.host}:9090", + "password": "${PASSWORD}", + }, + }, + "keystore": keystore, + }, + len: 1, + result: common.MapStr{ + "module": "mockmoduledefaults", + "metricsets": []string{"default"}, + "hosts": []interface{}{"1.2.3.4:9090"}, + "timeout": "3s", + "period": "1m", + "enabled": true, + "password": "env_secret", + }, + }, + } + for _, test := range tests { + mockRegister := mb.NewRegister() + mockRegister.MustAddMetricSet("mockmoduledefaults", "default", NewMockMetricSet, mb.DefaultMetricSet()) + + m := metricHints{ + Key: defaultConfig().Key, + Registry: mockRegister, + } + cfgs := m.CreateConfig(test.event) + assert.Equal(t, len(cfgs), test.len) + if len(cfgs) != 0 { + config := common.MapStr{} + err := cfgs[0].Unpack(&config) + assert.Nil(t, err, test.message) + + // metricsets order is random, order it for tests + if v, err := config.GetValue("metricsets"); err == nil { + if msets, ok := v.([]interface{}); ok { + metricsets := make([]string, len(msets)) + for i, v := range msets { + metricsets[i] = v.(string) + } + sort.Strings(metricsets) + config["metricsets"] = metricsets + } + } + + assert.Equal(t, test.result, config, test.message) + } + + } +} + type MockMetricSet struct { mb.BaseMetricSet } @@ -335,3 +411,31 @@ func NewMockMetricSet(base mb.BaseMetricSet) (mb.MetricSet, error) { func (ms *MockMetricSet) Fetch(report mb.Reporter) { } + +// create a keystore with an existing key +/// `PASSWORD` with the value of `secret` variable. +func createAnExistingKeystore(path string, secret string) keystore.Keystore { + keyStore, err := keystore.NewFileKeystore(path) + // Fail fast in the test suite + if err != nil { + panic(err) + } + + writableKeystore, err := keystore.AsWritableKeystore(keyStore) + if err != nil { + panic(err) + } + + writableKeystore.Store("PASSWORD", []byte(secret)) + writableKeystore.Save() + return keyStore +} + +// create a temporary file on disk to save the keystore. +func getTemporaryKeystoreFile() string { + path, err := ioutils.TempDir("", "testing") + if err != nil { + panic(err) + } + return filepath.Join(path, "keystore") +} diff --git a/metricbeat/beater/metricbeat.go b/metricbeat/beater/metricbeat.go index 050e7f265c1..fbf9d23110f 100644 --- a/metricbeat/beater/metricbeat.go +++ b/metricbeat/beater/metricbeat.go @@ -181,7 +181,12 @@ func newMetricbeat(b *beat.Beat, c *common.Config, options ...Option) (*Metricbe if config.Autodiscover != nil { var err error metricbeat.autodiscover, err = autodiscover.NewAutodiscover( - "metricbeat", b.Publisher, factory, autodiscover.QueryConfig(), config.Autodiscover) + "metricbeat", + b.Publisher, + factory, autodiscover.QueryConfig(), + config.Autodiscover, + b.Keystore, + ) if err != nil { return nil, err } diff --git a/metricbeat/docs/autodiscover-docker-config.asciidoc b/metricbeat/docs/autodiscover-docker-config.asciidoc index 79dce08e7a6..bd026ec0c69 100644 --- a/metricbeat/docs/autodiscover-docker-config.asciidoc +++ b/metricbeat/docs/autodiscover-docker-config.asciidoc @@ -19,3 +19,24 @@ metricbeat.autodiscover: This configuration launches a `redis` module for all containers running an image with `redis` in the name. `labels.dedot` defaults to be `true` for docker autodiscover, which means dots in docker labels are replaced with '_' by default. +Also Metricbeat autodiscover supports leveraging <> in order to retrieve sensitive data like passwords. +Here is an example of how a configuration using keystore would look like: + +["source","yaml",subs="attributes"] +------------------------------------------------------------------------------------- +metricbeat.autodiscover: + providers: + - type: docker + labels.dedot: true + templates: + - condition: + contains: + docker.container.image: redis + config: + - module: redis + metricsets: ["info", "keyspace"] + hosts: "${data.host}:6379" + password: "${REDIS_PASSWORD}" +------------------------------------------------------------------------------------- + +where `REDIS_PASSWORD` is a key stored in local keystore of Metricbeat. diff --git a/metricbeat/docs/autodiscover-hints.asciidoc b/metricbeat/docs/autodiscover-hints.asciidoc index 1bc56d8f54d..a34b623bd36 100644 --- a/metricbeat/docs/autodiscover-hints.asciidoc +++ b/metricbeat/docs/autodiscover-hints.asciidoc @@ -43,7 +43,9 @@ The username to use for authentication [float] ===== `co.elastic.metrics/password` -The password to use for authentication. It is recommended to retrieve this sensitive information from an ENV variable or a keystore and avoid placing passwords in plain text +The password to use for authentication. It is recommended to retrieve this sensitive information from an ENV variable +and avoid placing passwords in plain text. Unlike static autodiscover configuration, hints based autodiscover has +no access to the keystore of Metricbeat since it could be a potential security issue. [float] ===== `co.elastic.metrics/ssl.*` diff --git a/metricbeat/docs/autodiscover-kubernetes-config.asciidoc b/metricbeat/docs/autodiscover-kubernetes-config.asciidoc index 81e4ad15949..f3e6a74cdfb 100644 --- a/metricbeat/docs/autodiscover-kubernetes-config.asciidoc +++ b/metricbeat/docs/autodiscover-kubernetes-config.asciidoc @@ -17,3 +17,24 @@ metricbeat.autodiscover: ------------------------------------------------------------------------------------- This configuration launches a `prometheus` module for all containers of pods annotated `prometheus.io/scrape=true`. + +Also Metricbeat autodiscover supports leveraging <> in order to retrieve sensitive data like passwords. +Here is an example of how a configuration using keystore would look like: + +["source","yaml",subs="attributes"] +------------------------------------------------------------------------------------- +metricbeat.autodiscover: + providers: + - type: kubernetes + templates: + - condition: + contains: + kubernetes.labels.app: "redis" + config: + - module: redis + metricsets: ["info", "keyspace"] + hosts: "${data.host}:6379" + password: "${REDIS_PASSWORD}" +------------------------------------------------------------------------------------- + +where `REDIS_PASSWORD` is a key stored in local keystore of Metricbeat. diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 81e2d066557..0e3d1530349 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -1577,6 +1577,17 @@ type: object `billing` contains the estimated charges for your AWS account in Cloudwatch. + + +*`aws.billing.metrics.EstimatedCharges.max`*:: ++ +-- +Maximum estimated charges for AWS acccount. + +type: long + +-- + [float] === cloudwatch @@ -1883,6 +1894,107 @@ type: double `ebs` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS EBS. + + +*`aws.ebs.metrics.VolumeReadBytes.avg`*:: ++ +-- +Average size of each read operation during the period, except on volumes attached to a Nitro-based instance, where the average represents the average over the specified period. + +type: double + +-- + +*`aws.ebs.metrics.VolumeWriteBytes.avg`*:: ++ +-- +Average size of each write operation during the period, except on volumes attached to a Nitro-based instance, where the average represents the average over the specified period. + +type: double + +-- + +*`aws.ebs.metrics.VolumeReadOps.avg`*:: ++ +-- +The total number of read operations in a specified period of time. + +type: double + +-- + +*`aws.ebs.metrics.VolumeWriteOps.avg`*:: ++ +-- +The total number of write operations in a specified period of time. + +type: double + +-- + +*`aws.ebs.metrics.VolumeQueueLength.avg`*:: ++ +-- +The number of read and write operation requests waiting to be completed in a specified period of time. + +type: double + +-- + +*`aws.ebs.metrics.VolumeThroughputPercentage.avg`*:: ++ +-- +The percentage of I/O operations per second (IOPS) delivered of the total IOPS provisioned for an Amazon EBS volume. Used with Provisioned IOPS SSD volumes only. + +type: double + +-- + +*`aws.ebs.metrics.VolumeConsumedReadWriteOps.avg`*:: ++ +-- +The total amount of read and write operations (normalized to 256K capacity units) consumed in a specified period of time. Used with Provisioned IOPS SSD volumes only. + +type: double + +-- + +*`aws.ebs.metrics.BurstBalance.avg`*:: ++ +-- +Used with General Purpose SSD (gp2), Throughput Optimized HDD (st1), and Cold HDD (sc1) volumes only. Provides information about the percentage of I/O credits (for gp2) or throughput credits (for st1 and sc1) remaining in the burst bucket. + +type: double + +-- + +*`aws.ebs.metrics.VolumeTotalReadTime.sum`*:: ++ +-- +The total number of seconds spent by all read operations that completed in a specified period of time. + +type: double + +-- + +*`aws.ebs.metrics.VolumeTotalWriteTime.sum`*:: ++ +-- +The total number of seconds spent by all write operations that completed in a specified period of time. + +type: double + +-- + +*`aws.ebs.metrics.VolumeIdleTime.sum`*:: ++ +-- +The total number of seconds in a specified period of time when no read or write operations were submitted. + +type: double + +-- + [float] === ec2 @@ -2244,1782 +2356,1689 @@ type: integer `elb` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS ELB. -[float] -=== lambda - -`lambda` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS Lambda. - - -[float] -=== natgateway - -`natgateway` contains the metrics from Cloudwatch to track usage of NAT gateway related resources. - -[float] -=== rds -`rds` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS RDS. +*`aws.elb.metrics.BackendConnectionErrors.sum`*:: ++ +-- +The number of connections that were not successfully established between the load balancer and the registered instances. +type: long +-- -*`aws.rds.cpu.total.pct`*:: +*`aws.elb.metrics.HTTPCode_Backend_2XX.sum`*:: + -- -The percentage of CPU utilization. +The number of HTTP 2XX response code generated by registered instances. - -type: scaled_float - -format: percent +type: long -- -*`aws.rds.cpu.credit_usage`*:: +*`aws.elb.metrics.HTTPCode_Backend_3XX.sum`*:: + -- -The number of CPU credits spent by the instance for CPU utilization. - +The number of HTTP 3XX response code generated by registered instances. type: long -- -*`aws.rds.cpu.credit_balance`*:: +*`aws.elb.metrics.HTTPCode_Backend_4XX.sum`*:: + -- -The number of earned CPU credits that an instance has accrued since it was launched or started. - +The number of HTTP 4XX response code generated by registered instances. type: long -- -*`aws.rds.database_connections`*:: +*`aws.elb.metrics.HTTPCode_Backend_5XX.sum`*:: + -- -The number of database connections in use. - +The number of HTTP 5XX response code generated by registered instances. type: long -- -*`aws.rds.db_instance.arn`*:: +*`aws.elb.metrics.HTTPCode_ELB_4XX.sum`*:: + -- -Amazon Resource Name(ARN) for each rds. +The number of HTTP 4XX client error codes generated by the load balancer. - -type: keyword +type: long -- -*`aws.rds.db_instance.class`*:: +*`aws.elb.metrics.HTTPCode_ELB_5XX.sum`*:: + -- -Contains the name of the compute and memory capacity class of the DB instance. - +The number of HTTP 5XX server error codes generated by the load balancer. -type: keyword +type: long -- -*`aws.rds.db_instance.identifier`*:: +*`aws.elb.metrics.RequestCount.sum`*:: + -- -Contains a user-supplied database identifier. This identifier is the unique key that identifies a DB instance. +The number of requests completed or connections made during the specified interval. - -type: keyword +type: long -- -*`aws.rds.db_instance.status`*:: +*`aws.elb.metrics.SpilloverCount.sum`*:: + -- -Specifies the current state of this database. - +The total number of requests that were rejected because the surge queue is full. -type: keyword +type: long -- -*`aws.rds.disk_queue_depth`*:: +*`aws.elb.metrics.HealthyHostCount.max`*:: + -- -The number of outstanding IOs (read/write requests) waiting to access the disk. +The number of healthy instances registered with your load balancer. - -type: float +type: long -- -*`aws.rds.failed_sql_server_agent_jobs`*:: +*`aws.elb.metrics.SurgeQueueLength.max`*:: + -- -The number of failed SQL Server Agent jobs during the last minute. - +The total number of requests (HTTP listener) or connections (TCP listener) that are pending routing to a healthy instance. type: long -- -*`aws.rds.freeable_memory.bytes`*:: +*`aws.elb.metrics.UnHealthyHostCount.max`*:: + -- -The amount of available random access memory. - +The number of unhealthy instances registered with your load balancer. type: long -format: bytes - -- -*`aws.rds.free_storage.bytes`*:: +*`aws.elb.metrics.Latency.avg`*:: + -- -The amount of available storage space. - - -type: long +The total time elapsed, in seconds, from the time the load balancer sent the request to a registered instance until the instance started to send the response headers. -format: bytes +type: double -- -*`aws.rds.maximum_used_transaction_ids`*:: +*`aws.elb.metrics.EstimatedALBActiveConnectionCount.avg`*:: + -- -The maximum transaction ID that has been used. Applies to PostgreSQL. +The estimated number of concurrent TCP connections active from clients to the load balancer and from the load balancer to targets. - -type: long +type: double -- -*`aws.rds.oldest_replication_slot_lag.mb`*:: +*`aws.elb.metrics.EstimatedALBConsumedLCUs.avg`*:: + -- -The lagging size of the replica lagging the most in terms of WAL data received. Applies to PostgreSQL. - +The estimated number of load balancer capacity units (LCU) used by an Application Load Balancer. -type: long +type: double -- -*`aws.rds.read_io.ops_per_sec`*:: +*`aws.elb.metrics.EstimatedALBNewConnectionCount.avg`*:: + -- -The average number of disk read I/O operations per second. +The estimated number of new TCP connections established from clients to the load balancer and from the load balancer to targets. - -type: float +type: double -- -*`aws.rds.replica_lag.sec`*:: +*`aws.elb.metrics.EstimatedProcessedBytes.avg`*:: + -- -The amount of time a Read Replica DB instance lags behind the source DB instance. Applies to MySQL, MariaDB, and PostgreSQL Read Replicas. +The estimated number of bytes processed by an Application Load Balancer. +type: double -type: long +-- -format: duration +[float] +=== applicationelb --- +`applicationelb` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS ApplicationELB. -*`aws.rds.swap_usage.bytes`*:: + + + +*`aws.applicationelb.metrics.ActiveConnectionCount.sum`*:: + -- -The amount of swap space used on the DB instance. This metric is not available for SQL Server. - +The total number of concurrent TCP connections active from clients to the load balancer and from the load balancer to targets. type: long -format: bytes - -- -*`aws.rds.transaction_logs_generation`*:: +*`aws.applicationelb.metrics.ClientTLSNegotiationErrorCount.sum`*:: + -- -The disk space used by transaction logs. Applies to PostgreSQL. - +The number of TLS connections initiated by the client that did not establish a session with the load balancer due to a TLS error. type: long -- -*`aws.rds.write_io.ops_per_sec`*:: +*`aws.applicationelb.metrics.HTTP_Fixed_Response_Count.sum`*:: + -- -The average number of disk write I/O operations per second. +The number of fixed-response actions that were successful. - -type: float +type: long -- -*`aws.rds.queries`*:: +*`aws.applicationelb.metrics.HTTP_Redirect_Count.sum`*:: + -- -The average number of queries executed per second. - +The number of redirect actions that were successful. type: long -- -*`aws.rds.deadlocks`*:: +*`aws.applicationelb.metrics.HTTP_Redirect_Url_Limit_Exceeded_Count.sum`*:: + -- -The average number of deadlocks in the database per second. - +The number of redirect actions that couldn't be completed because the URL in the response location header is larger than 8K. type: long -- -*`aws.rds.volume_used.bytes`*:: +*`aws.applicationelb.metrics.HTTPCode_ELB_3XX_Count.sum`*:: + -- -The amount of storage used by your Aurora DB instance, in bytes. - +The number of HTTP 3XX redirection codes that originate from the load balancer. type: long -format: bytes - -- -*`aws.rds.volume.read.iops`*:: +*`aws.applicationelb.metrics.HTTPCode_ELB_4XX_Count.sum`*:: + -- -The number of billed read I/O operations from a cluster volume, reported at 5-minute intervals. - +The number of HTTP 4XX client error codes that originate from the load balancer. type: long -format: bytes - -- -*`aws.rds.volume.write.iops`*:: +*`aws.applicationelb.metrics.HTTPCode_ELB_5XX_Count.sum`*:: + -- -The number of write disk I/O operations to the cluster volume, reported at 5-minute intervals. - +The number of HTTP 5XX server error codes that originate from the load balancer. type: long -format: bytes - -- -*`aws.rds.free_local_storage.bytes`*:: +*`aws.applicationelb.metrics.HTTPCode_ELB_500_Count.sum`*:: + -- -The amount of storage available for temporary tables and logs, in bytes. - +The number of HTTP 500 error codes that originate from the load balancer. type: long -format: bytes - -- -*`aws.rds.login_failures`*:: +*`aws.applicationelb.metrics.HTTPCode_ELB_502_Count.sum`*:: + -- -The average number of failed login attempts per second. - +The number of HTTP 502 error codes that originate from the load balancer. type: long -- -*`aws.rds.throughput.commit`*:: +*`aws.applicationelb.metrics.HTTPCode_ELB_503_Count.sum`*:: + -- -The average number of commit operations per second. +The number of HTTP 503 error codes that originate from the load balancer. - -type: float +type: long -- -*`aws.rds.throughput.delete`*:: +*`aws.applicationelb.metrics.HTTPCode_ELB_504_Count.sum`*:: + -- -The average number of delete queries per second. - +The number of HTTP 504 error codes that originate from the load balancer. -type: float +type: long -- -*`aws.rds.throughput.ddl`*:: +*`aws.applicationelb.metrics.IPv6ProcessedBytes.sum`*:: + -- -The average number of DDL requests per second. +The total number of bytes processed by the load balancer over IPv6. - -type: float +type: long -- -*`aws.rds.throughput.dml`*:: +*`aws.applicationelb.metrics.IPv6RequestCount.sum`*:: + -- -The average number of inserts, updates, and deletes per second. - +The number of IPv6 requests received by the load balancer. -type: float +type: long -- -*`aws.rds.throughput.insert`*:: +*`aws.applicationelb.metrics.NewConnectionCount.sum`*:: + -- -The average number of insert queries per second. +The total number of new TCP connections established from clients to the load balancer and from the load balancer to targets. - -type: float +type: long -- -*`aws.rds.throughput.network`*:: +*`aws.applicationelb.metrics.ProcessedBytes.sum`*:: + -- -The amount of network throughput both received from and transmitted to clients by each instance in the Aurora MySQL DB cluster, in bytes per second. - +The total number of bytes processed by the load balancer over IPv4 and IPv6. -type: float +type: long -- -*`aws.rds.throughput.network_receive`*:: +*`aws.applicationelb.metrics.RejectedConnectionCount.sum`*:: + -- -The incoming (Receive) network traffic on the DB instance, including both customer database traffic and Amazon RDS traffic used for monitoring and replication. +The number of connections that were rejected because the load balancer had reached its maximum number of connections. - -type: float +type: long -- -*`aws.rds.throughput.network_transmit`*:: +*`aws.applicationelb.metrics.RequestCount.sum`*:: + -- -The outgoing (Transmit) network traffic on the DB instance, including both customer database traffic and Amazon RDS traffic used for monitoring and replication. - +The number of requests processed over IPv4 and IPv6. -type: float +type: long -- -*`aws.rds.throughput.read`*:: +*`aws.applicationelb.metrics.RuleEvaluations.sum`*:: + -- -The average amount of time taken per disk I/O operation. +The number of rules processed by the load balancer given a request rate averaged over an hour. - -type: float +type: long -- -*`aws.rds.throughput.select`*:: +*`aws.applicationelb.metrics.ConsumedLCUs.avg`*:: + -- -The average number of select queries per second. - +The number of load balancer capacity units (LCU) used by your load balancer. -type: float +type: double -- -*`aws.rds.throughput.update`*:: -+ --- -The average number of update queries per second. +[float] +=== networkelb +`networkelb` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS NetworkELB. -type: float --- -*`aws.rds.throughput.write`*:: + +*`aws.networkelb.metrics.ActiveFlowCount.avg`*:: + -- -The average number of bytes written to disk per second. +The total number of concurrent flows (or connections) from clients to targets. - -type: float +type: double -- -*`aws.rds.latency.commit`*:: +*`aws.networkelb.metrics.ActiveFlowCount_TCP.avg`*:: + -- -The amount of latency for commit operations, in milliseconds. - +The total number of concurrent TCP flows (or connections) from clients to targets. -type: float - -format: duration +type: double -- -*`aws.rds.latency.ddl`*:: +*`aws.networkelb.metrics.ActiveFlowCount_TLS.avg`*:: + -- -The amount of latency for data definition language (DDL) requests, in milliseconds. - - -type: float +The total number of concurrent TLS flows (or connections) from clients to targets. -format: duration +type: double -- -*`aws.rds.latency.dml`*:: +*`aws.networkelb.metrics.ActiveFlowCount_UDP.avg`*:: + -- -The amount of latency for inserts, updates, and deletes, in milliseconds. - - -type: float +The total number of concurrent UDP flows (or connections) from clients to targets. -format: duration +type: double -- -*`aws.rds.latency.insert`*:: +*`aws.networkelb.metrics.ConsumedLCUs.avg`*:: + -- -The amount of latency for insert queries, in milliseconds. +The number of load balancer capacity units (LCU) used by your load balancer. - -type: float - -format: duration +type: double -- -*`aws.rds.latency.read`*:: +*`aws.networkelb.metrics.ClientTLSNegotiationErrorCount.sum`*:: + -- -The average amount of time taken per disk I/O operation. - +The total number of TLS handshakes that failed during negotiation between a client and a TLS listener. -type: float - -format: duration +type: long -- -*`aws.rds.latency.select`*:: +*`aws.networkelb.metrics.NewFlowCount.sum`*:: + -- -The amount of latency for select queries, in milliseconds. - - -type: float +The total number of new flows (or connections) established from clients to targets in the time period. -format: duration +type: long -- -*`aws.rds.latency.update`*:: +*`aws.networkelb.metrics.NewFlowCount_TLS.sum`*:: + -- -The amount of latency for update queries, in milliseconds. - - -type: float +The total number of new TLS flows (or connections) established from clients to targets in the time period. -format: duration +type: long -- -*`aws.rds.latency.write`*:: +*`aws.networkelb.metrics.ProcessedBytes.sum`*:: + -- -The average amount of time taken per disk I/O operation. +The total number of bytes processed by the load balancer, including TCP/IP headers. - -type: float - -format: duration +type: long -- -*`aws.rds.latency.delete`*:: +*`aws.networkelb.metrics.ProcessedBytes_TLS.sum`*:: + -- -The amount of latency for delete queries, in milliseconds. - +The total number of bytes processed by TLS listeners. -type: float - -format: duration +type: long -- -*`aws.rds.disk_usage.bin_log.bytes`*:: +*`aws.networkelb.metrics.TargetTLSNegotiationErrorCount.sum`*:: + -- -The amount of disk space occupied by binary logs on the master. Applies to MySQL read replicas. - +The total number of TLS handshakes that failed during negotiation between a TLS listener and a target. type: long -format: bytes - -- -*`aws.rds.disk_usage.replication_slot.mb`*:: +*`aws.networkelb.metrics.TCP_Client_Reset_Count.sum`*:: + -- -The disk space used by replication slot files. Applies to PostgreSQL. - +The total number of reset (RST) packets sent from a client to a target. type: long -- -*`aws.rds.disk_usage.transaction_logs.mb`*:: +*`aws.networkelb.metrics.TCP_ELB_Reset_Count.sum`*:: + -- -The disk space used by transaction logs. Applies to PostgreSQL. - +The total number of reset (RST) packets generated by the load balancer. type: long -- -*`aws.rds.transactions.active`*:: +*`aws.networkelb.metrics.TCP_Target_Reset_Count.sum`*:: + -- -The average number of current transactions executing on an Aurora database instance per second. - +The total number of reset (RST) packets sent from a target to a client. type: long -- -*`aws.rds.transactions.blocked`*:: +*`aws.networkelb.metrics.HealthyHostCount.max`*:: + -- -The average number of transactions in the database that are blocked per second. - +The number of targets that are considered healthy. type: long -- -*`aws.rds.db_instance.db_cluster_identifier`*:: +*`aws.networkelb.metrics.UnHealthyHostCount.max`*:: + -- -This identifier is the unique key that identifies a DB cluster specifically for Amazon Aurora DB cluster. - +The number of targets that are considered unhealthy. -type: keyword +type: long -- -*`aws.rds.db_instance.role`*:: -+ --- -DB roles like WRITER or READER, specifically for Amazon Aurora DB cluster. +[float] +=== lambda +`lambda` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS Lambda. -type: keyword --- -*`aws.rds.db_instance.engine_name`*:: + +*`aws.lambda.metrics.Invocations.avg`*:: + -- -Each DB instance runs a DB engine, like MySQL, MariaDB, PostgreSQL and etc. +The number of times your function code is executed, including successful executions and executions that result in a function error. - -type: keyword +type: double -- -*`aws.rds.aurora_bin_log_replica_lag`*:: +*`aws.lambda.metrics.Errors.avg`*:: + -- -The amount of time a replica DB cluster running on Aurora with MySQL compatibility lags behind the source DB cluster. - +The number of invocations that result in a function error. -type: long +type: double -- -*`aws.rds.aurora_global_db.replicated_write_io.bytes`*:: +*`aws.lambda.metrics.DeadLetterErrors.avg`*:: + -- -In an Aurora Global Database, the number of write I/O operations replicated from the primary AWS Region to the cluster volume in a secondary AWS Region. +For asynchronous invocation, the number of times Lambda attempts to send an event to a dead-letter queue but fails. - -type: long +type: double -- -*`aws.rds.aurora_global_db.data_transfer.bytes`*:: +*`aws.lambda.metrics.DestinationDeliveryFailures.avg`*:: + -- -In an Aurora Global Database, the amount of redo log data transferred from the master AWS Region to a secondary AWS Region. - +For asynchronous invocation, the number of times Lambda attempts to send an event to a destination but fails. -type: long +type: double -- -*`aws.rds.aurora_global_db.replication_lag.ms`*:: +*`aws.lambda.metrics.Duration.avg`*:: + -- -For an Aurora Global Database, the amount of lag when replicating updates from the primary AWS Region, in milliseconds. +The amount of time that your function code spends processing an event. - -type: long +type: double -- -*`aws.rds.aurora_replica.lag.ms`*:: +*`aws.lambda.metrics.Throttles.avg`*:: + -- -For an Aurora Replica, the amount of lag when replicating updates from the primary instance, in milliseconds. - +The number of invocation requests that are throttled. -type: long +type: double -- -*`aws.rds.aurora_replica.lag_max.ms`*:: +*`aws.lambda.metrics.IteratorAge.avg`*:: + -- -The maximum amount of lag between the primary instance and each Aurora DB instance in the DB cluster, in milliseconds. +For event source mappings that read from streams, the age of the last record in the event. - -type: long +type: double -- -*`aws.rds.aurora_replica.lag_min.ms`*:: +*`aws.lambda.metrics.ConcurrentExecutions.avg`*:: + -- -The minimum amount of lag between the primary instance and each Aurora DB instance in the DB cluster, in milliseconds. - +The number of function instances that are processing events. -type: long +type: double -- -*`aws.rds.backtrack_change_records.creation_rate`*:: +*`aws.lambda.metrics.UnreservedConcurrentExecutions.avg`*:: + -- -The number of backtrack change records created over five minutes for your DB cluster. +For an AWS Region, the number of events that are being processed by functions that don't have reserved concurrency. - -type: long +type: double -- -*`aws.rds.backtrack_change_records.stored`*:: +*`aws.lambda.metrics.ProvisionedConcurrentExecutions.max`*:: + -- -The actual number of backtrack change records used by your DB cluster. - +The number of function instances that are processing events on provisioned concurrency. type: long -- -*`aws.rds.backtrack_window.actual`*:: +*`aws.lambda.metrics.ProvisionedConcurrencyUtilization.max`*:: + -- -The difference between the target backtrack window and the actual backtrack window. - +For a version or alias, the value of ProvisionedConcurrentExecutions divided by the total amount of provisioned concurrency allocated. type: long -- -*`aws.rds.backtrack_window.alert`*:: +*`aws.lambda.metrics.ProvisionedConcurrencyInvocations.sum`*:: + -- -The number of times that the actual backtrack window is smaller than the target backtrack window for a given period of time. - +The number of times your function code is executed on provisioned concurrency. type: long -- -*`aws.rds.storage_used.backup_retention_period.bytes`*:: +*`aws.lambda.metrics.ProvisionedConcurrencySpilloverInvocations.sum`*:: + -- -The total amount of backup storage in bytes used to support the point-in-time restore feature within the Aurora DB cluster's backup retention window. - +The number of times your function code is executed on standard concurrency when all provisioned concurrency is in use. type: long -- -*`aws.rds.storage_used.snapshot.bytes`*:: -+ --- -The total amount of backup storage in bytes consumed by all Aurora snapshots for an Aurora DB cluster outside its backup retention window. +[float] +=== natgateway + +`natgateway` contains the metrics from Cloudwatch to track usage of NAT gateway related resources. -type: long --- -*`aws.rds.cache_hit_ratio.buffer`*:: +*`aws.natgateway.metrics.BytesInFromDestination.sum`*:: + -- -The percentage of requests that are served by the buffer cache. - +The number of bytes received by the NAT gateway from the destination. type: long -- -*`aws.rds.cache_hit_ratio.result_set`*:: +*`aws.natgateway.metrics.BytesInFromSource.sum`*:: + -- -The percentage of requests that are served by the Resultset cache. - +The number of bytes received by the NAT gateway from clients in your VPC. type: long -- -*`aws.rds.engine_uptime.sec`*:: +*`aws.natgateway.metrics.BytesOutToDestination.sum`*:: + -- -The amount of time that the instance has been running, in seconds. - +The number of bytes sent out through the NAT gateway to the destination. type: long -- -*`aws.rds.rds_to_aurora_postgresql_replica_lag.sec`*:: +*`aws.natgateway.metrics.BytesOutToSource.sum`*:: + -- -The amount of lag in seconds when replicating updates from the primary RDS PostgreSQL instance to other nodes in the cluster. - +The number of bytes sent through the NAT gateway to the clients in your VPC. type: long -- -*`aws.rds.backup_storage_billed_total.bytes`*:: +*`aws.natgateway.metrics.ConnectionAttemptCount.sum`*:: + -- -The total amount of backup storage in bytes for which you are billed for a given Aurora DB cluster. - +The number of connection attempts made through the NAT gateway. type: long -- -*`aws.rds.aurora_volume_left_total.bytes`*:: +*`aws.natgateway.metrics.ConnectionEstablishedCount.sum`*:: + -- -The remaining available space for the cluster volume, measured in bytes. - +The number of connections established through the NAT gateway. type: long -- -[float] -=== s3_daily_storage - -`s3_daily_storage` contains the daily storage metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS S3. - - - -*`aws.s3_daily_storage.bucket.size.bytes`*:: +*`aws.natgateway.metrics.ErrorPortAllocation.sum`*:: + -- -The amount of data in bytes stored in a bucket. - +The number of times the NAT gateway could not allocate a source port. type: long -format: bytes - -- -*`aws.s3_daily_storage.number_of_objects`*:: +*`aws.natgateway.metrics.IdleTimeoutCount.sum`*:: + -- -The total number of objects stored in a bucket for all storage classes. - +The number of connections that transitioned from the active state to the idle state. type: long -- -[float] -=== s3_request - -`s3_request` contains request metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS S3. - - - -*`aws.s3_request.requests.total`*:: +*`aws.natgateway.metrics.PacketsDropCount.sum`*:: + -- -The total number of HTTP requests made to an Amazon S3 bucket, regardless of type. - +The number of packets dropped by the NAT gateway. type: long -- -*`aws.s3_request.requests.get`*:: +*`aws.natgateway.metrics.PacketsInFromDestination.sum`*:: + -- -The number of HTTP GET requests made for objects in an Amazon S3 bucket. - +The number of packets received by the NAT gateway from the destination. type: long -- -*`aws.s3_request.requests.put`*:: +*`aws.natgateway.metrics.PacketsInFromSource.sum`*:: + -- -The number of HTTP PUT requests made for objects in an Amazon S3 bucket. - +The number of packets received by the NAT gateway from clients in your VPC. type: long -- -*`aws.s3_request.requests.delete`*:: +*`aws.natgateway.metrics.PacketsOutToDestination.sum`*:: + -- -The number of HTTP DELETE requests made for objects in an Amazon S3 bucket. - +The number of packets sent out through the NAT gateway to the destination. type: long -- -*`aws.s3_request.requests.head`*:: +*`aws.natgateway.metrics.PacketsOutToSource.sum`*:: + -- -The number of HTTP HEAD requests made to an Amazon S3 bucket. - +The number of packets sent through the NAT gateway to the clients in your VPC. type: long -- -*`aws.s3_request.requests.post`*:: +*`aws.natgateway.metrics.ActiveConnectionCount.max`*:: + -- -The number of HTTP POST requests made to an Amazon S3 bucket. - +The total number of concurrent active TCP connections through the NAT gateway. type: long -- -*`aws.s3_request.requests.select`*:: -+ --- -The number of Amazon S3 SELECT Object Content requests made for objects in an Amazon S3 bucket. +[float] +=== rds +`rds` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS RDS. -type: long --- -*`aws.s3_request.requests.select_scanned.bytes`*:: +*`aws.rds.cpu.total.pct`*:: + -- -The number of bytes of data scanned with Amazon S3 SELECT Object Content requests in an Amazon S3 bucket. +The percentage of CPU utilization. -type: long +type: scaled_float -format: bytes +format: percent -- -*`aws.s3_request.requests.select_returned.bytes`*:: +*`aws.rds.cpu.credit_usage`*:: + -- -The number of bytes of data returned with Amazon S3 SELECT Object Content requests in an Amazon S3 bucket. +The number of CPU credits spent by the instance for CPU utilization. type: long -format: bytes - -- -*`aws.s3_request.requests.list`*:: +*`aws.rds.cpu.credit_balance`*:: + -- -The number of HTTP requests that list the contents of a bucket. +The number of earned CPU credits that an instance has accrued since it was launched or started. type: long -- -*`aws.s3_request.downloaded.bytes`*:: +*`aws.rds.database_connections`*:: + -- -The number bytes downloaded for requests made to an Amazon S3 bucket, where the response includes a body. +The number of database connections in use. type: long -format: bytes - -- -*`aws.s3_request.uploaded.bytes`*:: +*`aws.rds.db_instance.arn`*:: + -- -The number bytes uploaded that contain a request body, made to an Amazon S3 bucket. - +Amazon Resource Name(ARN) for each rds. -type: long -format: bytes +type: keyword -- -*`aws.s3_request.errors.4xx`*:: +*`aws.rds.db_instance.class`*:: + -- -The number of HTTP 4xx client error status code requests made to an Amazon S3 bucket with a value of either 0 or 1. +Contains the name of the compute and memory capacity class of the DB instance. -type: long +type: keyword -- -*`aws.s3_request.errors.5xx`*:: +*`aws.rds.db_instance.identifier`*:: + -- -The number of HTTP 5xx server error status code requests made to an Amazon S3 bucket with a value of either 0 or 1. +Contains a user-supplied database identifier. This identifier is the unique key that identifies a DB instance. -type: long +type: keyword -- -*`aws.s3_request.latency.first_byte.ms`*:: +*`aws.rds.db_instance.status`*:: + -- -The per-request time from the complete request being received by an Amazon S3 bucket to when the response starts to be returned. - +Specifies the current state of this database. -type: long -format: duration +type: keyword -- -*`aws.s3_request.latency.total_request.ms`*:: +*`aws.rds.disk_queue_depth`*:: + -- -The elapsed per-request time from the first byte received to the last byte sent to an Amazon S3 bucket. +The number of outstanding IOs (read/write requests) waiting to access the disk. -type: long +type: float -format: duration +-- +*`aws.rds.failed_sql_server_agent_jobs`*:: ++ -- +The number of failed SQL Server Agent jobs during the last minute. -[float] -=== sns -`sns` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS SNS. +type: long +-- -[float] -=== sqs +*`aws.rds.freeable_memory.bytes`*:: ++ +-- +The amount of available random access memory. -`sqs` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS SQS. +type: long +format: bytes -*`aws.sqs.oldest_message_age.sec`*:: +-- + +*`aws.rds.free_storage.bytes`*:: + -- -The approximate age of the oldest non-deleted message in the queue. +The amount of available storage space. type: long -format: duration +format: bytes -- -*`aws.sqs.messages.delayed`*:: +*`aws.rds.maximum_used_transaction_ids`*:: + -- -TThe number of messages in the queue that are delayed and not available for reading immediately. +The maximum transaction ID that has been used. Applies to PostgreSQL. type: long -- -*`aws.sqs.messages.not_visible`*:: +*`aws.rds.oldest_replication_slot_lag.mb`*:: + -- -The number of messages that are in flight. +The lagging size of the replica lagging the most in terms of WAL data received. Applies to PostgreSQL. type: long -- -*`aws.sqs.messages.visible`*:: +*`aws.rds.read_io.ops_per_sec`*:: + -- -The number of messages available for retrieval from the queue. +The average number of disk read I/O operations per second. -type: long +type: float -- -*`aws.sqs.messages.deleted`*:: +*`aws.rds.replica_lag.sec`*:: + -- -The number of messages deleted from the queue. +The amount of time a Read Replica DB instance lags behind the source DB instance. Applies to MySQL, MariaDB, and PostgreSQL Read Replicas. type: long +format: duration + -- -*`aws.sqs.messages.received`*:: +*`aws.rds.swap_usage.bytes`*:: + -- -The number of messages returned by calls to the ReceiveMessage action. +The amount of swap space used on the DB instance. This metric is not available for SQL Server. type: long +format: bytes + -- -*`aws.sqs.messages.sent`*:: +*`aws.rds.transaction_logs_generation`*:: + -- -The number of messages added to a queue. +The disk space used by transaction logs. Applies to PostgreSQL. type: long -- -*`aws.sqs.empty_receives`*:: +*`aws.rds.write_io.ops_per_sec`*:: + -- -The number of ReceiveMessage API calls that did not return a message. +The average number of disk write I/O operations per second. -type: long +type: float -- -*`aws.sqs.sent_message_size.bytes`*:: +*`aws.rds.queries`*:: + -- -The size of messages added to a queue. +The average number of queries executed per second. type: long -format: bytes - -- -*`aws.sqs.queue.name`*:: +*`aws.rds.deadlocks`*:: + -- -SQS queue name +The average number of deadlocks in the database per second. -type: keyword +type: long -- -[float] -=== transitgateway - -`transitgateway` contains the metrics from Cloudwatch to track usage of transit gateway related resources. - +*`aws.rds.volume_used.bytes`*:: ++ +-- +The amount of storage used by your Aurora DB instance, in bytes. -[float] -=== usage -`usage` contains the metrics from Cloudwatch to track usage of some AWS resources. +type: long +format: bytes -[float] -=== vpn +-- -`vpn` contains the metrics from Cloudwatch to track usage of VPN related resources. +*`aws.rds.volume.read.iops`*:: ++ +-- +The number of billed read I/O operations from a cluster volume, reported at 5-minute intervals. -[[exported-fields-azure]] -== azure fields +type: long -azure module +format: bytes +-- +*`aws.rds.volume.write.iops`*:: ++ +-- +The number of write disk I/O operations to the cluster volume, reported at 5-minute intervals. -[float] -=== azure +type: long +format: bytes +-- -*`azure.timegrain`*:: +*`aws.rds.free_local_storage.bytes`*:: + -- -The Azure metric timegrain +The amount of storage available for temporary tables and logs, in bytes. -type: keyword +type: long + +format: bytes -- -[float] -=== resource +*`aws.rds.login_failures`*:: ++ +-- +The average number of failed login attempts per second. -The resource specified +type: long +-- -*`azure.resource.type`*:: +*`aws.rds.throughput.commit`*:: + -- -The type of the resource +The average number of commit operations per second. -type: keyword +type: float -- -*`azure.resource.group`*:: +*`aws.rds.throughput.delete`*:: + -- -The resource group +The average number of delete queries per second. -type: keyword +type: float -- -*`azure.resource.tags.*`*:: +*`aws.rds.throughput.ddl`*:: + -- -Azure resource tags. +The average number of DDL requests per second. -type: object +type: float -- -*`azure.namespace`*:: +*`aws.rds.throughput.dml`*:: + -- -The namespace selected +The average number of inserts, updates, and deletes per second. -type: keyword +type: float -- -*`azure.subscription_id`*:: +*`aws.rds.throughput.insert`*:: + -- -The subscription ID +The average number of insert queries per second. -type: keyword +type: float -- -*`azure.dimensions.*`*:: +*`aws.rds.throughput.network`*:: + -- -Azure metric dimensions. +The amount of network throughput both received from and transmitted to clients by each instance in the Aurora MySQL DB cluster, in bytes per second. -type: object +type: float -- -*`azure.compute_vm.*.*`*:: +*`aws.rds.throughput.network_receive`*:: + -- -compute_vm +The incoming (Receive) network traffic on the DB instance, including both customer database traffic and Amazon RDS traffic used for monitoring and replication. -type: object +type: float -- -*`azure.compute_vm_scaleset.*.*`*:: +*`aws.rds.throughput.network_transmit`*:: + -- -compute_vm_scaleset +The outgoing (Transmit) network traffic on the DB instance, including both customer database traffic and Amazon RDS traffic used for monitoring and replication. -type: object +type: float -- -*`azure.container_instance.*.*`*:: +*`aws.rds.throughput.read`*:: + -- -container instance +The average amount of time taken per disk I/O operation. -type: object +type: float -- -*`azure.container_registry.*.*`*:: +*`aws.rds.throughput.select`*:: + -- -container registry +The average number of select queries per second. -type: object +type: float -- -*`azure.container_service.*.*`*:: +*`aws.rds.throughput.update`*:: + -- -container service +The average number of update queries per second. -type: object +type: float -- -*`azure.database_account.*.*`*:: +*`aws.rds.throughput.write`*:: + -- -database account +The average number of bytes written to disk per second. -type: object +type: float -- -[float] -=== monitor - -monitor - - - -*`azure.monitor.metrics.*.*`*:: +*`aws.rds.latency.commit`*:: + -- -Metrics returned. +The amount of latency for commit operations, in milliseconds. -type: object +type: float + +format: duration -- -*`azure.storage.*.*`*:: +*`aws.rds.latency.ddl`*:: + -- -storage account +The amount of latency for data definition language (DDL) requests, in milliseconds. -type: object +type: float + +format: duration -- -[[exported-fields-beat-common]] -== Beat fields +*`aws.rds.latency.dml`*:: ++ +-- +The amount of latency for inserts, updates, and deletes, in milliseconds. -Contains common beat fields available in all event types. +type: float + +format: duration +-- -*`agent.hostname`*:: +*`aws.rds.latency.insert`*:: + -- -Hostname of the agent. +The amount of latency for insert queries, in milliseconds. -type: keyword + +type: float + +format: duration -- -*`beat.timezone`*:: +*`aws.rds.latency.read`*:: + -- -type: alias +The average amount of time taken per disk I/O operation. -alias to: event.timezone + +type: float + +format: duration -- -*`fields`*:: +*`aws.rds.latency.select`*:: + -- -Contains user configurable fields. +The amount of latency for select queries, in milliseconds. -type: object +type: float + +format: duration -- -*`beat.name`*:: +*`aws.rds.latency.update`*:: + -- -type: alias +The amount of latency for update queries, in milliseconds. -alias to: host.name + +type: float + +format: duration -- -*`beat.hostname`*:: +*`aws.rds.latency.write`*:: + -- -type: alias +The average amount of time taken per disk I/O operation. -alias to: agent.hostname + +type: float + +format: duration -- -*`timeseries.instance`*:: +*`aws.rds.latency.delete`*:: + -- -Time series instance id - -type: keyword +The amount of latency for delete queries, in milliseconds. --- -[[exported-fields-beat]] -== Beat fields +type: float -Beat module +format: duration +-- +*`aws.rds.disk_usage.bin_log.bytes`*:: ++ +-- +The amount of disk space occupied by binary logs on the master. Applies to MySQL read replicas. -[float] -=== beat +type: long +format: bytes +-- -*`beat.id`*:: +*`aws.rds.disk_usage.replication_slot.mb`*:: + -- -Beat ID. +The disk space used by replication slot files. Applies to PostgreSQL. -type: keyword +type: long -- -*`beat.type`*:: +*`aws.rds.disk_usage.transaction_logs.mb`*:: + -- -Beat type. +The disk space used by transaction logs. Applies to PostgreSQL. -type: keyword +type: long -- -[float] -=== state +*`aws.rds.transactions.active`*:: ++ +-- +The average number of current transactions executing on an Aurora database instance per second. -Beat state +type: long +-- -*`beat.state.management.enabled`*:: +*`aws.rds.transactions.blocked`*:: + -- -Is central management enabled? +The average number of transactions in the database that are blocked per second. -type: boolean +type: long -- -*`beat.state.module.count`*:: +*`aws.rds.db_instance.db_cluster_identifier`*:: + -- -Number of modules enabled +This identifier is the unique key that identifies a DB cluster specifically for Amazon Aurora DB cluster. -type: integer +type: keyword -- -*`beat.state.output.name`*:: +*`aws.rds.db_instance.role`*:: + -- -Name of output used by Beat +DB roles like WRITER or READER, specifically for Amazon Aurora DB cluster. type: keyword -- -*`beat.state.queue.name`*:: +*`aws.rds.db_instance.engine_name`*:: + -- -Name of queue being used by Beat +Each DB instance runs a DB engine, like MySQL, MariaDB, PostgreSQL and etc. type: keyword -- -[float] -=== stats +*`aws.rds.aurora_bin_log_replica_lag`*:: ++ +-- +The amount of time a replica DB cluster running on Aurora with MySQL compatibility lags behind the source DB cluster. -Beat stats +type: long +-- -*`beat.stats.uptime.ms`*:: +*`aws.rds.aurora_global_db.replicated_write_io.bytes`*:: + -- -Beat uptime +In an Aurora Global Database, the number of write I/O operations replicated from the primary AWS Region to the cluster volume in a secondary AWS Region. type: long -- -*`beat.stats.runtime.goroutines`*:: +*`aws.rds.aurora_global_db.data_transfer.bytes`*:: + -- -Number of goroutines running in Beat +In an Aurora Global Database, the amount of redo log data transferred from the master AWS Region to a secondary AWS Region. type: long -- -[float] -=== libbeat +*`aws.rds.aurora_global_db.replication_lag.ms`*:: ++ +-- +For an Aurora Global Database, the amount of lag when replicating updates from the primary AWS Region, in milliseconds. -Fields common to all Beats +type: long +-- -[float] -=== output +*`aws.rds.aurora_replica.lag.ms`*:: ++ +-- +For an Aurora Replica, the amount of lag when replicating updates from the primary instance, in milliseconds. -Output stats +type: long +-- -*`beat.stats.libbeat.output.type`*:: +*`aws.rds.aurora_replica.lag_max.ms`*:: + -- -Type of output +The maximum amount of lag between the primary instance and each Aurora DB instance in the DB cluster, in milliseconds. -type: keyword +type: long -- -[float] -=== events +*`aws.rds.aurora_replica.lag_min.ms`*:: ++ +-- +The minimum amount of lag between the primary instance and each Aurora DB instance in the DB cluster, in milliseconds. -Event counters +type: long +-- -*`beat.stats.libbeat.output.events.acked`*:: +*`aws.rds.backtrack_change_records.creation_rate`*:: + -- -Number of events acknowledged +The number of backtrack change records created over five minutes for your DB cluster. type: long -- -*`beat.stats.libbeat.output.events.active`*:: +*`aws.rds.backtrack_change_records.stored`*:: + -- -Number of active events +The actual number of backtrack change records used by your DB cluster. type: long -- -*`beat.stats.libbeat.output.events.batches`*:: +*`aws.rds.backtrack_window.actual`*:: + -- -Number of event batches +The difference between the target backtrack window and the actual backtrack window. type: long -- -*`beat.stats.libbeat.output.events.dropped`*:: +*`aws.rds.backtrack_window.alert`*:: + -- -Number of events dropped +The number of times that the actual backtrack window is smaller than the target backtrack window for a given period of time. type: long -- -*`beat.stats.libbeat.output.events.duplicates`*:: +*`aws.rds.storage_used.backup_retention_period.bytes`*:: + -- -Number of events duplicated +The total amount of backup storage in bytes used to support the point-in-time restore feature within the Aurora DB cluster's backup retention window. type: long -- -*`beat.stats.libbeat.output.events.failed`*:: +*`aws.rds.storage_used.snapshot.bytes`*:: + -- -Number of events failed +The total amount of backup storage in bytes consumed by all Aurora snapshots for an Aurora DB cluster outside its backup retention window. type: long -- -*`beat.stats.libbeat.output.events.toomany`*:: +*`aws.rds.cache_hit_ratio.buffer`*:: + -- -Number of too many events +The percentage of requests that are served by the buffer cache. type: long -- -*`beat.stats.libbeat.output.events.total`*:: +*`aws.rds.cache_hit_ratio.result_set`*:: + -- -Total number of events +The percentage of requests that are served by the Resultset cache. type: long -- -[float] -=== read +*`aws.rds.engine_uptime.sec`*:: ++ +-- +The amount of time that the instance has been running, in seconds. -Read stats +type: long +-- -*`beat.stats.libbeat.output.read.bytes`*:: +*`aws.rds.rds_to_aurora_postgresql_replica_lag.sec`*:: + -- -Number of bytes read +The amount of lag in seconds when replicating updates from the primary RDS PostgreSQL instance to other nodes in the cluster. type: long -- -*`beat.stats.libbeat.output.read.errors`*:: +*`aws.rds.backup_storage_billed_total.bytes`*:: + -- -Number of read errors +The total amount of backup storage in bytes for which you are billed for a given Aurora DB cluster. type: long -- -[float] -=== write - -Write stats - - - -*`beat.stats.libbeat.output.write.bytes`*:: +*`aws.rds.aurora_volume_left_total.bytes`*:: + -- -Number of bytes written +The remaining available space for the cluster volume, measured in bytes. type: long -- -*`beat.stats.libbeat.output.write.errors`*:: -+ --- -Number of write errors +[float] +=== s3_daily_storage + +`s3_daily_storage` contains the daily storage metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS S3. -type: long +*`aws.s3_daily_storage.bucket.size.bytes`*:: ++ -- +The amount of data in bytes stored in a bucket. -[[exported-fields-ceph]] -== Ceph fields -Ceph module +type: long +format: bytes +-- -[float] -=== ceph +*`aws.s3_daily_storage.number_of_objects`*:: ++ +-- +The total number of objects stored in a bucket for all storage classes. -`ceph` contains the metrics that were scraped from CEPH. +type: long +-- [float] -=== cluster_disk +=== s3_request -cluster_disk +`s3_request` contains request metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS S3. -*`ceph.cluster_disk.available.bytes`*:: +*`aws.s3_request.requests.total`*:: + -- -Available bytes of the cluster +The total number of HTTP requests made to an Amazon S3 bucket, regardless of type. type: long -format: bytes - -- -*`ceph.cluster_disk.total.bytes`*:: +*`aws.s3_request.requests.get`*:: + -- -Total bytes of the cluster +The number of HTTP GET requests made for objects in an Amazon S3 bucket. type: long -format: bytes - -- -*`ceph.cluster_disk.used.bytes`*:: +*`aws.s3_request.requests.put`*:: + -- -Used bytes of the cluster +The number of HTTP PUT requests made for objects in an Amazon S3 bucket. type: long -format: bytes - -- -[float] -=== cluster_health +*`aws.s3_request.requests.delete`*:: ++ +-- +The number of HTTP DELETE requests made for objects in an Amazon S3 bucket. -cluster_health +type: long +-- -*`ceph.cluster_health.overall_status`*:: +*`aws.s3_request.requests.head`*:: + -- -Overall status of the cluster +The number of HTTP HEAD requests made to an Amazon S3 bucket. -type: keyword +type: long -- -*`ceph.cluster_health.timechecks.epoch`*:: +*`aws.s3_request.requests.post`*:: + -- -Map version +The number of HTTP POST requests made to an Amazon S3 bucket. type: long -- -*`ceph.cluster_health.timechecks.round.value`*:: +*`aws.s3_request.requests.select`*:: + -- -timecheck round +The number of Amazon S3 SELECT Object Content requests made for objects in an Amazon S3 bucket. type: long -- -*`ceph.cluster_health.timechecks.round.status`*:: +*`aws.s3_request.requests.select_scanned.bytes`*:: + -- -Status of the round +The number of bytes of data scanned with Amazon S3 SELECT Object Content requests in an Amazon S3 bucket. -type: keyword +type: long + +format: bytes -- -[float] -=== cluster_status +*`aws.s3_request.requests.select_returned.bytes`*:: ++ +-- +The number of bytes of data returned with Amazon S3 SELECT Object Content requests in an Amazon S3 bucket. -cluster_status +type: long + +format: bytes +-- -*`ceph.cluster_status.version`*:: +*`aws.s3_request.requests.list`*:: + -- -Ceph Status version +The number of HTTP requests that list the contents of a bucket. type: long -- -*`ceph.cluster_status.traffic.read_bytes`*:: +*`aws.s3_request.downloaded.bytes`*:: + -- -Cluster read throughput per second +The number bytes downloaded for requests made to an Amazon S3 bucket, where the response includes a body. type: long @@ -4028,10 +4047,10 @@ format: bytes -- -*`ceph.cluster_status.traffic.write_bytes`*:: +*`aws.s3_request.uploaded.bytes`*:: + -- -Cluster write throughput per second +The number bytes uploaded that contain a request body, made to an Amazon S3 bucket. type: long @@ -4040,1577 +4059,1578 @@ format: bytes -- -*`ceph.cluster_status.traffic.read_op_per_sec`*:: +*`aws.s3_request.errors.4xx`*:: + -- -Cluster read iops per second +The number of HTTP 4xx client error status code requests made to an Amazon S3 bucket with a value of either 0 or 1. type: long -- -*`ceph.cluster_status.traffic.write_op_per_sec`*:: +*`aws.s3_request.errors.5xx`*:: + -- -Cluster write iops per second +The number of HTTP 5xx server error status code requests made to an Amazon S3 bucket with a value of either 0 or 1. type: long -- -*`ceph.cluster_status.misplace.total`*:: +*`aws.s3_request.latency.first_byte.ms`*:: + -- -Cluster misplace pg number +The per-request time from the complete request being received by an Amazon S3 bucket to when the response starts to be returned. type: long +format: duration + -- -*`ceph.cluster_status.misplace.objects`*:: +*`aws.s3_request.latency.total_request.ms`*:: + -- -Cluster misplace objects number +The elapsed per-request time from the first byte received to the last byte sent to an Amazon S3 bucket. type: long +format: duration + -- -*`ceph.cluster_status.misplace.ratio`*:: +[float] +=== sns + +`sns` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS SNS. + + + + +*`aws.sns.metrics.PublishSize.avg`*:: + -- -Cluster misplace ratio +The size of messages published. +type: double -type: scaled_float +-- -format: percent +*`aws.sns.metrics.SMSSuccessRate.avg`*:: ++ +-- +The rate of successful SMS message deliveries. + +type: double -- -*`ceph.cluster_status.degraded.total`*:: +*`aws.sns.metrics.NumberOfMessagesPublished.sum`*:: + -- -Cluster degraded pg number - +The number of messages published to your Amazon SNS topics. type: long -- -*`ceph.cluster_status.degraded.objects`*:: +*`aws.sns.metrics.NumberOfNotificationsDelivered.sum`*:: + -- -Cluster degraded objects number - +The number of messages successfully delivered from your Amazon SNS topics to subscribing endpoints. type: long -- -*`ceph.cluster_status.degraded.ratio`*:: +*`aws.sns.metrics.NumberOfNotificationsFailed.sum`*:: + -- -Cluster degraded ratio +The number of messages that Amazon SNS failed to deliver. +type: long -type: scaled_float +-- -format: percent +*`aws.sns.metrics.NumberOfNotificationsFilteredOut.sum`*:: ++ +-- +The number of messages that were rejected by subscription filter policies. + +type: long -- -*`ceph.cluster_status.pg.data_bytes`*:: +*`aws.sns.metrics.NumberOfNotificationsFilteredOut-InvalidAttributes.sum`*:: + -- -Cluster pg data bytes - +The number of messages that were rejected by subscription filter policies because the messages' attributes are invalid - for example, because the attribute JSON is incorrectly formatted. type: long -format: bytes - -- -*`ceph.cluster_status.pg.avail_bytes`*:: +*`aws.sns.metrics.NumberOfNotificationsFilteredOut-NoMessageAttributes.sum`*:: + -- -Cluster available bytes - +The number of messages that were rejected by subscription filter policies because the messages have no attributes. type: long -format: bytes - -- -*`ceph.cluster_status.pg.total_bytes`*:: +*`aws.sns.metrics.NumberOfNotificationsRedrivenToDlq.sum`*:: + -- -Cluster total bytes - +The number of messages that have been moved to a dead-letter queue. type: long -format: bytes - -- -*`ceph.cluster_status.pg.used_bytes`*:: +*`aws.sns.metrics.NumberOfNotificationsFailedToRedriveToDlq.sum`*:: + -- -Cluster used bytes - +The number of messages that couldn't be moved to a dead-letter queue. type: long -format: bytes - -- -*`ceph.cluster_status.pg_state.state_name`*:: +*`aws.sns.metrics.SMSMonthToDateSpentUSD.sum`*:: + -- -Pg state description - +The charges you have accrued since the start of the current calendar month for sending SMS messages. type: long -- -*`ceph.cluster_status.pg_state.count`*:: +[float] +=== sqs + +`sqs` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS SQS. + + + +*`aws.sqs.oldest_message_age.sec`*:: + -- -Shows how many pgs are in state of pg_state.state_name +The approximate age of the oldest non-deleted message in the queue. type: long +format: duration + -- -*`ceph.cluster_status.pg_state.version`*:: +*`aws.sqs.messages.delayed`*:: + -- -Cluster status version +TThe number of messages in the queue that are delayed and not available for reading immediately. type: long -- -*`ceph.cluster_status.osd.full`*:: +*`aws.sqs.messages.not_visible`*:: + -- -Is osd full +The number of messages that are in flight. -type: boolean +type: long -- -*`ceph.cluster_status.osd.nearfull`*:: +*`aws.sqs.messages.visible`*:: + -- -Is osd near full +The number of messages available for retrieval from the queue. -type: boolean +type: long -- -*`ceph.cluster_status.osd.num_osds`*:: +*`aws.sqs.messages.deleted`*:: + -- -Shows how many osds in the cluster +The number of messages deleted from the queue. type: long -- -*`ceph.cluster_status.osd.num_up_osds`*:: +*`aws.sqs.messages.received`*:: + -- -Shows how many osds are on the state of UP +The number of messages returned by calls to the ReceiveMessage action. type: long -- -*`ceph.cluster_status.osd.num_in_osds`*:: +*`aws.sqs.messages.sent`*:: + -- -Shows how many osds are on the state of IN +The number of messages added to a queue. type: long -- -*`ceph.cluster_status.osd.num_remapped_pgs`*:: +*`aws.sqs.empty_receives`*:: + -- -Shows how many osds are on the state of REMAPPED +The number of ReceiveMessage API calls that did not return a message. type: long -- -*`ceph.cluster_status.osd.epoch`*:: +*`aws.sqs.sent_message_size.bytes`*:: + -- -epoch number +The size of messages added to a queue. type: long --- - -[float] -=== mgr_cluster_disk +format: bytes -see: cluster_disk +-- +*`aws.sqs.queue.name`*:: ++ +-- +SQS queue name -[float] -=== mgr_cluster_health -see: cluster_health +type: keyword +-- [float] -=== mgr_osd_perf +=== transitgateway -OSD performance metrics of Ceph cluster +`transitgateway` contains the metrics from Cloudwatch to track usage of transit gateway related resources. -*`ceph.mgr_osd_perf.id`*:: + +*`aws.transitgateway.metrics.BytesIn.sum`*:: + -- -OSD ID +The number of bytes received by the transit gateway. type: long -- -*`ceph.mgr_osd_perf.stats.commit_latency_ms`*:: +*`aws.transitgateway.metrics.BytesOut.sum`*:: + -- -Commit latency in ms +The number of bytes sent from the transit gateway. type: long -- -*`ceph.mgr_osd_perf.stats.apply_latency_ms`*:: +*`aws.transitgateway.metrics.PacketsIn.sum`*:: + -- -Apply latency in ms +The number of packets received by the transit gateway. type: long -- -*`ceph.mgr_osd_perf.stats.commit_latency_ns`*:: +*`aws.transitgateway.metrics.PacketsOut.sum`*:: + -- -Commit latency in ns +The number of packets sent by the transit gateway. type: long -- -*`ceph.mgr_osd_perf.stats.apply_latency_ns`*:: +*`aws.transitgateway.metrics.PacketDropCountBlackhole.sum`*:: + -- -Apply latency in ns +The number of packets dropped because they matched a blackhole route. type: long -- -[float] -=== mgr_osd_pool_stats +*`aws.transitgateway.metrics.PacketDropCountNoRoute.sum`*:: ++ +-- +The number of packets dropped because they did not match a route. -OSD pool stats of Ceph cluster +type: long +-- +[float] +=== usage -*`ceph.mgr_osd_pool_stats.pool_name`*:: -+ --- -Pool name +`usage` contains the metrics from Cloudwatch to track usage of some AWS resources. -type: keyword --- -*`ceph.mgr_osd_pool_stats.pool_id`*:: + +*`aws.usage.metrics.CallCount.sum`*:: + -- -Pool ID +The number of specified API operations performed in your account. type: long -- -*`ceph.mgr_osd_pool_stats.client_io_rate`*:: +*`aws.usage.metrics.ResourceCount.sum`*:: + -- -Client I/O rates +The number of the specified resources running in your account. The resources are defined by the dimensions associated with the metric. -type: object +type: long -- [float] -=== mgr_osd_tree - -see: osd_tree - +=== vpn -[float] -=== mgr_pool_disk +`vpn` contains the metrics from Cloudwatch to track usage of VPN related resources. -see: pool_disk -[float] -=== monitor_health -monitor_health stats data +*`aws.vpn.metrics.TunnelState.avg`*:: ++ +-- +The state of the tunnel. For static VPNs, 0 indicates DOWN and 1 indicates UP. For BGP VPNs, 1 indicates ESTABLISHED and 0 is used for all other states. +type: double +-- -*`ceph.monitor_health.available.pct`*:: +*`aws.vpn.metrics.TunnelDataIn.sum`*:: + -- -Available percent of the MON +The bytes received through the VPN tunnel. - -type: long +type: double -- -*`ceph.monitor_health.health`*:: +*`aws.vpn.metrics.TunnelDataOut.sum`*:: + -- -Health of the MON - +The bytes sent through the VPN tunnel. -type: keyword +type: double -- -*`ceph.monitor_health.available.kb`*:: -+ --- -Available KB of the MON +[[exported-fields-azure]] +== azure fields +azure module -type: long --- -*`ceph.monitor_health.total.kb`*:: -+ --- -Total KB of the MON +[float] +=== azure -type: long --- -*`ceph.monitor_health.used.kb`*:: +*`azure.timegrain`*:: + -- -Used KB of the MON +The Azure metric timegrain -type: long +type: keyword -- -*`ceph.monitor_health.last_updated`*:: -+ --- -Time when was updated +[float] +=== resource +The resource specified -type: date --- -*`ceph.monitor_health.name`*:: +*`azure.resource.type`*:: + -- -Name of the MON +The type of the resource type: keyword -- -*`ceph.monitor_health.store_stats.log.bytes`*:: +*`azure.resource.group`*:: + -- -Log bytes of MON - +The resource group -type: long -format: bytes +type: keyword -- -*`ceph.monitor_health.store_stats.misc.bytes`*:: +*`azure.resource.tags.*`*:: + -- -Misc bytes of MON - +Azure resource tags. -type: long -format: bytes +type: object -- -*`ceph.monitor_health.store_stats.sst.bytes`*:: +*`azure.namespace`*:: + -- -SST bytes of MON - +The namespace selected -type: long -format: bytes +type: keyword -- -*`ceph.monitor_health.store_stats.total.bytes`*:: +*`azure.subscription_id`*:: + -- -Total bytes of MON - +The subscription ID -type: long -format: bytes +type: keyword -- -*`ceph.monitor_health.store_stats.last_updated`*:: +*`azure.dimensions.*`*:: + -- -Last updated +Azure metric dimensions. -type: long +type: object -- -[float] -=== osd_df +*`azure.compute_vm.*.*`*:: ++ +-- +compute_vm -ceph osd disk usage information +type: object +-- -*`ceph.osd_df.id`*:: +*`azure.compute_vm_scaleset.*.*`*:: + -- -osd node id +compute_vm_scaleset -type: long +type: object -- -*`ceph.osd_df.name`*:: +*`azure.container_instance.*.*`*:: + -- -osd node name +container instance -type: keyword +type: object -- -*`ceph.osd_df.device_class`*:: +*`azure.container_registry.*.*`*:: + -- -osd node type, illegal type include hdd, ssd etc. +container registry -type: keyword +type: object -- -*`ceph.osd_df.total.byte`*:: +*`azure.container_service.*.*`*:: + -- -osd disk total volume - +container service -type: long -format: bytes +type: object -- -*`ceph.osd_df.used.byte`*:: +*`azure.database_account.*.*`*:: + -- -osd disk usage volume - - -type: long +database account -format: bytes --- +type: object -*`ceph.osd_df.available.bytes`*:: -+ -- -osd disk available volume +[float] +=== monitor -type: long +monitor -format: bytes --- -*`ceph.osd_df.pg_num`*:: +*`azure.monitor.metrics.*.*`*:: + -- -shows how many pg located on this osd +Metrics returned. -type: long +type: object -- -*`ceph.osd_df.used.pct`*:: +*`azure.storage.*.*`*:: + -- -osd disk usage percentage - +storage account -type: scaled_float -format: percent +type: object -- -[float] -=== osd_tree +[[exported-fields-beat-common]] +== Beat fields -ceph osd tree info +Contains common beat fields available in all event types. -*`ceph.osd_tree.id`*:: +*`agent.hostname`*:: + -- -osd or bucket node id +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. -type: long +type: keyword -- -*`ceph.osd_tree.name`*:: +*`beat.timezone`*:: + -- -osd or bucket node name - +type: alias -type: keyword +alias to: event.timezone -- -*`ceph.osd_tree.type`*:: +*`fields`*:: + -- -osd or bucket node type, illegal type include osd, host, root etc. +Contains user configurable fields. -type: keyword +type: object -- -*`ceph.osd_tree.type_id`*:: +*`beat.name`*:: + -- -osd or bucket node typeID - +type: alias -type: long +alias to: host.name -- -*`ceph.osd_tree.children`*:: +*`beat.hostname`*:: + -- -bucket children list, separated by comma. - +type: alias -type: keyword +alias to: agent.hostname -- -*`ceph.osd_tree.crush_weight`*:: +*`timeseries.instance`*:: + -- -osd node crush weight - +Time series instance id -type: float +type: keyword -- -*`ceph.osd_tree.depth`*:: -+ --- -node depth +[[exported-fields-beat]] +== Beat fields +Beat module -type: long --- -*`ceph.osd_tree.exists`*:: -+ --- -is node still exist or not(1-yes, 0-no) +[float] +=== beat -type: boolean --- -*`ceph.osd_tree.primary_affinity`*:: +*`beat.id`*:: + -- -the weight of reading data from primary osd +Beat ID. -type: float +type: keyword -- -*`ceph.osd_tree.reweight`*:: +*`beat.type`*:: + -- -the reweight of osd +Beat type. -type: long +type: keyword -- -*`ceph.osd_tree.status`*:: -+ --- -status of osd, it should be up or down +[float] +=== state +Beat state -type: keyword --- -*`ceph.osd_tree.device_class`*:: +*`beat.state.management.enabled`*:: + -- -the device class of osd, like hdd, ssd etc. +Is central management enabled? -type: keyword +type: boolean -- -*`ceph.osd_tree.father`*:: +*`beat.state.module.count`*:: + -- -the parent node of this osd or bucket node +Number of modules enabled -type: keyword +type: integer -- -[float] -=== pool_disk - -pool_disk - - - -*`ceph.pool_disk.id`*:: +*`beat.state.output.name`*:: + -- -Id of the pool +Name of output used by Beat -type: long +type: keyword -- -*`ceph.pool_disk.name`*:: +*`beat.state.queue.name`*:: + -- -Name of the pool +Name of queue being used by Beat type: keyword -- -*`ceph.pool_disk.stats.available.bytes`*:: -+ --- -Available bytes of the pool - +[float] +=== stats -type: long +Beat stats -format: bytes --- -*`ceph.pool_disk.stats.objects`*:: +*`beat.stats.uptime.ms`*:: + -- -Number of objects of the pool +Beat uptime type: long -- -*`ceph.pool_disk.stats.used.bytes`*:: +*`beat.stats.runtime.goroutines`*:: + -- -Used bytes of the pool +Number of goroutines running in Beat type: long -format: bytes - -- -*`ceph.pool_disk.stats.used.kb`*:: -+ --- -Used kb of the pool +[float] +=== libbeat +Fields common to all Beats -type: long --- -[[exported-fields-cloud]] -== Cloud provider metadata fields +[float] +=== output -Metadata from cloud providers added by the add_cloud_metadata processor. +Output stats -*`cloud.project.id`*:: +*`beat.stats.libbeat.output.type`*:: + -- -Name of the project in Google Cloud. +Type of output -example: project-x +type: keyword -- -*`cloud.image.id`*:: -+ --- -Image ID for the cloud instance. +[float] +=== events +Event counters -example: ami-abcd1234 --- -*`meta.cloud.provider`*:: +*`beat.stats.libbeat.output.events.acked`*:: + -- -type: alias +Number of events acknowledged -alias to: cloud.provider + +type: long -- -*`meta.cloud.instance_id`*:: +*`beat.stats.libbeat.output.events.active`*:: + -- -type: alias +Number of active events -alias to: cloud.instance.id + +type: long -- -*`meta.cloud.instance_name`*:: +*`beat.stats.libbeat.output.events.batches`*:: + -- -type: alias +Number of event batches -alias to: cloud.instance.name + +type: long -- -*`meta.cloud.machine_type`*:: +*`beat.stats.libbeat.output.events.dropped`*:: + -- -type: alias +Number of events dropped -alias to: cloud.machine.type + +type: long -- -*`meta.cloud.availability_zone`*:: +*`beat.stats.libbeat.output.events.duplicates`*:: + -- -type: alias +Number of events duplicated -alias to: cloud.availability_zone + +type: long -- -*`meta.cloud.project_id`*:: +*`beat.stats.libbeat.output.events.failed`*:: + -- -type: alias +Number of events failed -alias to: cloud.project.id + +type: long -- -*`meta.cloud.region`*:: +*`beat.stats.libbeat.output.events.toomany`*:: + -- -type: alias +Number of too many events -alias to: cloud.region + +type: long -- -[[exported-fields-cloudfoundry]] -== cloudfoundry fields +*`beat.stats.libbeat.output.events.total`*:: ++ +-- +Total number of events -Cloud Foundry module +type: long +-- [float] -=== cloudfoundry +=== read +Read stats -*`cloudfoundry.type`*:: +*`beat.stats.libbeat.output.read.bytes`*:: + -- -The type of event from Cloud Foundry. Possible values include 'container', 'counter' and 'value'. +Number of bytes read -type: keyword +type: long -- -[float] -=== app - -The application the metric is associated with. - - - -*`cloudfoundry.app.id`*:: +*`beat.stats.libbeat.output.read.errors`*:: + -- -The ID of the application. +Number of read errors -type: keyword +type: long -- [float] -=== container +=== write -`container` contains container metrics from Cloud Foundry. +Write stats -*`cloudfoundry.container.instance_index`*:: +*`beat.stats.libbeat.output.write.bytes`*:: + -- -Index of the instance the metric belongs to. +Number of bytes written type: long -- -*`cloudfoundry.container.cpu.pct`*:: +*`beat.stats.libbeat.output.write.errors`*:: + -- -CPU usage percentage. +Number of write errors -type: float +type: long -- -*`cloudfoundry.container.memory.bytes`*:: -+ --- -Bytes of used memory. +[[exported-fields-ceph]] +== Ceph fields +Ceph module -type: long --- -*`cloudfoundry.container.memory.quota.bytes`*:: +[float] +=== ceph + +`ceph` contains the metrics that were scraped from CEPH. + + + +[float] +=== cluster_disk + +cluster_disk + + + +*`ceph.cluster_disk.available.bytes`*:: + -- -Bytes of available memory. +Available bytes of the cluster type: long +format: bytes + -- -*`cloudfoundry.container.disk.bytes`*:: +*`ceph.cluster_disk.total.bytes`*:: + -- -Bytes of used storage. +Total bytes of the cluster type: long +format: bytes + -- -*`cloudfoundry.container.disk.quota.bytes`*:: +*`ceph.cluster_disk.used.bytes`*:: + -- -Bytes of available storage. +Used bytes of the cluster type: long +format: bytes + -- [float] -=== counter +=== cluster_health -`counter` contains counter metrics from Cloud Foundry. +cluster_health -*`cloudfoundry.counter.name`*:: +*`ceph.cluster_health.overall_status`*:: + -- -The name of the counter. +Overall status of the cluster type: keyword -- -*`cloudfoundry.counter.delta`*:: +*`ceph.cluster_health.timechecks.epoch`*:: + -- -The difference between the last time the counter event occurred. +Map version type: long -- -*`cloudfoundry.counter.total`*:: +*`ceph.cluster_health.timechecks.round.value`*:: + -- -The total value for the counter. +timecheck round type: long -- -[float] -=== value - -`value` contains counter metrics from Cloud Foundry. - - - -*`cloudfoundry.value.name`*:: +*`ceph.cluster_health.timechecks.round.status`*:: + -- -The name of the value. +Status of the round type: keyword -- -*`cloudfoundry.value.unit`*:: -+ --- -The unit of the value. +[float] +=== cluster_status +cluster_status -type: keyword --- -*`cloudfoundry.value.value`*:: +*`ceph.cluster_status.version`*:: + -- -The value of the value. +Ceph Status version -type: float +type: long -- -[[exported-fields-cockroachdb]] -== CockroachDB fields - -CockroachDB module - - - +*`ceph.cluster_status.traffic.read_bytes`*:: ++ +-- +Cluster read throughput per second -[[exported-fields-common]] -== Common fields -Contains common fields available in all event types. +type: long +format: bytes +-- -*`metricset.module`*:: +*`ceph.cluster_status.traffic.write_bytes`*:: + -- -The name of the module that generated the event. +Cluster write throughput per second -type: alias +type: long -alias to: event.module +format: bytes -- -*`metricset.name`*:: +*`ceph.cluster_status.traffic.read_op_per_sec`*:: + -- -The name of the metricset that generated the event. +Cluster read iops per second + +type: long -- -*`metricset.period`*:: +*`ceph.cluster_status.traffic.write_op_per_sec`*:: + -- -Current data collection period for this event in milliseconds. +Cluster write iops per second -type: integer +type: long -- -*`service.address`*:: +*`ceph.cluster_status.misplace.total`*:: + -- -Address of the machine where the service is running. This field may not be present when the data was collected locally. +Cluster misplace pg number + +type: long -- -*`service.hostname`*:: +*`ceph.cluster_status.misplace.objects`*:: + -- -Host name of the machine where the service is running. +Cluster misplace objects number + +type: long -- -*`type`*:: +*`ceph.cluster_status.misplace.ratio`*:: + -- -The document type. Always set to "doc". +Cluster misplace ratio -example: metricsets +type: scaled_float -required: True +format: percent -- -*`systemd.fragment_path`*:: +*`ceph.cluster_status.degraded.total`*:: + -- -the location of the systemd unit path +Cluster degraded pg number -type: keyword + +type: long -- -*`systemd.unit`*:: +*`ceph.cluster_status.degraded.objects`*:: + -- -the unit name of the systemd service +Cluster degraded objects number -type: keyword + +type: long -- -[[exported-fields-consul]] -== consul fields +*`ceph.cluster_status.degraded.ratio`*:: ++ +-- +Cluster degraded ratio -Consul module +type: scaled_float +format: percent +-- -[float] -=== agent +*`ceph.cluster_status.pg.data_bytes`*:: ++ +-- +Cluster pg data bytes -Agent Metricset fetches metrics information from a Consul instance running as Agent +type: long +format: bytes +-- -*`consul.agent.autopilot.healthy`*:: +*`ceph.cluster_status.pg.avail_bytes`*:: + -- -Overall health of the local server cluster +Cluster available bytes -type: boolean + +type: long + +format: bytes -- -[float] -=== runtime +*`ceph.cluster_status.pg.total_bytes`*:: ++ +-- +Cluster total bytes -Runtime related metrics +type: long +format: bytes -*`consul.agent.runtime.sys.bytes`*:: +-- + +*`ceph.cluster_status.pg.used_bytes`*:: + -- -Number of bytes of memory obtained from the OS. +Cluster used bytes + type: long +format: bytes + -- -*`consul.agent.runtime.malloc_count`*:: +*`ceph.cluster_status.pg_state.state_name`*:: + -- -Heap objects allocated +Pg state description + type: long -- -*`consul.agent.runtime.heap_objects`*:: +*`ceph.cluster_status.pg_state.count`*:: + -- -Objects allocated on the heap and is a general memory pressure indicator. This may burst from time to time but should return to a steady state value. +Shows how many pgs are in state of pg_state.state_name + type: long -- -*`consul.agent.runtime.goroutines`*:: +*`ceph.cluster_status.pg_state.version`*:: + -- -Running goroutines and is a general load pressure indicator. This may burst from time to time but should return to a steady state value. +Cluster status version + type: long -- - -*`consul.agent.runtime.alloc.bytes`*:: +*`ceph.cluster_status.osd.full`*:: + -- -Bytes allocated by the Consul process. +Is osd full -type: long + +type: boolean -- -[float] -=== garbage_collector +*`ceph.cluster_status.osd.nearfull`*:: ++ +-- +Is osd near full -Garbage collector metrics +type: boolean -*`consul.agent.runtime.garbage_collector.runs`*:: +-- + +*`ceph.cluster_status.osd.num_osds`*:: + -- -Garbage collector total executions +Shows how many osds in the cluster + type: long -- -[float] -=== pause +*`ceph.cluster_status.osd.num_up_osds`*:: ++ +-- +Shows how many osds are on the state of UP -Time that the garbage collector has paused the app +type: long +-- -*`consul.agent.runtime.garbage_collector.pause.current.ns`*:: +*`ceph.cluster_status.osd.num_in_osds`*:: + -- -Garbage collector pause time in nanoseconds +Shows how many osds are on the state of IN + type: long -- - -*`consul.agent.runtime.garbage_collector.pause.total.ns`*:: +*`ceph.cluster_status.osd.num_remapped_pgs`*:: + -- -Nanoseconds consumed by stop-the-world garbage collection pauses since Consul started. +Shows how many osds are on the state of REMAPPED + type: long -- -[[exported-fields-coredns]] -== coredns fields +*`ceph.cluster_status.osd.epoch`*:: ++ +-- +epoch number -coredns Module +type: long +-- [float] -=== coredns +=== mgr_cluster_disk + +see: cluster_disk -`coredns` contains statistics that were read from coreDNS +[float] +=== mgr_cluster_health + +see: cluster_health [float] -=== stats +=== mgr_osd_perf -Contains statistics related to the coreDNS service +OSD performance metrics of Ceph cluster -*`coredns.stats.panic.count`*:: +*`ceph.mgr_osd_perf.id`*:: + -- -Total number of panics - +OSD ID type: long -- -*`coredns.stats.dns.request.count`*:: +*`ceph.mgr_osd_perf.stats.commit_latency_ms`*:: + -- -Total query count - +Commit latency in ms type: long -- -*`coredns.stats.dns.request.duration.ns.bucket.*`*:: +*`ceph.mgr_osd_perf.stats.apply_latency_ms`*:: + -- -Request duration histogram buckets in nanoseconds - +Apply latency in ms -type: object +type: long -- -*`coredns.stats.dns.request.duration.ns.sum`*:: +*`ceph.mgr_osd_perf.stats.commit_latency_ns`*:: + -- -Requests duration, sum of durations in nanoseconds - +Commit latency in ns type: long -format: duration - -- -*`coredns.stats.dns.request.duration.ns.count`*:: +*`ceph.mgr_osd_perf.stats.apply_latency_ns`*:: + -- -Requests duration, number of requests - +Apply latency in ns type: long -- -*`coredns.stats.dns.request.size.bytes.bucket.*`*:: -+ --- -Request Size histogram buckets +[float] +=== mgr_osd_pool_stats +OSD pool stats of Ceph cluster -type: object --- -*`coredns.stats.dns.request.size.bytes.sum`*:: +*`ceph.mgr_osd_pool_stats.pool_name`*:: + -- -Request Size histogram sum - +Pool name -type: long +type: keyword -- -*`coredns.stats.dns.request.size.bytes.count`*:: +*`ceph.mgr_osd_pool_stats.pool_id`*:: + -- -Request Size histogram count - +Pool ID type: long -- -*`coredns.stats.dns.request.do.count`*:: +*`ceph.mgr_osd_pool_stats.client_io_rate`*:: + -- -Number of queries that have the DO bit set - +Client I/O rates -type: long +type: object -- -*`coredns.stats.dns.request.type.count`*:: -+ --- -Counter of queries per zone and type +[float] +=== mgr_osd_tree +see: osd_tree -type: long --- +[float] +=== mgr_pool_disk -*`coredns.stats.type`*:: -+ --- -Holds the query type of the request +see: pool_disk -type: keyword +[float] +=== monitor_health --- +monitor_health stats data -*`coredns.stats.dns.response.rcode.count`*:: + + +*`ceph.monitor_health.available.pct`*:: + -- -Counter of responses per zone and rcode +Available percent of the MON type: long -- -*`coredns.stats.rcode`*:: +*`ceph.monitor_health.health`*:: + -- -Holds the rcode of the response +Health of the MON type: keyword -- -*`coredns.stats.family`*:: +*`ceph.monitor_health.available.kb`*:: + -- -The address family of the transport (1 = IP (IP version 4), 2 = IP6 (IP version 6)) +Available KB of the MON -type: keyword +type: long -- -*`coredns.stats.dns.response.size.bytes.bucket.*`*:: +*`ceph.monitor_health.total.kb`*:: + -- -Response Size histogram buckets +Total KB of the MON -type: object +type: long -- -*`coredns.stats.dns.response.size.bytes.sum`*:: +*`ceph.monitor_health.used.kb`*:: + -- -Response Size histogram sum +Used KB of the MON type: long -- -*`coredns.stats.dns.response.size.bytes.count`*:: +*`ceph.monitor_health.last_updated`*:: + -- -Response Size histogram count +Time when was updated -type: long +type: date -- -*`coredns.stats.server`*:: +*`ceph.monitor_health.name`*:: + -- -The server responsible for the request +Name of the MON type: keyword -- -*`coredns.stats.zone`*:: +*`ceph.monitor_health.store_stats.log.bytes`*:: + -- -The zonename used for the request/response +Log bytes of MON -type: keyword +type: long + +format: bytes -- -*`coredns.stats.proto`*:: +*`ceph.monitor_health.store_stats.misc.bytes`*:: + -- -The transport of the response ("udp" or "tcp") +Misc bytes of MON -type: keyword +type: long + +format: bytes -- -*`coredns.stats.dns.cache.hits.count`*:: +*`ceph.monitor_health.store_stats.sst.bytes`*:: + -- -Cache hits count for the cache plugin +SST bytes of MON type: long +format: bytes + -- -*`coredns.stats.dns.cache.misses.count`*:: +*`ceph.monitor_health.store_stats.total.bytes`*:: + -- -Cache misses count for the cache plugin +Total bytes of MON type: long --- - -[[exported-fields-couchbase]] -== Couchbase fields - -Metrics collected from Couchbase servers. - +format: bytes +-- -[float] -=== couchbase +*`ceph.monitor_health.store_stats.last_updated`*:: ++ +-- +Last updated -`couchbase` contains the metrics that were scraped from Couchbase. +type: long +-- [float] -=== bucket +=== osd_df -Couchbase bucket metrics. +ceph osd disk usage information -*`couchbase.bucket.name`*:: +*`ceph.osd_df.id`*:: + -- -Name of the bucket. +osd node id -type: keyword +type: long -- -*`couchbase.bucket.type`*:: +*`ceph.osd_df.name`*:: + -- -Type of the bucket. +osd node name type: keyword -- -*`couchbase.bucket.data.used.bytes`*:: +*`ceph.osd_df.device_class`*:: + -- -Size of user data within buckets of the specified state that are resident in RAM. - +osd node type, illegal type include hdd, ssd etc. -type: long -format: bytes +type: keyword -- -*`couchbase.bucket.disk.fetches`*:: +*`ceph.osd_df.total.byte`*:: + -- -Number of disk fetches. +osd disk total volume type: long +format: bytes + -- -*`couchbase.bucket.disk.used.bytes`*:: +*`ceph.osd_df.used.byte`*:: + -- -Amount of disk used (bytes). +osd disk usage volume type: long @@ -5619,10 +5639,10 @@ format: bytes -- -*`couchbase.bucket.memory.used.bytes`*:: +*`ceph.osd_df.available.bytes`*:: + -- -Amount of memory used by the bucket (bytes). +osd disk available volume type: long @@ -5631,22 +5651,20 @@ format: bytes -- -*`couchbase.bucket.quota.ram.bytes`*:: +*`ceph.osd_df.pg_num`*:: + -- -Amount of RAM used by the bucket (bytes). +shows how many pg located on this osd type: long -format: bytes - -- -*`couchbase.bucket.quota.use.pct`*:: +*`ceph.osd_df.used.pct`*:: + -- -Percentage of RAM used (for active objects) against the configured bucket size (%). +osd disk usage percentage type: scaled_float @@ -5655,199 +5673,174 @@ format: percent -- -*`couchbase.bucket.ops_per_sec`*:: -+ --- -Number of operations per second. +[float] +=== osd_tree +ceph osd tree info -type: long --- -*`couchbase.bucket.item_count`*:: +*`ceph.osd_tree.id`*:: + -- -Number of items associated with the bucket. +osd or bucket node id type: long -- -[float] -=== cluster +*`ceph.osd_tree.name`*:: ++ +-- +osd or bucket node name -Couchbase cluster metrics. +type: keyword +-- -*`couchbase.cluster.hdd.free.bytes`*:: +*`ceph.osd_tree.type`*:: + -- -Free hard drive space in the cluster (bytes). - +osd or bucket node type, illegal type include osd, host, root etc. -type: long -format: bytes +type: keyword -- -*`couchbase.cluster.hdd.quota.total.bytes`*:: +*`ceph.osd_tree.type_id`*:: + -- -Hard drive quota total for the cluster (bytes). +osd or bucket node typeID type: long -format: bytes - -- -*`couchbase.cluster.hdd.total.bytes`*:: +*`ceph.osd_tree.children`*:: + -- -Total hard drive space available to the cluster (bytes). - +bucket children list, separated by comma. -type: long -format: bytes +type: keyword -- -*`couchbase.cluster.hdd.used.value.bytes`*:: +*`ceph.osd_tree.crush_weight`*:: + -- -Hard drive space used by the cluster (bytes). - +osd node crush weight -type: long -format: bytes +type: float -- -*`couchbase.cluster.hdd.used.by_data.bytes`*:: +*`ceph.osd_tree.depth`*:: + -- -Hard drive space used by the data in the cluster (bytes). +node depth type: long -format: bytes - -- -*`couchbase.cluster.max_bucket_count`*:: +*`ceph.osd_tree.exists`*:: + -- -Max bucket count setting. +is node still exist or not(1-yes, 0-no) -type: long +type: boolean -- -*`couchbase.cluster.quota.index_memory.mb`*:: +*`ceph.osd_tree.primary_affinity`*:: + -- -Memory quota setting for the Index service (Mbyte). +the weight of reading data from primary osd -type: long +type: float -- -*`couchbase.cluster.quota.memory.mb`*:: +*`ceph.osd_tree.reweight`*:: + -- -Memory quota setting for the cluster (Mbyte). +the reweight of osd type: long -- -*`couchbase.cluster.ram.quota.total.value.bytes`*:: +*`ceph.osd_tree.status`*:: + -- -RAM quota total for the cluster (bytes). +status of osd, it should be up or down -type: long - -format: bytes +type: keyword -- -*`couchbase.cluster.ram.quota.total.per_node.bytes`*:: +*`ceph.osd_tree.device_class`*:: + -- -RAM quota used by the current node in the cluster (bytes). - +the device class of osd, like hdd, ssd etc. -type: long -format: bytes +type: keyword -- -*`couchbase.cluster.ram.quota.used.value.bytes`*:: +*`ceph.osd_tree.father`*:: + -- -RAM quota used by the cluster (bytes). - - -type: long +the parent node of this osd or bucket node -format: bytes --- +type: keyword -*`couchbase.cluster.ram.quota.used.per_node.bytes`*:: -+ -- -Ram quota used by the current node in the cluster (bytes) +[float] +=== pool_disk -type: long +pool_disk -format: bytes --- -*`couchbase.cluster.ram.total.bytes`*:: +*`ceph.pool_disk.id`*:: + -- -Total RAM available to cluster (bytes). +Id of the pool type: long -format: bytes - -- -*`couchbase.cluster.ram.used.value.bytes`*:: +*`ceph.pool_disk.name`*:: + -- -RAM used by the cluster (bytes). - +Name of the pool -type: long -format: bytes +type: keyword -- -*`couchbase.cluster.ram.used.by_data.bytes`*:: +*`ceph.pool_disk.stats.available.bytes`*:: + -- -RAM used by the data in the cluster (bytes). +Available bytes of the pool type: long @@ -5856,27 +5849,20 @@ format: bytes -- -[float] -=== node - -Couchbase node metrics. - - - -*`couchbase.node.cmd_get`*:: +*`ceph.pool_disk.stats.objects`*:: + -- -Number of get commands +Number of objects of the pool type: long -- -*`couchbase.node.couch.docs.disk_size.bytes`*:: +*`ceph.pool_disk.stats.used.bytes`*:: + -- -Amount of disk space used by Couch docs (bytes). +Used bytes of the pool type: long @@ -5885,916 +5871,894 @@ format: bytes -- -*`couchbase.node.couch.docs.data_size.bytes`*:: +*`ceph.pool_disk.stats.used.kb`*:: + -- -Data size of Couch docs associated with a node (bytes). +Used kb of the pool type: long -format: bytes - -- -*`couchbase.node.couch.spatial.data_size.bytes`*:: -+ --- -Size of object data for spatial views (bytes). +[[exported-fields-cloud]] +== Cloud provider metadata fields +Metadata from cloud providers added by the add_cloud_metadata processor. -type: long --- -*`couchbase.node.couch.spatial.disk_size.bytes`*:: +*`cloud.project.id`*:: + -- -Amount of disk space used by spatial views (bytes). +Name of the project in Google Cloud. -type: long +example: project-x -- -*`couchbase.node.couch.views.disk_size.bytes`*:: +*`cloud.image.id`*:: + -- -Amount of disk space used by Couch views (bytes). +Image ID for the cloud instance. -type: long +example: ami-abcd1234 -- -*`couchbase.node.couch.views.data_size.bytes`*:: +*`meta.cloud.provider`*:: + -- -Size of object data for Couch views (bytes). - +type: alias -type: long +alias to: cloud.provider -- -*`couchbase.node.cpu_utilization_rate.pct`*:: +*`meta.cloud.instance_id`*:: + -- -The CPU utilization rate (%). - +type: alias -type: scaled_float +alias to: cloud.instance.id -- -*`couchbase.node.current_items.value`*:: +*`meta.cloud.instance_name`*:: + -- -Number of current items. - +type: alias -type: long +alias to: cloud.instance.name -- -*`couchbase.node.current_items.total`*:: +*`meta.cloud.machine_type`*:: + -- -Total number of items associated with the node. - +type: alias -type: long +alias to: cloud.machine.type -- -*`couchbase.node.ep_bg_fetched`*:: +*`meta.cloud.availability_zone`*:: + -- -Number of disk fetches performed since the server was started. - +type: alias -type: long +alias to: cloud.availability_zone -- -*`couchbase.node.get_hits`*:: +*`meta.cloud.project_id`*:: + -- -Number of get hits. - +type: alias -type: long +alias to: cloud.project.id -- -*`couchbase.node.hostname`*:: +*`meta.cloud.region`*:: + -- -The hostname of the node. - +type: alias -type: keyword +alias to: cloud.region -- -*`couchbase.node.mcd_memory.allocated.bytes`*:: -+ --- -Amount of memcached memory allocated (bytes). +[[exported-fields-cloudfoundry]] +== cloudfoundry fields +Cloud Foundry module -type: long -format: bytes --- +[float] +=== cloudfoundry -*`couchbase.node.mcd_memory.reserved.bytes`*:: + + + +*`cloudfoundry.type`*:: + -- -Amount of memcached memory reserved (bytes). +The type of event from Cloud Foundry. Possible values include 'container', 'counter' and 'value'. -type: long +type: keyword -- -*`couchbase.node.memory.free.bytes`*:: -+ --- -Amount of memory free for the node (bytes). +[float] +=== app +The application the metric is associated with. -type: long --- -*`couchbase.node.memory.total.bytes`*:: +*`cloudfoundry.app.id`*:: + -- -Total memory available to the node (bytes). +The ID of the application. -type: long +type: keyword -- -*`couchbase.node.memory.used.bytes`*:: -+ --- -Memory used by the node (bytes). +[float] +=== container +`container` contains container metrics from Cloud Foundry. -type: long --- -*`couchbase.node.ops`*:: +*`cloudfoundry.container.instance_index`*:: + -- -Number of operations performed on Couchbase. +Index of the instance the metric belongs to. type: long -- -*`couchbase.node.swap.total.bytes`*:: +*`cloudfoundry.container.cpu.pct`*:: + -- -Total swap size allocated (bytes). +CPU usage percentage. -type: long +type: float -- -*`couchbase.node.swap.used.bytes`*:: +*`cloudfoundry.container.memory.bytes`*:: + -- -Amount of swap space used (bytes). +Bytes of used memory. type: long -- -*`couchbase.node.uptime.sec`*:: +*`cloudfoundry.container.memory.quota.bytes`*:: + -- -Time during which the node was in operation (sec). +Bytes of available memory. type: long -- -*`couchbase.node.vb_replica_curr_items`*:: +*`cloudfoundry.container.disk.bytes`*:: + -- -Number of items/documents that are replicas. +Bytes of used storage. type: long -- -[[exported-fields-couchdb]] -== CouchDB fields - -couchdb module - - +*`cloudfoundry.container.disk.quota.bytes`*:: ++ +-- +Bytes of available storage. -[float] -=== couchdb -Couchdb metrics +type: long +-- [float] -=== server +=== counter -Contains CouchDB server stats +`counter` contains counter metrics from Cloud Foundry. -[float] -=== httpd +*`cloudfoundry.counter.name`*:: ++ +-- +The name of the counter. -HTTP statistics +type: keyword +-- -*`couchdb.server.httpd.view_reads`*:: +*`cloudfoundry.counter.delta`*:: + -- -Number of view reads +The difference between the last time the counter event occurred. type: long -- -*`couchdb.server.httpd.bulk_requests`*:: +*`cloudfoundry.counter.total`*:: + -- -Number of bulk requests +The total value for the counter. type: long -- -*`couchdb.server.httpd.clients_requesting_changes`*:: -+ --- -Number of clients for continuous _changes +[float] +=== value +`value` contains counter metrics from Cloud Foundry. -type: long --- -*`couchdb.server.httpd.temporary_view_reads`*:: +*`cloudfoundry.value.name`*:: + -- -Number of temporary view reads +The name of the value. -type: long +type: keyword -- -*`couchdb.server.httpd.requests`*:: +*`cloudfoundry.value.unit`*:: + -- -Number of HTTP requests +The unit of the value. -type: long +type: keyword -- -[float] -=== httpd_request_methods +*`cloudfoundry.value.value`*:: ++ +-- +The value of the value. -HTTP request methods +type: float +-- -*`couchdb.server.httpd_request_methods.COPY`*:: +[[exported-fields-cockroachdb]] +== CockroachDB fields + +CockroachDB module + + + + +[[exported-fields-common]] +== Common fields + +Contains common fields available in all event types. + + + +*`metricset.module`*:: + -- -Number of HTTP COPY requests +The name of the module that generated the event. -type: long +type: alias + +alias to: event.module -- -*`couchdb.server.httpd_request_methods.HEAD`*:: +*`metricset.name`*:: + -- -Number of HTTP HEAD requests - +The name of the metricset that generated the event. -type: long -- -*`couchdb.server.httpd_request_methods.POST`*:: +*`metricset.period`*:: + -- -Number of HTTP POST requests +Current data collection period for this event in milliseconds. -type: long +type: integer -- -*`couchdb.server.httpd_request_methods.DELETE`*:: +*`service.address`*:: + -- -Number of HTTP DELETE requests +Address of the machine where the service is running. This field may not be present when the data was collected locally. -type: long +-- +*`service.hostname`*:: ++ -- +Host name of the machine where the service is running. -*`couchdb.server.httpd_request_methods.GET`*:: + +-- + +*`type`*:: + -- -Number of HTTP GET requests +The document type. Always set to "doc". -type: long +example: metricsets + +required: True -- -*`couchdb.server.httpd_request_methods.PUT`*:: +*`systemd.fragment_path`*:: + -- -Number of HTTP PUT requests +the location of the systemd unit path +type: keyword -type: long +-- + +*`systemd.unit`*:: ++ +-- +the unit name of the systemd service + +type: keyword -- +[[exported-fields-consul]] +== consul fields + +Consul module + + + + [float] -=== httpd_status_codes +=== agent -HTTP status codes statistics +Agent Metricset fetches metrics information from a Consul instance running as Agent -*`couchdb.server.httpd_status_codes.200`*:: + +*`consul.agent.autopilot.healthy`*:: + -- -Number of HTTP 200 OK responses - +Overall health of the local server cluster -type: long +type: boolean -- -*`couchdb.server.httpd_status_codes.201`*:: -+ --- -Number of HTTP 201 Created responses +[float] +=== runtime +Runtime related metrics -type: long --- -*`couchdb.server.httpd_status_codes.202`*:: +*`consul.agent.runtime.sys.bytes`*:: + -- -Number of HTTP 202 Accepted responses - +Number of bytes of memory obtained from the OS. type: long -- -*`couchdb.server.httpd_status_codes.301`*:: +*`consul.agent.runtime.malloc_count`*:: + -- -Number of HTTP 301 Moved Permanently responses - +Heap objects allocated type: long -- -*`couchdb.server.httpd_status_codes.304`*:: +*`consul.agent.runtime.heap_objects`*:: + -- -Number of HTTP 304 Not Modified responses - +Objects allocated on the heap and is a general memory pressure indicator. This may burst from time to time but should return to a steady state value. type: long -- -*`couchdb.server.httpd_status_codes.400`*:: +*`consul.agent.runtime.goroutines`*:: + -- -Number of HTTP 400 Bad Request responses - +Running goroutines and is a general load pressure indicator. This may burst from time to time but should return to a steady state value. type: long -- -*`couchdb.server.httpd_status_codes.401`*:: + +*`consul.agent.runtime.alloc.bytes`*:: + -- -Number of HTTP 401 Unauthorized responses - +Bytes allocated by the Consul process. type: long -- -*`couchdb.server.httpd_status_codes.403`*:: +[float] +=== garbage_collector + +Garbage collector metrics + + +*`consul.agent.runtime.garbage_collector.runs`*:: + -- -Number of HTTP 403 Forbidden responses - +Garbage collector total executions type: long -- -*`couchdb.server.httpd_status_codes.404`*:: -+ --- -Number of HTTP 404 Not Found responses +[float] +=== pause +Time that the garbage collector has paused the app -type: long --- -*`couchdb.server.httpd_status_codes.405`*:: +*`consul.agent.runtime.garbage_collector.pause.current.ns`*:: + -- -Number of HTTP 405 Method Not Allowed responses - +Garbage collector pause time in nanoseconds type: long -- -*`couchdb.server.httpd_status_codes.409`*:: + +*`consul.agent.runtime.garbage_collector.pause.total.ns`*:: + -- -Number of HTTP 409 Conflict responses - +Nanoseconds consumed by stop-the-world garbage collection pauses since Consul started. type: long -- -*`couchdb.server.httpd_status_codes.412`*:: -+ --- -Number of HTTP 412 Precondition Failed responses +[[exported-fields-coredns]] +== coredns fields +coredns Module -type: long --- -*`couchdb.server.httpd_status_codes.500`*:: -+ --- -Number of HTTP 500 Internal Server Error responses +[float] +=== coredns +`coredns` contains statistics that were read from coreDNS -type: long --- [float] -=== couchdb +=== stats -couchdb statistics +Contains statistics related to the coreDNS service -*`couchdb.server.couchdb.database_writes`*:: +*`coredns.stats.panic.count`*:: + -- -Number of times a database was changed +Total number of panics type: long -- -*`couchdb.server.couchdb.open_databases`*:: +*`coredns.stats.dns.request.count`*:: + -- -Number of open databases +Total query count type: long -- -*`couchdb.server.couchdb.auth_cache_misses`*:: +*`coredns.stats.dns.request.duration.ns.bucket.*`*:: + -- -Number of authentication cache misses +Request duration histogram buckets in nanoseconds -type: long +type: object -- -*`couchdb.server.couchdb.request_time`*:: +*`coredns.stats.dns.request.duration.ns.sum`*:: + -- -Length of a request inside CouchDB without MochiWeb +Requests duration, sum of durations in nanoseconds type: long +format: duration + -- -*`couchdb.server.couchdb.database_reads`*:: +*`coredns.stats.dns.request.duration.ns.count`*:: + -- -Number of times a document was read from a database +Requests duration, number of requests type: long -- -*`couchdb.server.couchdb.auth_cache_hits`*:: +*`coredns.stats.dns.request.size.bytes.bucket.*`*:: + -- -Number of authentication cache hits +Request Size histogram buckets -type: long +type: object -- -*`couchdb.server.couchdb.open_os_files`*:: +*`coredns.stats.dns.request.size.bytes.sum`*:: + -- -Number of file descriptors CouchDB has open +Request Size histogram sum type: long -- -[[exported-fields-docker-processor]] -== Docker fields - -Docker stats collected from Docker. +*`coredns.stats.dns.request.size.bytes.count`*:: ++ +-- +Request Size histogram count +type: long +-- -*`docker.container.id`*:: +*`coredns.stats.dns.request.do.count`*:: + -- -type: alias +Number of queries that have the DO bit set -alias to: container.id + +type: long -- -*`docker.container.image`*:: +*`coredns.stats.dns.request.type.count`*:: + -- -type: alias +Counter of queries per zone and type -alias to: container.image.name + +type: long -- -*`docker.container.name`*:: +*`coredns.stats.type`*:: + -- -type: alias +Holds the query type of the request -alias to: container.name --- - -*`docker.container.labels`*:: -+ --- -Image labels. - - -type: object +type: keyword -- -[[exported-fields-docker]] -== Docker fields - -Docker stats collected from Docker. - - - -[float] -=== docker - -Information and statistics about docker's running containers. - - - -[float] -=== container - -Docker container metrics. - - - -*`docker.container.command`*:: +*`coredns.stats.dns.response.rcode.count`*:: + -- -Command that was executed in the Docker container. +Counter of responses per zone and rcode -type: keyword +type: long -- -*`docker.container.created`*:: +*`coredns.stats.rcode`*:: + -- -Date when the container was created. +Holds the rcode of the response -type: date +type: keyword -- -*`docker.container.status`*:: +*`coredns.stats.family`*:: + -- -Container status. +The address family of the transport (1 = IP (IP version 4), 2 = IP6 (IP version 6)) type: keyword -- -*`docker.container.ip_addresses`*:: +*`coredns.stats.dns.response.size.bytes.bucket.*`*:: + -- -Container IP addresses. +Response Size histogram buckets -type: ip +type: object -- -[float] -=== size - -Container size metrics. - - - -*`docker.container.size.root_fs`*:: +*`coredns.stats.dns.response.size.bytes.sum`*:: + -- -Total size of all the files in the container. +Response Size histogram sum type: long -- -*`docker.container.size.rw`*:: +*`coredns.stats.dns.response.size.bytes.count`*:: + -- -Size of the files that have been created or changed since creation. +Response Size histogram count type: long -- -*`docker.container.tags`*:: +*`coredns.stats.server`*:: + -- -Image tags. +The server responsible for the request type: keyword -- -[float] -=== cpu +*`coredns.stats.zone`*:: ++ +-- +The zonename used for the request/response -Runtime CPU metrics. +type: keyword +-- -*`docker.cpu.kernel.pct`*:: +*`coredns.stats.proto`*:: + -- -Percentage of time in kernel space. - +The transport of the response ("udp" or "tcp") -type: scaled_float -format: percent +type: keyword -- -*`docker.cpu.kernel.norm.pct`*:: +*`coredns.stats.dns.cache.hits.count`*:: + -- -Percentage of time in kernel space normalized by the number of CPU cores. - +Cache hits count for the cache plugin -type: scaled_float -format: percent +type: long -- -*`docker.cpu.kernel.ticks`*:: +*`coredns.stats.dns.cache.misses.count`*:: + -- -CPU ticks in kernel space. +Cache misses count for the cache plugin type: long -- -*`docker.cpu.system.pct`*:: -+ --- -Percentage of total CPU time in the system. +[[exported-fields-couchbase]] +== Couchbase fields +Metrics collected from Couchbase servers. -type: scaled_float -format: percent --- +[float] +=== couchbase -*`docker.cpu.system.norm.pct`*:: -+ --- -Percentage of total CPU time in the system normalized by the number of CPU cores. +`couchbase` contains the metrics that were scraped from Couchbase. -type: scaled_float -format: percent +[float] +=== bucket --- +Couchbase bucket metrics. -*`docker.cpu.system.ticks`*:: + + +*`couchbase.bucket.name`*:: + -- -CPU system ticks. +Name of the bucket. -type: long +type: keyword -- -*`docker.cpu.user.pct`*:: +*`couchbase.bucket.type`*:: + -- -Percentage of time in user space. - +Type of the bucket. -type: scaled_float -format: percent +type: keyword -- -*`docker.cpu.user.norm.pct`*:: +*`couchbase.bucket.data.used.bytes`*:: + -- -Percentage of time in user space normalized by the number of CPU cores. +Size of user data within buckets of the specified state that are resident in RAM. -type: scaled_float +type: long -format: percent +format: bytes -- -*`docker.cpu.user.ticks`*:: +*`couchbase.bucket.disk.fetches`*:: + -- -CPU ticks in user space. +Number of disk fetches. type: long -- -*`docker.cpu.total.pct`*:: +*`couchbase.bucket.disk.used.bytes`*:: + -- -Total CPU usage. +Amount of disk used (bytes). -type: scaled_float +type: long -format: percent +format: bytes -- -*`docker.cpu.total.norm.pct`*:: +*`couchbase.bucket.memory.used.bytes`*:: + -- -Total CPU usage normalized by the number of CPU cores. +Amount of memory used by the bucket (bytes). -type: scaled_float +type: long -format: percent +format: bytes -- -*`docker.cpu.core.*.pct`*:: +*`couchbase.bucket.quota.ram.bytes`*:: + -- -Percentage of CPU time in this core. +Amount of RAM used by the bucket (bytes). -type: object +type: long -format: percent +format: bytes -- -*`docker.cpu.core.*.norm.pct`*:: +*`couchbase.bucket.quota.use.pct`*:: + -- -Percentage of CPU time in this core, normalized by the number of CPU cores. +Percentage of RAM used (for active objects) against the configured bucket size (%). -type: object +type: scaled_float format: percent -- -*`docker.cpu.core.*.ticks`*:: +*`couchbase.bucket.ops_per_sec`*:: + -- -Number of CPU ticks in this core. +Number of operations per second. -type: object +type: long -- -[float] -=== diskio +*`couchbase.bucket.item_count`*:: ++ +-- +Number of items associated with the bucket. -Disk I/O metrics. +type: long +-- [float] -=== read +=== cluster -Accumulated reads during the life of the container +Couchbase cluster metrics. -*`docker.diskio.read.ops`*:: +*`couchbase.cluster.hdd.free.bytes`*:: + -- -Number of reads during the life of the container +Free hard drive space in the cluster (bytes). type: long +format: bytes + -- -*`docker.diskio.read.bytes`*:: +*`couchbase.cluster.hdd.quota.total.bytes`*:: + -- -Bytes read during the life of the container +Hard drive quota total for the cluster (bytes). type: long @@ -6803,80 +6767,76 @@ format: bytes -- -*`docker.diskio.read.rate`*:: +*`couchbase.cluster.hdd.total.bytes`*:: + -- -Number of current reads per second +Total hard drive space available to the cluster (bytes). type: long +format: bytes + -- -*`docker.diskio.read.service_time`*:: +*`couchbase.cluster.hdd.used.value.bytes`*:: + -- -Total time to service IO requests, in nanoseconds +Hard drive space used by the cluster (bytes). type: long +format: bytes + -- -*`docker.diskio.read.wait_time`*:: +*`couchbase.cluster.hdd.used.by_data.bytes`*:: + -- -Total time requests spent waiting in queues for service, in nanoseconds +Hard drive space used by the data in the cluster (bytes). type: long +format: bytes + -- -*`docker.diskio.read.queued`*:: +*`couchbase.cluster.max_bucket_count`*:: + -- -Total number of queued requests +Max bucket count setting. type: long -- -*`docker.diskio.reads`*:: +*`couchbase.cluster.quota.index_memory.mb`*:: + -- - -deprecated:[6.4] - -Number of current reads per second +Memory quota setting for the Index service (Mbyte). -type: scaled_float +type: long -- -[float] -=== write - -Accumulated writes during the life of the container - - - -*`docker.diskio.write.ops`*:: +*`couchbase.cluster.quota.memory.mb`*:: + -- -Number of writes during the life of the container +Memory quota setting for the cluster (Mbyte). type: long -- -*`docker.diskio.write.bytes`*:: +*`couchbase.cluster.ram.quota.total.value.bytes`*:: + -- -Bytes written during the life of the container +RAM quota total for the cluster (bytes). type: long @@ -6885,80 +6845,99 @@ format: bytes -- -*`docker.diskio.write.rate`*:: +*`couchbase.cluster.ram.quota.total.per_node.bytes`*:: + -- -Number of current writes per second +RAM quota used by the current node in the cluster (bytes). type: long +format: bytes + -- -*`docker.diskio.write.service_time`*:: +*`couchbase.cluster.ram.quota.used.value.bytes`*:: + -- -Total time to service IO requests, in nanoseconds +RAM quota used by the cluster (bytes). type: long +format: bytes + -- -*`docker.diskio.write.wait_time`*:: +*`couchbase.cluster.ram.quota.used.per_node.bytes`*:: + -- -Total time requests spent waiting in queues for service, in nanoseconds +Ram quota used by the current node in the cluster (bytes) type: long +format: bytes + -- -*`docker.diskio.write.queued`*:: +*`couchbase.cluster.ram.total.bytes`*:: + -- -Total number of queued requests +Total RAM available to cluster (bytes). type: long +format: bytes + -- -*`docker.diskio.writes`*:: +*`couchbase.cluster.ram.used.value.bytes`*:: + -- +RAM used by the cluster (bytes). -deprecated:[6.4] -Number of current writes per second +type: long +format: bytes -type: scaled_float +-- + +*`couchbase.cluster.ram.used.by_data.bytes`*:: ++ +-- +RAM used by the data in the cluster (bytes). + + +type: long + +format: bytes -- [float] -=== summary +=== node -Accumulated reads and writes during the life of the container +Couchbase node metrics. -*`docker.diskio.summary.ops`*:: +*`couchbase.node.cmd_get`*:: + -- -Number of I/O operations during the life of the container +Number of get commands type: long -- -*`docker.diskio.summary.bytes`*:: +*`couchbase.node.couch.docs.disk_size.bytes`*:: + -- -Bytes read and written during the life of the container +Amount of disk space used by Couch docs (bytes). type: long @@ -6967,377 +6946,358 @@ format: bytes -- -*`docker.diskio.summary.rate`*:: +*`couchbase.node.couch.docs.data_size.bytes`*:: + -- -Number of current operations per second +Data size of Couch docs associated with a node (bytes). type: long +format: bytes + -- -*`docker.diskio.summary.service_time`*:: +*`couchbase.node.couch.spatial.data_size.bytes`*:: + -- -Total time to service IO requests, in nanoseconds +Size of object data for spatial views (bytes). type: long -- -*`docker.diskio.summary.wait_time`*:: +*`couchbase.node.couch.spatial.disk_size.bytes`*:: + -- -Total time requests spent waiting in queues for service, in nanoseconds +Amount of disk space used by spatial views (bytes). type: long -- -*`docker.diskio.summary.queued`*:: +*`couchbase.node.couch.views.disk_size.bytes`*:: + -- -Total number of queued requests +Amount of disk space used by Couch views (bytes). type: long -- -*`docker.diskio.total`*:: +*`couchbase.node.couch.views.data_size.bytes`*:: + -- - -deprecated:[6.4] - -Number of reads and writes per second +Size of object data for Couch views (bytes). -type: scaled_float +type: long -- -[float] -=== event +*`couchbase.node.cpu_utilization_rate.pct`*:: ++ +-- +The CPU utilization rate (%). -Docker event +type: scaled_float +-- -*`docker.event.status`*:: +*`couchbase.node.current_items.value`*:: + -- -Event status +Number of current items. -type: keyword +type: long -- -*`docker.event.id`*:: +*`couchbase.node.current_items.total`*:: + -- -Event id when available +Total number of items associated with the node. -type: keyword +type: long -- -*`docker.event.from`*:: +*`couchbase.node.ep_bg_fetched`*:: + -- -Event source +Number of disk fetches performed since the server was started. -type: keyword +type: long -- -*`docker.event.type`*:: +*`couchbase.node.get_hits`*:: + -- -The type of object emitting the event +Number of get hits. -type: keyword +type: long -- -*`docker.event.action`*:: +*`couchbase.node.hostname`*:: + -- -The type of event +The hostname of the node. type: keyword -- -[float] -=== actor +*`couchbase.node.mcd_memory.allocated.bytes`*:: ++ +-- +Amount of memcached memory allocated (bytes). -Actor +type: long + +format: bytes +-- -*`docker.event.actor.id`*:: +*`couchbase.node.mcd_memory.reserved.bytes`*:: + -- -The ID of the object emitting the event +Amount of memcached memory reserved (bytes). -type: keyword +type: long -- -*`docker.event.actor.attributes`*:: +*`couchbase.node.memory.free.bytes`*:: + -- -Various key/value attributes of the object, depending on its type +Amount of memory free for the node (bytes). -type: object +type: long -- -[float] -=== healthcheck +*`couchbase.node.memory.total.bytes`*:: ++ +-- +Total memory available to the node (bytes). -Docker healthcheck metrics. -Healthcheck data will only be available from docker containers where the docker `HEALTHCHECK` instruction has been used to build the docker image. +type: long +-- -*`docker.healthcheck.failingstreak`*:: +*`couchbase.node.memory.used.bytes`*:: + -- -concurent failed check +Memory used by the node (bytes). -type: integer +type: long -- -*`docker.healthcheck.status`*:: +*`couchbase.node.ops`*:: + -- -Healthcheck status code +Number of operations performed on Couchbase. -type: keyword +type: long -- -[float] -=== event +*`couchbase.node.swap.total.bytes`*:: ++ +-- +Total swap size allocated (bytes). -event fields. +type: long +-- -*`docker.healthcheck.event.end_date`*:: +*`couchbase.node.swap.used.bytes`*:: + -- -Healthcheck end date +Amount of swap space used (bytes). -type: date +type: long -- -*`docker.healthcheck.event.start_date`*:: +*`couchbase.node.uptime.sec`*:: + -- -Healthcheck start date +Time during which the node was in operation (sec). -type: date +type: long -- -*`docker.healthcheck.event.output`*:: +*`couchbase.node.vb_replica_curr_items`*:: + -- -Healthcheck output +Number of items/documents that are replicas. -type: keyword +type: long -- -*`docker.healthcheck.event.exit_code`*:: -+ --- -Healthcheck status code +[[exported-fields-couchdb]] +== CouchDB fields +couchdb module -type: integer --- [float] -=== image - -Docker image metrics. +=== couchdb +Couchdb metrics [float] -=== id +=== server -The image layers identifier. +Contains CouchDB server stats -*`docker.image.id.current`*:: -+ --- -Unique image identifier given upon its creation. +[float] +=== httpd +HTTP statistics -type: keyword --- -*`docker.image.id.parent`*:: +*`couchdb.server.httpd.view_reads`*:: + -- -Identifier of the image, if it exists, from which the current image directly descends. +Number of view reads -type: keyword +type: long -- -*`docker.image.created`*:: +*`couchdb.server.httpd.bulk_requests`*:: + -- -Date and time when the image was created. +Number of bulk requests -type: date +type: long -- -[float] -=== size +*`couchdb.server.httpd.clients_requesting_changes`*:: ++ +-- +Number of clients for continuous _changes -Image size layers. +type: long +-- -*`docker.image.size.virtual`*:: +*`couchdb.server.httpd.temporary_view_reads`*:: + -- -Size of the image. +Number of temporary view reads type: long -- -*`docker.image.size.regular`*:: +*`couchdb.server.httpd.requests`*:: + -- -Total size of the all cached images associated to the current image. +Number of HTTP requests type: long -- -*`docker.image.labels`*:: -+ --- -Image labels. +[float] +=== httpd_request_methods +HTTP request methods -type: object --- -*`docker.image.tags`*:: -+ --- -Image tags. - - -type: keyword - --- - -[float] -=== info - -Info metrics based on https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/display-system-wide-information. - - - -[float] -=== containers - -Overall container stats. - - - -*`docker.info.containers.paused`*:: +*`couchdb.server.httpd_request_methods.COPY`*:: + -- -Total number of paused containers. +Number of HTTP COPY requests type: long -- -*`docker.info.containers.running`*:: +*`couchdb.server.httpd_request_methods.HEAD`*:: + -- -Total number of running containers. +Number of HTTP HEAD requests type: long -- -*`docker.info.containers.stopped`*:: +*`couchdb.server.httpd_request_methods.POST`*:: + -- -Total number of stopped containers. +Number of HTTP POST requests type: long -- -*`docker.info.containers.total`*:: +*`couchdb.server.httpd_request_methods.DELETE`*:: + -- -Total number of existing containers. +Number of HTTP DELETE requests type: long -- -*`docker.info.id`*:: +*`couchdb.server.httpd_request_methods.GET`*:: + -- -Unique Docker host identifier. +Number of HTTP GET requests -type: keyword +type: long -- -*`docker.info.images`*:: +*`couchdb.server.httpd_request_methods.PUT`*:: + -- -Total number of existing images. +Number of HTTP PUT requests type: long @@ -7345,2266 +7305,2098 @@ type: long -- [float] -=== memory +=== httpd_status_codes -Memory metrics. +HTTP status codes statistics -*`docker.memory.stats.*`*:: +*`couchdb.server.httpd_status_codes.200`*:: + -- -Raw memory stats from the cgroups memory.stat interface +Number of HTTP 200 OK responses -type: object +type: long -- -[float] -=== commit - -Committed bytes on Windows - - - -*`docker.memory.commit.total`*:: +*`couchdb.server.httpd_status_codes.201`*:: + -- -Total bytes +Number of HTTP 201 Created responses type: long -format: bytes - -- -*`docker.memory.commit.peak`*:: +*`couchdb.server.httpd_status_codes.202`*:: + -- -Peak committed bytes on Windows +Number of HTTP 202 Accepted responses type: long -format: bytes - -- -*`docker.memory.private_working_set.total`*:: +*`couchdb.server.httpd_status_codes.301`*:: + -- -private working sets on Windows +Number of HTTP 301 Moved Permanently responses type: long -format: bytes - -- -*`docker.memory.fail.count`*:: +*`couchdb.server.httpd_status_codes.304`*:: + -- -Fail counter. +Number of HTTP 304 Not Modified responses -type: scaled_float +type: long -- -*`docker.memory.limit`*:: +*`couchdb.server.httpd_status_codes.400`*:: + -- -Memory limit. +Number of HTTP 400 Bad Request responses type: long -format: bytes - -- -[float] -=== rss - -RSS memory stats. - - - -*`docker.memory.rss.total`*:: +*`couchdb.server.httpd_status_codes.401`*:: + -- -Total memory resident set size. +Number of HTTP 401 Unauthorized responses type: long -format: bytes - -- -*`docker.memory.rss.pct`*:: +*`couchdb.server.httpd_status_codes.403`*:: + -- -Memory resident set size percentage. - +Number of HTTP 403 Forbidden responses -type: scaled_float -format: percent +type: long -- -[float] -=== usage - -Usage memory stats. - - - -*`docker.memory.usage.max`*:: +*`couchdb.server.httpd_status_codes.404`*:: + -- -Max memory usage. +Number of HTTP 404 Not Found responses type: long -format: bytes - -- -*`docker.memory.usage.pct`*:: +*`couchdb.server.httpd_status_codes.405`*:: + -- -Memory usage percentage. - +Number of HTTP 405 Method Not Allowed responses -type: scaled_float -format: percent +type: long -- -*`docker.memory.usage.total`*:: +*`couchdb.server.httpd_status_codes.409`*:: + -- -Total memory usage. +Number of HTTP 409 Conflict responses type: long -format: bytes - -- -[float] -=== network +*`couchdb.server.httpd_status_codes.412`*:: ++ +-- +Number of HTTP 412 Precondition Failed responses -Network metrics. +type: long +-- -*`docker.network.interface`*:: +*`couchdb.server.httpd_status_codes.500`*:: + -- -Network interface name. +Number of HTTP 500 Internal Server Error responses -type: keyword +type: long -- [float] -=== in +=== couchdb -Incoming network stats per second. +couchdb statistics -*`docker.network.in.bytes`*:: +*`couchdb.server.couchdb.database_writes`*:: + -- -Total number of incoming bytes. +Number of times a database was changed type: long -format: bytes - -- -*`docker.network.in.dropped`*:: +*`couchdb.server.couchdb.open_databases`*:: + -- -Total number of dropped incoming packets. +Number of open databases -type: scaled_float +type: long -- -*`docker.network.in.errors`*:: +*`couchdb.server.couchdb.auth_cache_misses`*:: + -- -Total errors on incoming packets. +Number of authentication cache misses type: long -- -*`docker.network.in.packets`*:: +*`couchdb.server.couchdb.request_time`*:: + -- -Total number of incoming packets. +Length of a request inside CouchDB without MochiWeb type: long -- -[float] -=== out - -Outgoing network stats per second. - - - -*`docker.network.out.bytes`*:: +*`couchdb.server.couchdb.database_reads`*:: + -- -Total number of outgoing bytes. +Number of times a document was read from a database type: long -format: bytes - -- -*`docker.network.out.dropped`*:: +*`couchdb.server.couchdb.auth_cache_hits`*:: + -- -Total number of dropped outgoing packets. +Number of authentication cache hits -type: scaled_float +type: long -- -*`docker.network.out.errors`*:: +*`couchdb.server.couchdb.open_os_files`*:: + -- -Total errors on outgoing packets. +Number of file descriptors CouchDB has open type: long -- -*`docker.network.out.packets`*:: -+ --- -Total number of outgoing packets. +[[exported-fields-docker-processor]] +== Docker fields +Docker stats collected from Docker. -type: long --- -[float] -=== inbound -Incoming network stats since the container started. +*`docker.container.id`*:: ++ +-- +type: alias +alias to: container.id +-- -*`docker.network.inbound.bytes`*:: +*`docker.container.image`*:: + -- -Total number of incoming bytes. - - -type: long +type: alias -format: bytes +alias to: container.image.name -- -*`docker.network.inbound.dropped`*:: +*`docker.container.name`*:: + -- -Total number of dropped incoming packets. - +type: alias -type: long +alias to: container.name -- -*`docker.network.inbound.errors`*:: +*`docker.container.labels`*:: + -- -Total errors on incoming packets. +Image labels. -type: long +type: object -- -*`docker.network.inbound.packets`*:: -+ --- -Total number of incoming packets. +[[exported-fields-docker]] +== Docker fields +Docker stats collected from Docker. -type: long --- [float] -=== outbound - -Outgoing network stats since the container started. +=== docker +Information and statistics about docker's running containers. -*`docker.network.outbound.bytes`*:: -+ --- -Total number of outgoing bytes. +[float] +=== container -type: long +Docker container metrics. -format: bytes --- -*`docker.network.outbound.dropped`*:: +*`docker.container.command`*:: + -- -Total number of dropped outgoing packets. +Command that was executed in the Docker container. -type: long +type: keyword -- -*`docker.network.outbound.errors`*:: +*`docker.container.created`*:: + -- -Total errors on outgoing packets. +Date when the container was created. -type: long +type: date -- -*`docker.network.outbound.packets`*:: +*`docker.container.status`*:: + -- -Total number of outgoing packets. +Container status. -type: long +type: keyword -- -[[exported-fields-dropwizard]] -== Dropwizard fields +*`docker.container.ip_addresses`*:: ++ +-- +Container IP addresses. -Stats collected from Dropwizard. +type: ip +-- [float] -=== dropwizard - - - +=== size -[[exported-fields-ecs]] -== ECS fields +Container size metrics. -ECS Fields. -*`@timestamp`*:: +*`docker.container.size.root_fs`*:: + -- -Date/time when the event originated. -This is the date/time extracted from the event, typically representing when the event was generated by the source. -If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. -Required field for all events. - -type: date +Total size of all the files in the container. -example: 2016-05-23T08:05:34.853Z -required: True +type: long -- -*`labels`*:: +*`docker.container.size.rw`*:: + -- -Custom key/value pairs. -Can be used to add meta information to events. Should not contain nested objects. All values are stored as keyword. -Example: `docker` and `k8s` labels. +Size of the files that have been created or changed since creation. -type: object -example: {"application": "foo-bar", "env": "production"} +type: long -- -*`message`*:: +*`docker.container.tags`*:: + -- -For log events the message field contains the log message, optimized for viewing in a log viewer. -For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. -If multiple messages exist, they can be combined into one message. - -type: text - -example: Hello World - --- +Image tags. -*`tags`*:: -+ --- -List of keywords used to tag each event. type: keyword -example: ["production", "env2"] - -- [float] -=== agent +=== cpu -The agent fields contain the data about the software entity, if any, that collects, detects, or observes events on a host, or takes measurements on a host. -Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken. +Runtime CPU metrics. -*`agent.ephemeral_id`*:: + +*`docker.cpu.kernel.pct`*:: + -- -Ephemeral identifier of this agent (if one exists). -This id normally changes across restarts, but `agent.id` does not. +Percentage of time in kernel space. -type: keyword -example: 8a4f500f +type: scaled_float + +format: percent -- -*`agent.id`*:: +*`docker.cpu.kernel.norm.pct`*:: + -- -Unique identifier of this agent (if one exists). -Example: For Beats this would be beat.id. +Percentage of time in kernel space normalized by the number of CPU cores. -type: keyword -example: 8a4f500d +type: scaled_float + +format: percent -- -*`agent.name`*:: +*`docker.cpu.kernel.ticks`*:: + -- -Custom name of the agent. -This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. -If no name is given, the name is often left empty. +CPU ticks in kernel space. -type: keyword -example: foo +type: long -- -*`agent.type`*:: +*`docker.cpu.system.pct`*:: + -- -Type of the agent. -The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine. +Percentage of total CPU time in the system. -type: keyword -example: filebeat +type: scaled_float + +format: percent -- -*`agent.version`*:: +*`docker.cpu.system.norm.pct`*:: + -- -Version of the agent. - -type: keyword - -example: 6.0.0-rc2 +Percentage of total CPU time in the system normalized by the number of CPU cores. --- -[float] -=== as +type: scaled_float -An autonomous system (AS) is a collection of connected Internet Protocol (IP) routing prefixes under the control of one or more network operators on behalf of a single administrative entity or domain that presents a common, clearly defined routing policy to the internet. +format: percent +-- -*`as.number`*:: +*`docker.cpu.system.ticks`*:: + -- -Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. +CPU system ticks. -type: long -example: 15169 +type: long -- -*`as.organization.name`*:: +*`docker.cpu.user.pct`*:: + -- -Organization name. +Percentage of time in user space. -type: keyword -example: Google LLC +type: scaled_float + +format: percent -- -*`as.organization.name.text`*:: +*`docker.cpu.user.norm.pct`*:: + -- -type: text +Percentage of time in user space normalized by the number of CPU cores. --- -[float] -=== client +type: scaled_float -A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. -For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. -Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately. +format: percent +-- -*`client.address`*:: +*`docker.cpu.user.ticks`*:: + -- -Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. -Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. +CPU ticks in user space. -type: keyword + +type: long -- -*`client.as.number`*:: +*`docker.cpu.total.pct`*:: + -- -Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. +Total CPU usage. -type: long -example: 15169 +type: scaled_float + +format: percent -- -*`client.as.organization.name`*:: +*`docker.cpu.total.norm.pct`*:: + -- -Organization name. +Total CPU usage normalized by the number of CPU cores. -type: keyword -example: Google LLC +type: scaled_float + +format: percent -- -*`client.as.organization.name.text`*:: +*`docker.cpu.core.*.pct`*:: + -- -type: text +Percentage of CPU time in this core. + + +type: object + +format: percent -- -*`client.bytes`*:: +*`docker.cpu.core.*.norm.pct`*:: + -- -Bytes sent from the client to the server. +Percentage of CPU time in this core, normalized by the number of CPU cores. -type: long -example: 184 +type: object -format: bytes +format: percent -- -*`client.domain`*:: +*`docker.cpu.core.*.ticks`*:: + -- -Client domain. +Number of CPU ticks in this core. -type: keyword --- +type: object -*`client.geo.city_name`*:: -+ -- -City name. -type: keyword +[float] +=== diskio -example: Montreal +Disk I/O metrics. --- -*`client.geo.continent_name`*:: + +[float] +=== read + +Accumulated reads during the life of the container + + + +*`docker.diskio.read.ops`*:: + -- -Name of the continent. +Number of reads during the life of the container -type: keyword -example: North America +type: long -- -*`client.geo.country_iso_code`*:: +*`docker.diskio.read.bytes`*:: + -- -Country ISO code. +Bytes read during the life of the container -type: keyword -example: CA +type: long + +format: bytes -- -*`client.geo.country_name`*:: +*`docker.diskio.read.rate`*:: + -- -Country name. +Number of current reads per second -type: keyword -example: Canada +type: long -- -*`client.geo.location`*:: +*`docker.diskio.read.service_time`*:: + -- -Longitude and latitude. +Total time to service IO requests, in nanoseconds -type: geo_point -example: { "lon": -73.614830, "lat": 45.505918 } +type: long -- -*`client.geo.name`*:: +*`docker.diskio.read.wait_time`*:: + -- -User-defined description of a location, at the level of granularity they care about. -Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. -Not typically used in automated geolocation. +Total time requests spent waiting in queues for service, in nanoseconds -type: keyword -example: boston-dc +type: long -- -*`client.geo.region_iso_code`*:: +*`docker.diskio.read.queued`*:: + -- -Region ISO code. +Total number of queued requests -type: keyword -example: CA-QC +type: long -- -*`client.geo.region_name`*:: +*`docker.diskio.reads`*:: + -- -Region name. -type: keyword - -example: Quebec +deprecated:[6.4] --- +Number of current reads per second -*`client.ip`*:: -+ --- -IP address of the client. -Can be one or multiple IPv4 or IPv6 addresses. -type: ip +type: scaled_float -- -*`client.mac`*:: -+ --- -MAC address of the client. +[float] +=== write -type: keyword +Accumulated writes during the life of the container --- -*`client.nat.ip`*:: + +*`docker.diskio.write.ops`*:: + -- -Translated IP of source based NAT sessions (e.g. internal client to internet). -Typically connections traversing load balancers, firewalls, or routers. +Number of writes during the life of the container -type: ip + +type: long -- -*`client.nat.port`*:: +*`docker.diskio.write.bytes`*:: + -- -Translated port of source based NAT sessions (e.g. internal client to internet). -Typically connections traversing load balancers, firewalls, or routers. +Bytes written during the life of the container + type: long -format: string +format: bytes -- -*`client.packets`*:: +*`docker.diskio.write.rate`*:: + -- -Packets sent from the client to the server. +Number of current writes per second -type: long -example: 12 +type: long -- -*`client.port`*:: +*`docker.diskio.write.service_time`*:: + -- -Port of the client. +Total time to service IO requests, in nanoseconds -type: long -format: string +type: long -- -*`client.registered_domain`*:: +*`docker.diskio.write.wait_time`*:: + -- -The highest registered client domain, stripped of the subdomain. -For example, the registered domain for "foo.google.com" is "google.com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". +Total time requests spent waiting in queues for service, in nanoseconds -type: keyword -example: google.com +type: long -- -*`client.top_level_domain`*:: +*`docker.diskio.write.queued`*:: + -- -The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". +Total number of queued requests -type: keyword -example: co.uk +type: long -- -*`client.user.domain`*:: +*`docker.diskio.writes`*:: + -- -Name of the directory the user is a member of. -For example, an LDAP or Active Directory domain name. -type: keyword +deprecated:[6.4] --- +Number of current writes per second -*`client.user.email`*:: -+ --- -User email address. -type: keyword +type: scaled_float -- -*`client.user.full_name`*:: -+ --- -User's full name, if available. +[float] +=== summary -type: keyword +Accumulated reads and writes during the life of the container -example: Albert Einstein --- -*`client.user.full_name.text`*:: +*`docker.diskio.summary.ops`*:: + -- -type: text +Number of I/O operations during the life of the container + + +type: long -- -*`client.user.group.domain`*:: +*`docker.diskio.summary.bytes`*:: + -- -Name of the directory the group is a member of. -For example, an LDAP or Active Directory domain name. +Bytes read and written during the life of the container -type: keyword + +type: long + +format: bytes -- -*`client.user.group.id`*:: +*`docker.diskio.summary.rate`*:: + -- -Unique identifier for the group on the system/platform. +Number of current operations per second -type: keyword + +type: long -- -*`client.user.group.name`*:: +*`docker.diskio.summary.service_time`*:: + -- -Name of the group. +Total time to service IO requests, in nanoseconds -type: keyword + +type: long -- -*`client.user.hash`*:: +*`docker.diskio.summary.wait_time`*:: + -- -Unique user hash to correlate information for a user in anonymized form. -Useful if `user.id` or `user.name` contain confidential information and cannot be used. +Total time requests spent waiting in queues for service, in nanoseconds -type: keyword + +type: long -- -*`client.user.id`*:: +*`docker.diskio.summary.queued`*:: + -- -Unique identifiers of the user. +Total number of queued requests -type: keyword + +type: long -- -*`client.user.name`*:: +*`docker.diskio.total`*:: + -- -Short name or login of the user. -type: keyword +deprecated:[6.4] -example: albert +Number of reads and writes per second --- -*`client.user.name.text`*:: -+ --- -type: text +type: scaled_float -- [float] -=== cloud +=== event -Fields related to the cloud or infrastructure the events are coming from. +Docker event -*`cloud.account.id`*:: + +*`docker.event.status`*:: + -- -The cloud account or organization id used to identify different entities in a multi-tenant environment. -Examples: AWS account id, Google Cloud ORG Id, or other unique identifier. +Event status -type: keyword -example: 666777888999 +type: keyword -- -*`cloud.availability_zone`*:: +*`docker.event.id`*:: + -- -Availability zone in which this host is running. +Event id when available -type: keyword -example: us-east-1c +type: keyword -- -*`cloud.instance.id`*:: +*`docker.event.from`*:: + -- -Instance ID of the host machine. +Event source -type: keyword -example: i-1234567890abcdef0 +type: keyword -- -*`cloud.instance.name`*:: +*`docker.event.type`*:: + -- -Instance name of the host machine. +The type of object emitting the event + type: keyword -- -*`cloud.machine.type`*:: +*`docker.event.action`*:: + -- -Machine type of the host machine. +The type of event -type: keyword -example: t2.medium +type: keyword -- -*`cloud.provider`*:: +[float] +=== actor + +Actor + + + +*`docker.event.actor.id`*:: + -- -Name of the cloud provider. Example values are aws, azure, gcp, or digitalocean. +The ID of the object emitting the event -type: keyword -example: aws +type: keyword -- -*`cloud.region`*:: +*`docker.event.actor.attributes`*:: + -- -Region in which this host is running. +Various key/value attributes of the object, depending on its type -type: keyword -example: us-east-1 +type: object -- [float] -=== code_signature +=== healthcheck -These fields contain information about binary code signatures. +Docker healthcheck metrics. +Healthcheck data will only be available from docker containers where the docker `HEALTHCHECK` instruction has been used to build the docker image. -*`code_signature.exists`*:: + +*`docker.healthcheck.failingstreak`*:: + -- -Boolean to capture if a signature is present. +concurent failed check -type: boolean -example: true +type: integer -- -*`code_signature.status`*:: +*`docker.healthcheck.status`*:: + -- -Additional information about the certificate status. -This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. +Healthcheck status code -type: keyword -example: ERROR_UNTRUSTED_ROOT +type: keyword -- -*`code_signature.subject_name`*:: -+ --- -Subject name of the code signer +[float] +=== event -type: keyword +event fields. -example: Microsoft Corporation --- -*`code_signature.trusted`*:: +*`docker.healthcheck.event.end_date`*:: + -- -Stores the trust status of the certificate chain. -Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. +Healthcheck end date -type: boolean -example: true +type: date -- -*`code_signature.valid`*:: +*`docker.healthcheck.event.start_date`*:: + -- -Boolean to capture if the digital signature is verified against the binary content. -Leave unpopulated if a certificate was unchecked. +Healthcheck start date -type: boolean -example: true +type: date -- -[float] -=== container - -Container fields are used for meta information about the specific container that is the source of information. -These fields help correlate data based containers from any runtime. - - -*`container.id`*:: +*`docker.healthcheck.event.output`*:: + -- -Unique container id. +Healthcheck output + type: keyword -- -*`container.image.name`*:: +*`docker.healthcheck.event.exit_code`*:: + -- -Name of the image the container was built on. +Healthcheck status code -type: keyword --- +type: integer -*`container.image.tag`*:: -+ -- -Container image tags. -type: keyword +[float] +=== image --- +Docker image metrics. -*`container.labels`*:: + + +[float] +=== id + +The image layers identifier. + + + +*`docker.image.id.current`*:: + -- -Image labels. +Unique image identifier given upon its creation. -type: object + +type: keyword -- -*`container.name`*:: +*`docker.image.id.parent`*:: + -- -Container name. +Identifier of the image, if it exists, from which the current image directly descends. + type: keyword -- -*`container.runtime`*:: +*`docker.image.created`*:: + -- -Runtime managing this container. +Date and time when the image was created. -type: keyword -example: docker +type: date -- [float] -=== destination +=== size -Destination fields describe details about the destination of a packet/event. -Destination fields are usually populated in conjunction with source fields. +Image size layers. -*`destination.address`*:: + +*`docker.image.size.virtual`*:: + -- -Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. -Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. +Size of the image. -type: keyword + +type: long -- -*`destination.as.number`*:: +*`docker.image.size.regular`*:: + -- -Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. +Total size of the all cached images associated to the current image. -type: long -example: 15169 +type: long -- -*`destination.as.organization.name`*:: +*`docker.image.labels`*:: + -- -Organization name. +Image labels. -type: keyword -example: Google LLC +type: object -- -*`destination.as.organization.name.text`*:: +*`docker.image.tags`*:: + -- -type: text +Image tags. --- -*`destination.bytes`*:: -+ +type: keyword + -- -Bytes sent from the destination to the source. -type: long +[float] +=== info -example: 184 +Info metrics based on https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/display-system-wide-information. -format: bytes --- -*`destination.domain`*:: -+ --- -Destination domain. +[float] +=== containers -type: keyword +Overall container stats. --- -*`destination.geo.city_name`*:: + +*`docker.info.containers.paused`*:: + -- -City name. +Total number of paused containers. -type: keyword -example: Montreal +type: long -- -*`destination.geo.continent_name`*:: +*`docker.info.containers.running`*:: + -- -Name of the continent. +Total number of running containers. -type: keyword -example: North America +type: long -- -*`destination.geo.country_iso_code`*:: +*`docker.info.containers.stopped`*:: + -- -Country ISO code. +Total number of stopped containers. -type: keyword -example: CA +type: long -- -*`destination.geo.country_name`*:: +*`docker.info.containers.total`*:: + -- -Country name. +Total number of existing containers. -type: keyword -example: Canada +type: long -- -*`destination.geo.location`*:: +*`docker.info.id`*:: + -- -Longitude and latitude. +Unique Docker host identifier. -type: geo_point -example: { "lon": -73.614830, "lat": 45.505918 } +type: keyword -- -*`destination.geo.name`*:: +*`docker.info.images`*:: + -- -User-defined description of a location, at the level of granularity they care about. -Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. -Not typically used in automated geolocation. +Total number of existing images. -type: keyword -example: boston-dc +type: long -- -*`destination.geo.region_iso_code`*:: -+ --- -Region ISO code. +[float] +=== memory -type: keyword +Memory metrics. -example: CA-QC --- -*`destination.geo.region_name`*:: +*`docker.memory.stats.*`*:: + -- -Region name. +Raw memory stats from the cgroups memory.stat interface -type: keyword -example: Quebec +type: object -- -*`destination.ip`*:: -+ --- -IP address of the destination. -Can be one or multiple IPv4 or IPv6 addresses. +[float] +=== commit -type: ip +Committed bytes on Windows --- -*`destination.mac`*:: + +*`docker.memory.commit.total`*:: + -- -MAC address of the destination. - -type: keyword +Total bytes --- -*`destination.nat.ip`*:: -+ --- -Translated ip of destination based NAT sessions (e.g. internet to private DMZ) -Typically used with load balancers, firewalls, or routers. +type: long -type: ip +format: bytes -- -*`destination.nat.port`*:: +*`docker.memory.commit.peak`*:: + -- -Port the source session is translated to by NAT Device. -Typically used with load balancers, firewalls, or routers. +Peak committed bytes on Windows + type: long -format: string +format: bytes -- -*`destination.packets`*:: +*`docker.memory.private_working_set.total`*:: + -- -Packets sent from the destination to the source. +private working sets on Windows + type: long -example: 12 +format: bytes -- -*`destination.port`*:: +*`docker.memory.fail.count`*:: + -- -Port of the destination. +Fail counter. -type: long -format: string +type: scaled_float -- -*`destination.registered_domain`*:: +*`docker.memory.limit`*:: + -- -The highest registered destination domain, stripped of the subdomain. -For example, the registered domain for "foo.google.com" is "google.com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". +Memory limit. -type: keyword -example: google.com +type: long --- +format: bytes -*`destination.top_level_domain`*:: -+ -- -The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". -type: keyword +[float] +=== rss -example: co.uk +RSS memory stats. --- -*`destination.user.domain`*:: + +*`docker.memory.rss.total`*:: + -- -Name of the directory the user is a member of. -For example, an LDAP or Active Directory domain name. +Total memory resident set size. -type: keyword + +type: long + +format: bytes -- -*`destination.user.email`*:: +*`docker.memory.rss.pct`*:: + -- -User email address. +Memory resident set size percentage. -type: keyword --- +type: scaled_float + +format: percent -*`destination.user.full_name`*:: -+ -- -User's full name, if available. -type: keyword +[float] +=== usage -example: Albert Einstein +Usage memory stats. --- -*`destination.user.full_name.text`*:: + +*`docker.memory.usage.max`*:: + -- -type: text +Max memory usage. + + +type: long + +format: bytes -- -*`destination.user.group.domain`*:: +*`docker.memory.usage.pct`*:: + -- -Name of the directory the group is a member of. -For example, an LDAP or Active Directory domain name. +Memory usage percentage. -type: keyword + +type: scaled_float + +format: percent -- -*`destination.user.group.id`*:: +*`docker.memory.usage.total`*:: + -- -Unique identifier for the group on the system/platform. +Total memory usage. -type: keyword + +type: long + +format: bytes -- -*`destination.user.group.name`*:: +[float] +=== network + +Network metrics. + + + +*`docker.network.interface`*:: + -- -Name of the group. +Network interface name. + type: keyword -- -*`destination.user.hash`*:: +[float] +=== in + +Incoming network stats per second. + + + +*`docker.network.in.bytes`*:: + -- -Unique user hash to correlate information for a user in anonymized form. -Useful if `user.id` or `user.name` contain confidential information and cannot be used. +Total number of incoming bytes. -type: keyword + +type: long + +format: bytes -- -*`destination.user.id`*:: +*`docker.network.in.dropped`*:: + -- -Unique identifiers of the user. +Total number of dropped incoming packets. -type: keyword + +type: scaled_float -- -*`destination.user.name`*:: +*`docker.network.in.errors`*:: + -- -Short name or login of the user. +Total errors on incoming packets. -type: keyword -example: albert +type: long -- -*`destination.user.name.text`*:: +*`docker.network.in.packets`*:: + -- -type: text +Total number of incoming packets. + + +type: long -- [float] -=== dll +=== out -These fields contain information about code libraries dynamically loaded into processes. +Outgoing network stats per second. -Many operating systems refer to "shared code libraries" with different names, but this field set refers to all of the following: -* Dynamic-link library (`.dll`) commonly used on Windows -* Shared Object (`.so`) commonly used on Unix-like operating systems -* Dynamic library (`.dylib`) commonly used on macOS -*`dll.code_signature.exists`*:: +*`docker.network.out.bytes`*:: + -- -Boolean to capture if a signature is present. +Total number of outgoing bytes. -type: boolean -example: true +type: long + +format: bytes -- -*`dll.code_signature.status`*:: +*`docker.network.out.dropped`*:: + -- -Additional information about the certificate status. -This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. +Total number of dropped outgoing packets. -type: keyword -example: ERROR_UNTRUSTED_ROOT +type: scaled_float -- -*`dll.code_signature.subject_name`*:: +*`docker.network.out.errors`*:: + -- -Subject name of the code signer +Total errors on outgoing packets. -type: keyword -example: Microsoft Corporation +type: long -- -*`dll.code_signature.trusted`*:: +*`docker.network.out.packets`*:: + -- -Stores the trust status of the certificate chain. -Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. +Total number of outgoing packets. -type: boolean -example: true +type: long -- -*`dll.code_signature.valid`*:: -+ --- -Boolean to capture if the digital signature is verified against the binary content. -Leave unpopulated if a certificate was unchecked. +[float] +=== inbound -type: boolean +Incoming network stats since the container started. -example: true --- -*`dll.hash.md5`*:: +*`docker.network.inbound.bytes`*:: + -- -MD5 hash. +Total number of incoming bytes. -type: keyword + +type: long + +format: bytes -- -*`dll.hash.sha1`*:: +*`docker.network.inbound.dropped`*:: + -- -SHA1 hash. +Total number of dropped incoming packets. -type: keyword + +type: long -- -*`dll.hash.sha256`*:: +*`docker.network.inbound.errors`*:: + -- -SHA256 hash. +Total errors on incoming packets. -type: keyword + +type: long -- -*`dll.hash.sha512`*:: +*`docker.network.inbound.packets`*:: + -- -SHA512 hash. +Total number of incoming packets. -type: keyword --- +type: long -*`dll.name`*:: -+ -- -Name of the library. -This generally maps to the name of the file on disk. -type: keyword +[float] +=== outbound -example: kernel32.dll +Outgoing network stats since the container started. --- -*`dll.path`*:: + +*`docker.network.outbound.bytes`*:: + -- -Full file path of the library. +Total number of outgoing bytes. -type: keyword -example: C:\Windows\System32\kernel32.dll +type: long + +format: bytes -- -*`dll.pe.company`*:: +*`docker.network.outbound.dropped`*:: + -- -Internal company name of the file, provided at compile-time. +Total number of dropped outgoing packets. -type: keyword -example: Microsoft Corporation +type: long -- -*`dll.pe.description`*:: +*`docker.network.outbound.errors`*:: + -- -Internal description of the file, provided at compile-time. +Total errors on outgoing packets. -type: keyword -example: Paint +type: long -- -*`dll.pe.file_version`*:: +*`docker.network.outbound.packets`*:: + -- -Internal version of the file, provided at compile-time. +Total number of outgoing packets. -type: keyword -example: 6.3.9600.17415 +type: long -- -*`dll.pe.original_file_name`*:: -+ --- -Internal name of the file, provided at compile-time. +[[exported-fields-dropwizard]] +== Dropwizard fields -type: keyword +Stats collected from Dropwizard. -example: MSPAINT.EXE --- -*`dll.pe.product`*:: -+ --- -Internal product name of the file, provided at compile-time. +[float] +=== dropwizard -type: keyword -example: Microsoft® Windows® Operating System --- -[float] -=== dns +[[exported-fields-ecs]] +== ECS fields -Fields describing DNS queries and answers. -DNS events should either represent a single DNS query prior to getting answers (`dns.type:query`) or they should represent a full exchange and contain the query details as well as all of the answers that were provided for this query (`dns.type:answer`). +ECS Fields. -*`dns.answers`*:: +*`@timestamp`*:: + -- -An array containing an object for each answer section returned by the server. -The main keys that should be present in these objects are defined by ECS. Records that have more information may contain more keys than what ECS defines. -Not all DNS data sources give all details about DNS answers. At minimum, answer objects must contain the `data` key. If more information is available, map as much of it to ECS as possible, and add any additional fields to the answer objects as custom fields. +Date/time when the event originated. +This is the date/time extracted from the event, typically representing when the event was generated by the source. +If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. +Required field for all events. -type: object +type: date + +example: 2016-05-23T08:05:34.853Z + +required: True -- -*`dns.answers.class`*:: +*`labels`*:: + -- -The class of DNS data contained in this resource record. +Custom key/value pairs. +Can be used to add meta information to events. Should not contain nested objects. All values are stored as keyword. +Example: `docker` and `k8s` labels. -type: keyword +type: object -example: IN +example: {"application": "foo-bar", "env": "production"} -- -*`dns.answers.data`*:: +*`message`*:: + -- -The data describing the resource. -The meaning of this data depends on the type and class of the resource record. +For log events the message field contains the log message, optimized for viewing in a log viewer. +For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. +If multiple messages exist, they can be combined into one message. -type: keyword +type: text -example: 10.10.10.10 +example: Hello World -- -*`dns.answers.name`*:: +*`tags`*:: + -- -The domain name to which this resource record pertains. -If a chain of CNAME is being resolved, each answer's `name` should be the one that corresponds with the answer's `data`. It should not simply be the original `question.name` repeated. +List of keywords used to tag each event. type: keyword -example: www.google.com +example: ["production", "env2"] -- -*`dns.answers.ttl`*:: +[float] +=== agent + +The agent fields contain the data about the software entity, if any, that collects, detects, or observes events on a host, or takes measurements on a host. +Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken. + + +*`agent.ephemeral_id`*:: + -- -The time interval in seconds that this resource record may be cached before it should be discarded. Zero values mean that the data should not be cached. +Ephemeral identifier of this agent (if one exists). +This id normally changes across restarts, but `agent.id` does not. -type: long +type: keyword -example: 180 +example: 8a4f500f -- -*`dns.answers.type`*:: +*`agent.id`*:: + -- -The type of data contained in this resource record. +Unique identifier of this agent (if one exists). +Example: For Beats this would be beat.id. type: keyword -example: CNAME +example: 8a4f500d -- -*`dns.header_flags`*:: +*`agent.name`*:: + -- -Array of 2 letter DNS header flags. -Expected values are: AA, TC, RD, RA, AD, CD, DO. +Custom name of the agent. +This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. +If no name is given, the name is often left empty. type: keyword -example: ['RD', 'RA'] +example: foo -- -*`dns.id`*:: +*`agent.type`*:: + -- -The DNS packet identifier assigned by the program that generated the query. The identifier is copied to the response. +Type of the agent. +The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine. type: keyword -example: 62111 +example: filebeat -- -*`dns.op_code`*:: +*`agent.version`*:: + -- -The DNS operation code that specifies the kind of query in the message. This value is set by the originator of a query and copied into the response. +Version of the agent. type: keyword -example: QUERY +example: 6.0.0-rc2 -- -*`dns.question.class`*:: +[float] +=== as + +An autonomous system (AS) is a collection of connected Internet Protocol (IP) routing prefixes under the control of one or more network operators on behalf of a single administrative entity or domain that presents a common, clearly defined routing policy to the internet. + + +*`as.number`*:: + -- -The class of records being queried. +Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. -type: keyword +type: long -example: IN +example: 15169 -- -*`dns.question.name`*:: +*`as.organization.name`*:: + -- -The name being queried. -If the name field contains non-printable characters (below 32 or above 126), those characters should be represented as escaped base 10 integers (\DDD). Back slashes and quotes should be escaped. Tabs, carriage returns, and line feeds should be converted to \t, \r, and \n respectively. +Organization name. type: keyword -example: www.google.com +example: Google LLC -- -*`dns.question.registered_domain`*:: +*`as.organization.name.text`*:: + -- -The highest registered domain, stripped of the subdomain. -For example, the registered domain for "foo.google.com" is "google.com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". +type: text -type: keyword +-- -example: google.com +[float] +=== client --- +A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. +For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. +Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately. -*`dns.question.subdomain`*:: + +*`client.address`*:: + -- -The subdomain is all of the labels under the registered_domain. -If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period. +Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. +Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. type: keyword -example: www - -- -*`dns.question.top_level_domain`*:: +*`client.as.number`*:: + -- -The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". +Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. -type: keyword +type: long -example: co.uk +example: 15169 -- -*`dns.question.type`*:: +*`client.as.organization.name`*:: + -- -The type of record being queried. +Organization name. type: keyword -example: AAAA +example: Google LLC -- -*`dns.resolved_ip`*:: +*`client.as.organization.name.text`*:: + -- -Array containing all IPs seen in `answers.data`. -The `answers` array can be difficult to use, because of the variety of data formats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip` makes it possible to index them as IP addresses, and makes them easier to visualize and query for. - -type: ip - -example: ['10.10.10.10', '10.10.10.11'] +type: text -- -*`dns.response_code`*:: +*`client.bytes`*:: + -- -The DNS response code. +Bytes sent from the client to the server. -type: keyword +type: long -example: NOERROR +example: 184 + +format: bytes -- -*`dns.type`*:: +*`client.domain`*:: + -- -The type of DNS event captured, query or answer. -If your source of DNS events only gives you DNS queries, you should only create dns events of type `dns.type:query`. -If your source of DNS events gives you answers as well, you should create one event per query (optionally as soon as the query is seen). And a second event containing all query details as well as an array of answers. +Client domain. type: keyword -example: answer - -- -[float] -=== ecs - -Meta-information specific to ECS. - - -*`ecs.version`*:: +*`client.geo.city_name`*:: + -- -ECS version this event conforms to. `ecs.version` is a required field and must exist in all events. -When querying across multiple indices -- which may conform to slightly different ECS versions -- this field lets integrations adjust to the schema version of the events. +City name. type: keyword -example: 1.0.0 +example: Montreal -required: True +-- +*`client.geo.continent_name`*:: ++ -- +Name of the continent. -[float] -=== error +type: keyword -These fields can represent errors of any kind. -Use them for errors that happen while fetching events or in cases where the event itself contains an error. +example: North America +-- -*`error.code`*:: +*`client.geo.country_iso_code`*:: + -- -Error code describing the error. +Country ISO code. type: keyword +example: CA + -- -*`error.id`*:: +*`client.geo.country_name`*:: + -- -Unique identifier for the error. +Country name. type: keyword +example: Canada + -- -*`error.message`*:: +*`client.geo.location`*:: + -- -Error message. +Longitude and latitude. -type: text +type: geo_point + +example: { "lon": -73.614830, "lat": 45.505918 } -- -*`error.stack_trace`*:: +*`client.geo.name`*:: + -- -The stack trace of this error in plain text. +User-defined description of a location, at the level of granularity they care about. +Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. +Not typically used in automated geolocation. type: keyword +example: boston-dc + -- -*`error.stack_trace.text`*:: +*`client.geo.region_iso_code`*:: + -- -type: text +Region ISO code. + +type: keyword + +example: CA-QC -- -*`error.type`*:: +*`client.geo.region_name`*:: + -- -The type of the error, for example the class name of the exception. +Region name. type: keyword -example: java.lang.NullPointerException +example: Quebec -- -[float] -=== event +*`client.ip`*:: ++ +-- +IP address of the client. +Can be one or multiple IPv4 or IPv6 addresses. -The event fields are used for context information about the log or metric event itself. -A log is defined as an event containing details of something that happened. Log events must include the time at which the thing happened. Examples of log events include a process starting on a host, a network packet being sent from a source to a destination, or a network connection between a client and a server being initiated or closed. A metric is defined as an event containing one or more numerical measurements and the time at which the measurement was taken. Examples of metric events include memory pressure measured on a host and device temperature. See the `event.kind` definition in this section for additional details about metric and state events. +type: ip +-- -*`event.action`*:: +*`client.mac`*:: + -- -The action captured by the event. -This describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer. +MAC address of the client. type: keyword -example: user-password-change +-- +*`client.nat.ip`*:: ++ -- +Translated IP of source based NAT sessions (e.g. internal client to internet). +Typically connections traversing load balancers, firewalls, or routers. -*`event.category`*:: +type: ip + +-- + +*`client.nat.port`*:: + -- -This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy. -`event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory. -This field is an array. This will allow proper categorization of some events that fall in multiple categories. +Translated port of source based NAT sessions (e.g. internal client to internet). +Typically connections traversing load balancers, firewalls, or routers. -type: keyword +type: long -example: authentication +format: string -- -*`event.code`*:: +*`client.packets`*:: + -- -Identification code for this event, if one exists. -Some event sources use event codes to identify messages unambiguously, regardless of message language or wording adjustments over time. An example of this is the Windows Event ID. +Packets sent from the client to the server. -type: keyword +type: long -example: 4648 +example: 12 -- -*`event.created`*:: +*`client.port`*:: + -- -event.created contains the date/time when the event was first read by an agent, or by your pipeline. -This field is distinct from @timestamp in that @timestamp typically contain the time extracted from the original event. -In most situations, these two timestamps will be slightly different. The difference can be used to calculate the delay between your source generating an event, and the time when your agent first processed it. This can be used to monitor your agent's or pipeline's ability to keep up with your event source. -In case the two timestamps are identical, @timestamp should be used. +Port of the client. -type: date +type: long -example: 2016-05-23T08:05:34.857Z +format: string -- -*`event.dataset`*:: +*`client.registered_domain`*:: + -- -Name of the dataset. -If an event source publishes more than one type of log or events (e.g. access log, error log), the dataset is used to specify which one the event comes from. -It's recommended but not required to start the dataset name with the module name, followed by a dot, then the dataset name. +The highest registered client domain, stripped of the subdomain. +For example, the registered domain for "foo.google.com" is "google.com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". type: keyword -example: apache.access +example: google.com -- -*`event.duration`*:: +*`client.top_level_domain`*:: + -- -Duration of the event in nanoseconds. -If event.start and event.end are known this value should be the difference between the end and start time. +The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". -type: long +type: keyword -format: duration +example: co.uk -- -*`event.end`*:: +*`client.user.domain`*:: + -- -event.end contains the date when the event ended or when the activity was last observed. +Name of the directory the user is a member of. +For example, an LDAP or Active Directory domain name. -type: date +type: keyword -- -*`event.hash`*:: +*`client.user.email`*:: + -- -Hash (perhaps logstash fingerprint) of raw field to be able to demonstrate log integrity. +User email address. type: keyword -example: 123456789012345678901234567890ABCD - -- -*`event.id`*:: +*`client.user.full_name`*:: + -- -Unique ID to describe the event. +User's full name, if available. type: keyword -example: 8a4f500d +example: Albert Einstein -- -*`event.ingested`*:: +*`client.user.full_name.text`*:: + -- -Timestamp when an event arrived in the central data store. -This is different from `@timestamp`, which is when the event originally occurred. It's also different from `event.created`, which is meant to capture the first time an agent saw the event. -In normal conditions, assuming no tampering, the timestamps should chronologically look like this: `@timestamp` < `event.created` < `event.ingested`. - -type: date - -example: 2016-05-23T08:05:35.101Z +type: text -- -*`event.kind`*:: +*`client.user.group.domain`*:: + -- -This is one of four ECS Categorization Fields, and indicates the highest level in the ECS category hierarchy. -`event.kind` gives high-level information about what type of information the event contains, without being specific to the contents of the event. For example, values of this field distinguish alert events from metric events. -The value of this field can be used to inform how these kinds of events should be handled. They may warrant different retention, different access control, it may also help understand whether the data coming in at a regular interval or not. +Name of the directory the group is a member of. +For example, an LDAP or Active Directory domain name. type: keyword -example: alert - -- -*`event.module`*:: +*`client.user.group.id`*:: + -- -Name of the module this data is coming from. -If your monitoring agent supports the concept of modules or plugins to process events of a given source (e.g. Apache logs), `event.module` should contain the name of this module. +Unique identifier for the group on the system/platform. type: keyword -example: apache - -- -*`event.original`*:: +*`client.user.group.name`*:: + -- -Raw text message of entire event. Used to demonstrate log integrity. -This field is not indexed and doc_values are disabled. It cannot be searched, but it can be retrieved from `_source`. +Name of the group. type: keyword -example: Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100| worm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232 - -- -*`event.outcome`*:: +*`client.user.hash`*:: + -- -This is one of four ECS Categorization Fields, and indicates the lowest level in the ECS category hierarchy. -`event.outcome` simply denotes whether the event represents a success or a failure from the perspective of the entity that produced the event. -Note that when a single transaction is described in multiple events, each event may populate different values of `event.outcome`, according to their perspective. -Also note that in the case of a compound event (a single event that contains multiple logical events), this field should be populated with the value that best captures the overall success or failure from the perspective of the event producer. -Further note that not all events will have an associated outcome. For example, this field is generally not populated for metric events, events with `event.type:info`, or any events for which an outcome does not make logical sense. +Unique user hash to correlate information for a user in anonymized form. +Useful if `user.id` or `user.name` contain confidential information and cannot be used. type: keyword -example: success - -- -*`event.provider`*:: +*`client.user.id`*:: + -- -Source of the event. -Event transports such as Syslog or the Windows Event Log typically mention the source of an event. It can be the name of the software that generated the event (e.g. Sysmon, httpd), or of a subsystem of the operating system (kernel, Microsoft-Windows-Security-Auditing). +Unique identifiers of the user. type: keyword -example: kernel - -- -*`event.reference`*:: +*`client.user.name`*:: + -- -Reference URL linking to additional information about this event. -This URL links to a static definition of the this event. Alert events, indicated by `event.kind:alert`, are a common use case for this field. +Short name or login of the user. type: keyword -example: https://system.vendor.com/event/#0001234 +example: albert -- -*`event.risk_score`*:: +*`client.user.name.text`*:: + -- -Risk score or priority of the event (e.g. security solutions). Use your system's original value here. - -type: float +type: text -- -*`event.risk_score_norm`*:: +[float] +=== cloud + +Fields related to the cloud or infrastructure the events are coming from. + + +*`cloud.account.id`*:: + -- -Normalized risk score or priority of the event, on a scale of 0 to 100. -This is mainly useful if you use more than one system that assigns risk scores, and you want to see a normalized value across all systems. +The cloud account or organization id used to identify different entities in a multi-tenant environment. +Examples: AWS account id, Google Cloud ORG Id, or other unique identifier. -type: float +type: keyword + +example: 666777888999 -- -*`event.sequence`*:: +*`cloud.availability_zone`*:: + -- -Sequence number of the event. -The sequence number is a value published by some event sources, to make the exact ordering of events unambiguous, regardless of the timestamp precision. +Availability zone in which this host is running. -type: long +type: keyword -format: string +example: us-east-1c -- -*`event.severity`*:: +*`cloud.instance.id`*:: + -- -The numeric severity of the event according to your event source. -What the different severity values mean can be different between sources and use cases. It's up to the implementer to make sure severities are consistent across events from the same source. -The Syslog severity belongs in `log.syslog.severity.code`. `event.severity` is meant to represent the severity according to the event source (e.g. firewall, IDS). If the event source does not publish its own severity, you may optionally copy the `log.syslog.severity.code` to `event.severity`. - -type: long +Instance ID of the host machine. -example: 7 +type: keyword -format: string +example: i-1234567890abcdef0 -- -*`event.start`*:: +*`cloud.instance.name`*:: + -- -event.start contains the date when the event started or when the activity was first observed. +Instance name of the host machine. -type: date +type: keyword -- -*`event.timezone`*:: +*`cloud.machine.type`*:: + -- -This field should be populated when the event's timestamp does not include timezone information already (e.g. default Syslog timestamps). It's optional otherwise. -Acceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"), abbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00"). +Machine type of the host machine. type: keyword +example: t2.medium + -- -*`event.type`*:: +*`cloud.provider`*:: + -- -This is one of four ECS Categorization Fields, and indicates the third level in the ECS category hierarchy. -`event.type` represents a categorization "sub-bucket" that, when used along with the `event.category` field values, enables filtering events down to a level appropriate for single visualization. -This field is an array. This will allow proper categorization of some events that fall in multiple event types. +Name of the cloud provider. Example values are aws, azure, gcp, or digitalocean. type: keyword +example: aws + -- -*`event.url`*:: +*`cloud.region`*:: + -- -URL linking to an external system to continue investigation of this event. -This URL links to another system where in-depth investigation of the specific occurence of this event can take place. Alert events, indicated by `event.kind:alert`, are a common use case for this field. +Region in which this host is running. type: keyword -example: https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe +example: us-east-1 -- [float] -=== file +=== code_signature -A file is defined as a set of information that has been created on, or has existed on a filesystem. -File objects can be associated with host events, network events, and/or file events (e.g., those produced by File Integrity Monitoring [FIM] products or services). File fields provide details about the affected file associated with the event or metric. +These fields contain information about binary code signatures. -*`file.accessed`*:: +*`code_signature.exists`*:: + -- -Last time the file was accessed. -Note that not all filesystems keep track of access time. +Boolean to capture if a signature is present. -type: date +type: boolean + +example: true -- -*`file.attributes`*:: -+ --- -Array of file attributes. -Attributes names will vary by platform. Here's a non-exhaustive list of values that are expected in this field: archive, compressed, directory, encrypted, execute, hidden, read, readonly, system, write. - -type: keyword - -example: ["readonly", "system"] - --- - -*`file.code_signature.exists`*:: -+ --- -Boolean to capture if a signature is present. - -type: boolean - -example: true - --- - -*`file.code_signature.status`*:: +*`code_signature.status`*:: + -- Additional information about the certificate status. @@ -9616,7 +9408,7 @@ example: ERROR_UNTRUSTED_ROOT -- -*`file.code_signature.subject_name`*:: +*`code_signature.subject_name`*:: + -- Subject name of the code signer @@ -9627,7 +9419,7 @@ example: Microsoft Corporation -- -*`file.code_signature.trusted`*:: +*`code_signature.trusted`*:: + -- Stores the trust status of the certificate chain. @@ -9639,7 +9431,7 @@ example: true -- -*`file.code_signature.valid`*:: +*`code_signature.valid`*:: + -- Boolean to capture if the digital signature is verified against the binary content. @@ -9651,453 +9443,487 @@ example: true -- -*`file.created`*:: +[float] +=== container + +Container fields are used for meta information about the specific container that is the source of information. +These fields help correlate data based containers from any runtime. + + +*`container.id`*:: + -- -File creation time. -Note that not all filesystems store the creation time. +Unique container id. -type: date +type: keyword -- -*`file.ctime`*:: +*`container.image.name`*:: + -- -Last time the file attributes or metadata changed. -Note that changes to the file content will update `mtime`. This implies `ctime` will be adjusted at the same time, since `mtime` is an attribute of the file. +Name of the image the container was built on. -type: date +type: keyword -- -*`file.device`*:: +*`container.image.tag`*:: + -- -Device that is the source of the file. +Container image tags. type: keyword -example: sda +-- + +*`container.labels`*:: ++ +-- +Image labels. + +type: object -- -*`file.directory`*:: +*`container.name`*:: + -- -Directory where the file is located. It should include the drive letter, when appropriate. +Container name. type: keyword -example: /home/alice - -- -*`file.drive_letter`*:: +*`container.runtime`*:: + -- -Drive letter where the file is located. This field is only relevant on Windows. -The value should be uppercase, and not include the colon. +Runtime managing this container. type: keyword -example: C +example: docker -- -*`file.extension`*:: +[float] +=== destination + +Destination fields describe details about the destination of a packet/event. +Destination fields are usually populated in conjunction with source fields. + + +*`destination.address`*:: + -- -File extension. +Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. +Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. type: keyword -example: png - -- -*`file.gid`*:: +*`destination.as.number`*:: + -- -Primary group ID (GID) of the file. +Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. -type: keyword +type: long -example: 1001 +example: 15169 -- -*`file.group`*:: +*`destination.as.organization.name`*:: + -- -Primary group name of the file. +Organization name. type: keyword -example: alice +example: Google LLC -- -*`file.hash.md5`*:: +*`destination.as.organization.name.text`*:: + -- -MD5 hash. - -type: keyword +type: text -- -*`file.hash.sha1`*:: +*`destination.bytes`*:: + -- -SHA1 hash. +Bytes sent from the destination to the source. -type: keyword +type: long + +example: 184 + +format: bytes -- -*`file.hash.sha256`*:: +*`destination.domain`*:: + -- -SHA256 hash. +Destination domain. type: keyword -- -*`file.hash.sha512`*:: +*`destination.geo.city_name`*:: + -- -SHA512 hash. +City name. type: keyword +example: Montreal + -- -*`file.inode`*:: +*`destination.geo.continent_name`*:: + -- -Inode representing the file in the filesystem. +Name of the continent. type: keyword -example: 256383 +example: North America -- -*`file.mime_type`*:: +*`destination.geo.country_iso_code`*:: + -- -MIME type should identify the format of the file or stream of bytes using https://www.iana.org/assignments/media-types/media-types.xhtml[IANA official types], where possible. When more than one type is applicable, the most specific type should be used. +Country ISO code. type: keyword +example: CA + -- -*`file.mode`*:: +*`destination.geo.country_name`*:: + -- -Mode of the file in octal representation. +Country name. type: keyword -example: 0640 +example: Canada -- -*`file.mtime`*:: +*`destination.geo.location`*:: + -- -Last time the file content was modified. +Longitude and latitude. -type: date +type: geo_point + +example: { "lon": -73.614830, "lat": 45.505918 } -- -*`file.name`*:: +*`destination.geo.name`*:: + -- -Name of the file including the extension, without the directory. +User-defined description of a location, at the level of granularity they care about. +Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. +Not typically used in automated geolocation. type: keyword -example: example.png +example: boston-dc -- -*`file.owner`*:: +*`destination.geo.region_iso_code`*:: + -- -File owner's username. +Region ISO code. type: keyword -example: alice +example: CA-QC -- -*`file.path`*:: +*`destination.geo.region_name`*:: + -- -Full path to the file, including the file name. It should include the drive letter, when appropriate. +Region name. type: keyword -example: /home/alice/example.png +example: Quebec -- -*`file.path.text`*:: +*`destination.ip`*:: + -- -type: text +IP address of the destination. +Can be one or multiple IPv4 or IPv6 addresses. + +type: ip -- -*`file.pe.company`*:: +*`destination.mac`*:: + -- -Internal company name of the file, provided at compile-time. +MAC address of the destination. type: keyword -example: Microsoft Corporation +-- + +*`destination.nat.ip`*:: ++ +-- +Translated ip of destination based NAT sessions (e.g. internet to private DMZ) +Typically used with load balancers, firewalls, or routers. + +type: ip -- -*`file.pe.description`*:: +*`destination.nat.port`*:: + -- -Internal description of the file, provided at compile-time. +Port the source session is translated to by NAT Device. +Typically used with load balancers, firewalls, or routers. -type: keyword +type: long -example: Paint +format: string -- -*`file.pe.file_version`*:: +*`destination.packets`*:: + -- -Internal version of the file, provided at compile-time. +Packets sent from the destination to the source. -type: keyword +type: long -example: 6.3.9600.17415 +example: 12 -- -*`file.pe.original_file_name`*:: +*`destination.port`*:: + -- -Internal name of the file, provided at compile-time. +Port of the destination. -type: keyword +type: long -example: MSPAINT.EXE +format: string -- -*`file.pe.product`*:: +*`destination.registered_domain`*:: + -- -Internal product name of the file, provided at compile-time. +The highest registered destination domain, stripped of the subdomain. +For example, the registered domain for "foo.google.com" is "google.com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". type: keyword -example: Microsoft® Windows® Operating System +example: google.com -- -*`file.size`*:: +*`destination.top_level_domain`*:: + -- -File size in bytes. -Only relevant when `file.type` is "file". +The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". -type: long +type: keyword -example: 16384 +example: co.uk -- -*`file.target_path`*:: +*`destination.user.domain`*:: + -- -Target path for symlinks. +Name of the directory the user is a member of. +For example, an LDAP or Active Directory domain name. type: keyword -- -*`file.target_path.text`*:: +*`destination.user.email`*:: + -- -type: text +User email address. + +type: keyword -- -*`file.type`*:: +*`destination.user.full_name`*:: + -- -File type (file, dir, or symlink). +User's full name, if available. type: keyword -example: file +example: Albert Einstein -- -*`file.uid`*:: +*`destination.user.full_name.text`*:: + -- -The user ID (UID) or security identifier (SID) of the file owner. - -type: keyword - -example: 1001 +type: text -- -[float] -=== geo - -Geo fields can carry data about a specific location related to an event. -This geolocation information can be derived from techniques such as Geo IP, or be user-supplied. - - -*`geo.city_name`*:: +*`destination.user.group.domain`*:: + -- -City name. +Name of the directory the group is a member of. +For example, an LDAP or Active Directory domain name. type: keyword -example: Montreal - -- -*`geo.continent_name`*:: +*`destination.user.group.id`*:: + -- -Name of the continent. +Unique identifier for the group on the system/platform. type: keyword -example: North America - -- -*`geo.country_iso_code`*:: +*`destination.user.group.name`*:: + -- -Country ISO code. +Name of the group. type: keyword -example: CA - -- -*`geo.country_name`*:: +*`destination.user.hash`*:: + -- -Country name. +Unique user hash to correlate information for a user in anonymized form. +Useful if `user.id` or `user.name` contain confidential information and cannot be used. type: keyword -example: Canada - -- -*`geo.location`*:: +*`destination.user.id`*:: + -- -Longitude and latitude. - -type: geo_point +Unique identifiers of the user. -example: { "lon": -73.614830, "lat": 45.505918 } +type: keyword -- -*`geo.name`*:: +*`destination.user.name`*:: + -- -User-defined description of a location, at the level of granularity they care about. -Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. -Not typically used in automated geolocation. +Short name or login of the user. type: keyword -example: boston-dc +example: albert -- -*`geo.region_iso_code`*:: +*`destination.user.name.text`*:: + -- -Region ISO code. - -type: keyword - -example: CA-QC +type: text -- -*`geo.region_name`*:: -+ --- -Region name. +[float] +=== dll -type: keyword +These fields contain information about code libraries dynamically loaded into processes. -example: Quebec +Many operating systems refer to "shared code libraries" with different names, but this field set refers to all of the following: +* Dynamic-link library (`.dll`) commonly used on Windows +* Shared Object (`.so`) commonly used on Unix-like operating systems +* Dynamic library (`.dylib`) commonly used on macOS + +*`dll.code_signature.exists`*:: ++ -- +Boolean to capture if a signature is present. -[float] -=== group +type: boolean -The group fields are meant to represent groups that are relevant to the event. +example: true +-- -*`group.domain`*:: +*`dll.code_signature.status`*:: + -- -Name of the directory the group is a member of. -For example, an LDAP or Active Directory domain name. +Additional information about the certificate status. +This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. type: keyword +example: ERROR_UNTRUSTED_ROOT + -- -*`group.id`*:: +*`dll.code_signature.subject_name`*:: + -- -Unique identifier for the group on the system/platform. +Subject name of the code signer type: keyword +example: Microsoft Corporation + -- -*`group.name`*:: +*`dll.code_signature.trusted`*:: + -- -Name of the group. +Stores the trust status of the certificate chain. +Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. -type: keyword +type: boolean + +example: true -- -[float] -=== hash +*`dll.code_signature.valid`*:: ++ +-- +Boolean to capture if the digital signature is verified against the binary content. +Leave unpopulated if a certificate was unchecked. -The hash fields represent different hash algorithms and their values. -Field names for common hashes (e.g. MD5, SHA1) are predefined. Add fields for other hashes by lowercasing the hash algorithm name and using underscore separators as appropriate (snake case, e.g. sha3_512). +type: boolean +example: true -*`hash.md5`*:: +-- + +*`dll.hash.md5`*:: + -- MD5 hash. @@ -10106,7 +9932,7 @@ type: keyword -- -*`hash.sha1`*:: +*`dll.hash.sha1`*:: + -- SHA1 hash. @@ -10115,7 +9941,7 @@ type: keyword -- -*`hash.sha256`*:: +*`dll.hash.sha256`*:: + -- SHA256 hash. @@ -10124,7 +9950,7 @@ type: keyword -- -*`hash.sha512`*:: +*`dll.hash.sha512`*:: + -- SHA512 hash. @@ -10133,990 +9959,1075 @@ type: keyword -- -[float] -=== host +*`dll.name`*:: ++ +-- +Name of the library. +This generally maps to the name of the file on disk. -A host is defined as a general computing instance. -ECS host.* fields should be populated with details about the host on which the event happened, or from which the measurement was taken. Host types include hardware, virtual machines, Docker containers, and Kubernetes nodes. +type: keyword +example: kernel32.dll -*`host.architecture`*:: +-- + +*`dll.path`*:: + -- -Operating system architecture. +Full file path of the library. type: keyword -example: x86_64 +example: C:\Windows\System32\kernel32.dll -- -*`host.domain`*:: +*`dll.pe.company`*:: + -- -Name of the domain of which the host is a member. -For example, on Windows this could be the host's Active Directory domain or NetBIOS domain name. For Linux this could be the domain of the host's LDAP provider. +Internal company name of the file, provided at compile-time. type: keyword -example: CONTOSO +example: Microsoft Corporation -- -*`host.geo.city_name`*:: +*`dll.pe.description`*:: + -- -City name. +Internal description of the file, provided at compile-time. type: keyword -example: Montreal +example: Paint -- -*`host.geo.continent_name`*:: +*`dll.pe.file_version`*:: + -- -Name of the continent. +Internal version of the file, provided at compile-time. type: keyword -example: North America +example: 6.3.9600.17415 -- -*`host.geo.country_iso_code`*:: +*`dll.pe.original_file_name`*:: + -- -Country ISO code. +Internal name of the file, provided at compile-time. type: keyword -example: CA +example: MSPAINT.EXE -- -*`host.geo.country_name`*:: +*`dll.pe.product`*:: + -- -Country name. +Internal product name of the file, provided at compile-time. type: keyword -example: Canada +example: Microsoft® Windows® Operating System -- -*`host.geo.location`*:: +[float] +=== dns + +Fields describing DNS queries and answers. +DNS events should either represent a single DNS query prior to getting answers (`dns.type:query`) or they should represent a full exchange and contain the query details as well as all of the answers that were provided for this query (`dns.type:answer`). + + +*`dns.answers`*:: + -- -Longitude and latitude. - -type: geo_point +An array containing an object for each answer section returned by the server. +The main keys that should be present in these objects are defined by ECS. Records that have more information may contain more keys than what ECS defines. +Not all DNS data sources give all details about DNS answers. At minimum, answer objects must contain the `data` key. If more information is available, map as much of it to ECS as possible, and add any additional fields to the answer objects as custom fields. -example: { "lon": -73.614830, "lat": 45.505918 } +type: object -- -*`host.geo.name`*:: +*`dns.answers.class`*:: + -- -User-defined description of a location, at the level of granularity they care about. -Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. -Not typically used in automated geolocation. +The class of DNS data contained in this resource record. type: keyword -example: boston-dc +example: IN -- -*`host.geo.region_iso_code`*:: +*`dns.answers.data`*:: + -- -Region ISO code. +The data describing the resource. +The meaning of this data depends on the type and class of the resource record. type: keyword -example: CA-QC +example: 10.10.10.10 -- -*`host.geo.region_name`*:: +*`dns.answers.name`*:: + -- -Region name. +The domain name to which this resource record pertains. +If a chain of CNAME is being resolved, each answer's `name` should be the one that corresponds with the answer's `data`. It should not simply be the original `question.name` repeated. type: keyword -example: Quebec +example: www.google.com -- -*`host.hostname`*:: +*`dns.answers.ttl`*:: + -- -Hostname of the host. -It normally contains what the `hostname` command returns on the host machine. +The time interval in seconds that this resource record may be cached before it should be discarded. Zero values mean that the data should not be cached. -type: keyword +type: long + +example: 180 -- -*`host.id`*:: +*`dns.answers.type`*:: + -- -Unique host id. -As hostname is not always unique, use values that are meaningful in your environment. -Example: The current usage of `beat.name`. +The type of data contained in this resource record. type: keyword +example: CNAME + -- -*`host.ip`*:: +*`dns.header_flags`*:: + -- -Host ip addresses. +Array of 2 letter DNS header flags. +Expected values are: AA, TC, RD, RA, AD, CD, DO. -type: ip +type: keyword + +example: ['RD', 'RA'] -- -*`host.mac`*:: +*`dns.id`*:: + -- -Host mac addresses. +The DNS packet identifier assigned by the program that generated the query. The identifier is copied to the response. type: keyword +example: 62111 + -- -*`host.name`*:: +*`dns.op_code`*:: + -- -Name of the host. -It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use. +The DNS operation code that specifies the kind of query in the message. This value is set by the originator of a query and copied into the response. type: keyword +example: QUERY + -- -*`host.os.family`*:: +*`dns.question.class`*:: + -- -OS family (such as redhat, debian, freebsd, windows). +The class of records being queried. type: keyword -example: debian +example: IN -- -*`host.os.full`*:: +*`dns.question.name`*:: + -- -Operating system name, including the version or code name. +The name being queried. +If the name field contains non-printable characters (below 32 or above 126), those characters should be represented as escaped base 10 integers (\DDD). Back slashes and quotes should be escaped. Tabs, carriage returns, and line feeds should be converted to \t, \r, and \n respectively. type: keyword -example: Mac OS Mojave +example: www.google.com -- -*`host.os.full.text`*:: +*`dns.question.registered_domain`*:: + -- -type: text +The highest registered domain, stripped of the subdomain. +For example, the registered domain for "foo.google.com" is "google.com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". + +type: keyword + +example: google.com -- -*`host.os.kernel`*:: +*`dns.question.subdomain`*:: + -- -Operating system kernel version as a raw string. +The subdomain is all of the labels under the registered_domain. +If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period. type: keyword -example: 4.4.0-112-generic +example: www -- -*`host.os.name`*:: +*`dns.question.top_level_domain`*:: + -- -Operating system name, without the version. +The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". type: keyword -example: Mac OS X +example: co.uk -- -*`host.os.name.text`*:: +*`dns.question.type`*:: + -- -type: text +The type of record being queried. + +type: keyword + +example: AAAA -- -*`host.os.platform`*:: +*`dns.resolved_ip`*:: + -- -Operating system platform (such centos, ubuntu, windows). +Array containing all IPs seen in `answers.data`. +The `answers` array can be difficult to use, because of the variety of data formats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip` makes it possible to index them as IP addresses, and makes them easier to visualize and query for. -type: keyword +type: ip -example: darwin +example: ['10.10.10.10', '10.10.10.11'] -- -*`host.os.version`*:: +*`dns.response_code`*:: + -- -Operating system version as a raw string. +The DNS response code. type: keyword -example: 10.14.1 +example: NOERROR -- -*`host.type`*:: +*`dns.type`*:: + -- -Type of host. -For Cloud providers this can be the machine type like `t2.medium`. If vm, this could be the container, for example, or other information meaningful in your environment. +The type of DNS event captured, query or answer. +If your source of DNS events only gives you DNS queries, you should only create dns events of type `dns.type:query`. +If your source of DNS events gives you answers as well, you should create one event per query (optionally as soon as the query is seen). And a second event containing all query details as well as an array of answers. type: keyword --- +example: answer -*`host.uptime`*:: -+ -- -Seconds the host has been up. -type: long +[float] +=== ecs -example: 1325 +Meta-information specific to ECS. --- -*`host.user.domain`*:: +*`ecs.version`*:: + -- -Name of the directory the user is a member of. -For example, an LDAP or Active Directory domain name. +ECS version this event conforms to. `ecs.version` is a required field and must exist in all events. +When querying across multiple indices -- which may conform to slightly different ECS versions -- this field lets integrations adjust to the schema version of the events. type: keyword +example: 1.0.0 + +required: True + -- -*`host.user.email`*:: +[float] +=== error + +These fields can represent errors of any kind. +Use them for errors that happen while fetching events or in cases where the event itself contains an error. + + +*`error.code`*:: + -- -User email address. +Error code describing the error. type: keyword -- -*`host.user.full_name`*:: +*`error.id`*:: + -- -User's full name, if available. +Unique identifier for the error. type: keyword -example: Albert Einstein - -- -*`host.user.full_name.text`*:: +*`error.message`*:: + -- +Error message. + type: text -- -*`host.user.group.domain`*:: +*`error.stack_trace`*:: + -- -Name of the directory the group is a member of. -For example, an LDAP or Active Directory domain name. +The stack trace of this error in plain text. type: keyword -- -*`host.user.group.id`*:: +*`error.stack_trace.text`*:: + -- -Unique identifier for the group on the system/platform. - -type: keyword +type: text -- -*`host.user.group.name`*:: +*`error.type`*:: + -- -Name of the group. +The type of the error, for example the class name of the exception. type: keyword --- +example: java.lang.NullPointerException -*`host.user.hash`*:: -+ -- -Unique user hash to correlate information for a user in anonymized form. -Useful if `user.id` or `user.name` contain confidential information and cannot be used. -type: keyword +[float] +=== event --- +The event fields are used for context information about the log or metric event itself. +A log is defined as an event containing details of something that happened. Log events must include the time at which the thing happened. Examples of log events include a process starting on a host, a network packet being sent from a source to a destination, or a network connection between a client and a server being initiated or closed. A metric is defined as an event containing one or more numerical measurements and the time at which the measurement was taken. Examples of metric events include memory pressure measured on a host and device temperature. See the `event.kind` definition in this section for additional details about metric and state events. -*`host.user.id`*:: + +*`event.action`*:: + -- -Unique identifiers of the user. +The action captured by the event. +This describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer. type: keyword +example: user-password-change + -- -*`host.user.name`*:: +*`event.category`*:: + -- -Short name or login of the user. +This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy. +`event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory. +This field is an array. This will allow proper categorization of some events that fall in multiple categories. type: keyword -example: albert +example: authentication -- -*`host.user.name.text`*:: +*`event.code`*:: + -- -type: text - --- +Identification code for this event, if one exists. +Some event sources use event codes to identify messages unambiguously, regardless of message language or wording adjustments over time. An example of this is the Windows Event ID. -[float] -=== http +type: keyword -Fields related to HTTP activity. Use the `url` field set to store the url of the request. +example: 4648 +-- -*`http.request.body.bytes`*:: +*`event.created`*:: + -- -Size in bytes of the request body. - -type: long +event.created contains the date/time when the event was first read by an agent, or by your pipeline. +This field is distinct from @timestamp in that @timestamp typically contain the time extracted from the original event. +In most situations, these two timestamps will be slightly different. The difference can be used to calculate the delay between your source generating an event, and the time when your agent first processed it. This can be used to monitor your agent's or pipeline's ability to keep up with your event source. +In case the two timestamps are identical, @timestamp should be used. -example: 887 +type: date -format: bytes +example: 2016-05-23T08:05:34.857Z -- -*`http.request.body.content`*:: +*`event.dataset`*:: + -- -The full HTTP request body. +Name of the dataset. +If an event source publishes more than one type of log or events (e.g. access log, error log), the dataset is used to specify which one the event comes from. +It's recommended but not required to start the dataset name with the module name, followed by a dot, then the dataset name. type: keyword -example: Hello world +example: apache.access -- -*`http.request.body.content.text`*:: +*`event.duration`*:: + -- -type: text +Duration of the event in nanoseconds. +If event.start and event.end are known this value should be the difference between the end and start time. + +type: long + +format: duration -- -*`http.request.bytes`*:: +*`event.end`*:: + -- -Total size in bytes of the request (body and headers). - -type: long - -example: 1437 +event.end contains the date when the event ended or when the activity was last observed. -format: bytes +type: date -- -*`http.request.method`*:: +*`event.hash`*:: + -- -HTTP request method. -The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". +Hash (perhaps logstash fingerprint) of raw field to be able to demonstrate log integrity. type: keyword -example: get, post, put +example: 123456789012345678901234567890ABCD -- -*`http.request.referrer`*:: +*`event.id`*:: + -- -Referrer for this HTTP request. +Unique ID to describe the event. type: keyword -example: https://blog.example.com/ +example: 8a4f500d -- -*`http.response.body.bytes`*:: +*`event.ingested`*:: + -- -Size in bytes of the response body. - -type: long +Timestamp when an event arrived in the central data store. +This is different from `@timestamp`, which is when the event originally occurred. It's also different from `event.created`, which is meant to capture the first time an agent saw the event. +In normal conditions, assuming no tampering, the timestamps should chronologically look like this: `@timestamp` < `event.created` < `event.ingested`. -example: 887 +type: date -format: bytes +example: 2016-05-23T08:05:35.101Z -- -*`http.response.body.content`*:: +*`event.kind`*:: + -- -The full HTTP response body. +This is one of four ECS Categorization Fields, and indicates the highest level in the ECS category hierarchy. +`event.kind` gives high-level information about what type of information the event contains, without being specific to the contents of the event. For example, values of this field distinguish alert events from metric events. +The value of this field can be used to inform how these kinds of events should be handled. They may warrant different retention, different access control, it may also help understand whether the data coming in at a regular interval or not. type: keyword -example: Hello world +example: alert -- -*`http.response.body.content.text`*:: +*`event.module`*:: + -- -type: text +Name of the module this data is coming from. +If your monitoring agent supports the concept of modules or plugins to process events of a given source (e.g. Apache logs), `event.module` should contain the name of this module. + +type: keyword + +example: apache -- -*`http.response.bytes`*:: +*`event.original`*:: + -- -Total size in bytes of the response (body and headers). +Raw text message of entire event. Used to demonstrate log integrity. +This field is not indexed and doc_values are disabled. It cannot be searched, but it can be retrieved from `_source`. -type: long - -example: 1437 +type: keyword -format: bytes +example: Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100| worm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232 -- -*`http.response.status_code`*:: +*`event.outcome`*:: + -- -HTTP response status code. - -type: long +This is one of four ECS Categorization Fields, and indicates the lowest level in the ECS category hierarchy. +`event.outcome` simply denotes whether the event represents a success or a failure from the perspective of the entity that produced the event. +Note that when a single transaction is described in multiple events, each event may populate different values of `event.outcome`, according to their perspective. +Also note that in the case of a compound event (a single event that contains multiple logical events), this field should be populated with the value that best captures the overall success or failure from the perspective of the event producer. +Further note that not all events will have an associated outcome. For example, this field is generally not populated for metric events, events with `event.type:info`, or any events for which an outcome does not make logical sense. -example: 404 +type: keyword -format: string +example: success -- -*`http.version`*:: +*`event.provider`*:: + -- -HTTP version. +Source of the event. +Event transports such as Syslog or the Windows Event Log typically mention the source of an event. It can be the name of the software that generated the event (e.g. Sysmon, httpd), or of a subsystem of the operating system (kernel, Microsoft-Windows-Security-Auditing). type: keyword -example: 1.1 +example: kernel -- -[float] -=== interface - -The interface fields are used to record ingress and egress interface information when reported by an observer (e.g. firewall, router, load balancer) in the context of the observer handling a network connection. In the case of a single observer interface (e.g. network sensor on a span port) only the observer.ingress information should be populated. - - -*`interface.alias`*:: +*`event.reference`*:: + -- -Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming. +Reference URL linking to additional information about this event. +This URL links to a static definition of the this event. Alert events, indicated by `event.kind:alert`, are a common use case for this field. type: keyword -example: outside +example: https://system.vendor.com/event/#0001234 -- -*`interface.id`*:: +*`event.risk_score`*:: + -- -Interface ID as reported by an observer (typically SNMP interface ID). - -type: keyword +Risk score or priority of the event (e.g. security solutions). Use your system's original value here. -example: 10 +type: float -- -*`interface.name`*:: +*`event.risk_score_norm`*:: + -- -Interface name as reported by the system. - -type: keyword +Normalized risk score or priority of the event, on a scale of 0 to 100. +This is mainly useful if you use more than one system that assigns risk scores, and you want to see a normalized value across all systems. -example: eth0 +type: float -- -[float] -=== log - -Details about the event's logging mechanism or logging transport. -The log.* fields are typically populated with details about the logging mechanism used to create and/or transport the event. For example, syslog details belong under `log.syslog.*`. -The details specific to your event source are typically not logged under `log.*`, but rather in `event.*` or in other ECS fields. - - -*`log.level`*:: +*`event.sequence`*:: + -- -Original log level of the log event. -If the source of the event provides a log level or textual severity, this is the one that goes in `log.level`. If your source doesn't specify one, you may put your event transport's severity here (e.g. Syslog severity). -Some examples are `warn`, `err`, `i`, `informational`. +Sequence number of the event. +The sequence number is a value published by some event sources, to make the exact ordering of events unambiguous, regardless of the timestamp precision. -type: keyword +type: long -example: error +format: string -- -*`log.logger`*:: +*`event.severity`*:: + -- -The name of the logger inside an application. This is usually the name of the class which initialized the logger, or can be a custom name. +The numeric severity of the event according to your event source. +What the different severity values mean can be different between sources and use cases. It's up to the implementer to make sure severities are consistent across events from the same source. +The Syslog severity belongs in `log.syslog.severity.code`. `event.severity` is meant to represent the severity according to the event source (e.g. firewall, IDS). If the event source does not publish its own severity, you may optionally copy the `log.syslog.severity.code` to `event.severity`. -type: keyword +type: long -example: org.elasticsearch.bootstrap.Bootstrap +example: 7 + +format: string -- -*`log.origin.file.line`*:: +*`event.start`*:: + -- -The line number of the file containing the source code which originated the log event. - -type: integer +event.start contains the date when the event started or when the activity was first observed. -example: 42 +type: date -- -*`log.origin.file.name`*:: +*`event.timezone`*:: + -- -The name of the file containing the source code which originated the log event. Note that this is not the name of the log file. +This field should be populated when the event's timestamp does not include timezone information already (e.g. default Syslog timestamps). It's optional otherwise. +Acceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"), abbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00"). type: keyword -example: Bootstrap.java - -- -*`log.origin.function`*:: +*`event.type`*:: + -- -The name of the function or method which originated the log event. +This is one of four ECS Categorization Fields, and indicates the third level in the ECS category hierarchy. +`event.type` represents a categorization "sub-bucket" that, when used along with the `event.category` field values, enables filtering events down to a level appropriate for single visualization. +This field is an array. This will allow proper categorization of some events that fall in multiple event types. type: keyword -example: init - -- -*`log.original`*:: +*`event.url`*:: + -- -This is the original log message and contains the full log message before splitting it up in multiple parts. -In contrast to the `message` field which can contain an extracted part of the log message, this field contains the original, full log message. It can have already some modifications applied like encoding or new lines removed to clean up the log message. -This field is not indexed and doc_values are disabled so it can't be queried but the value can be retrieved from `_source`. +URL linking to an external system to continue investigation of this event. +This URL links to another system where in-depth investigation of the specific occurence of this event can take place. Alert events, indicated by `event.kind:alert`, are a common use case for this field. type: keyword -example: Sep 19 08:26:10 localhost My log +example: https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe -- -*`log.syslog`*:: -+ --- -The Syslog metadata of the event, if the event was transmitted via Syslog. Please see RFCs 5424 or 3164. +[float] +=== file -type: object +A file is defined as a set of information that has been created on, or has existed on a filesystem. +File objects can be associated with host events, network events, and/or file events (e.g., those produced by File Integrity Monitoring [FIM] products or services). File fields provide details about the affected file associated with the event or metric. --- -*`log.syslog.facility.code`*:: +*`file.accessed`*:: + -- -The Syslog numeric facility of the log event, if available. -According to RFCs 5424 and 3164, this value should be an integer between 0 and 23. - -type: long - -example: 23 +Last time the file was accessed. +Note that not all filesystems keep track of access time. -format: string +type: date -- -*`log.syslog.facility.name`*:: +*`file.attributes`*:: + -- -The Syslog text-based facility of the log event, if available. +Array of file attributes. +Attributes names will vary by platform. Here's a non-exhaustive list of values that are expected in this field: archive, compressed, directory, encrypted, execute, hidden, read, readonly, system, write. type: keyword -example: local7 +example: ["readonly", "system"] -- -*`log.syslog.priority`*:: +*`file.code_signature.exists`*:: + -- -Syslog numeric priority of the event, if available. -According to RFCs 5424 and 3164, the priority is 8 * facility + severity. This number is therefore expected to contain a value between 0 and 191. - -type: long +Boolean to capture if a signature is present. -example: 135 +type: boolean -format: string +example: true -- -*`log.syslog.severity.code`*:: +*`file.code_signature.status`*:: + -- -The Syslog numeric severity of the log event, if available. -If the event source publishing via Syslog provides a different numeric severity value (e.g. firewall, IDS), your source's numeric severity should go to `event.severity`. If the event source does not specify a distinct severity, you can optionally copy the Syslog severity to `event.severity`. +Additional information about the certificate status. +This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. -type: long +type: keyword -example: 3 +example: ERROR_UNTRUSTED_ROOT -- -*`log.syslog.severity.name`*:: +*`file.code_signature.subject_name`*:: + -- -The Syslog numeric severity of the log event, if available. -If the event source publishing via Syslog provides a different severity value (e.g. firewall, IDS), your source's text severity should go to `log.level`. If the event source does not specify a distinct severity, you can optionally copy the Syslog severity to `log.level`. +Subject name of the code signer type: keyword -example: Error +example: Microsoft Corporation -- -[float] -=== network +*`file.code_signature.trusted`*:: ++ +-- +Stores the trust status of the certificate chain. +Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. -The network is defined as the communication path over which a host or network event happens. -The network.* fields should be populated with details about the network activity associated with an event. +type: boolean + +example: true +-- -*`network.application`*:: +*`file.code_signature.valid`*:: + -- -A name given to an application level protocol. This can be arbitrarily assigned for things like microservices, but also apply to things like skype, icq, facebook, twitter. This would be used in situations where the vendor or service can be decoded such as from the source/dest IP owners, ports, or wire format. -The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". +Boolean to capture if the digital signature is verified against the binary content. +Leave unpopulated if a certificate was unchecked. -type: keyword +type: boolean -example: aim +example: true -- -*`network.bytes`*:: +*`file.created`*:: + -- -Total bytes transferred in both directions. -If `source.bytes` and `destination.bytes` are known, `network.bytes` is their sum. +File creation time. +Note that not all filesystems store the creation time. -type: long +type: date -example: 368 +-- -format: bytes +*`file.ctime`*:: ++ +-- +Last time the file attributes or metadata changed. +Note that changes to the file content will update `mtime`. This implies `ctime` will be adjusted at the same time, since `mtime` is an attribute of the file. + +type: date -- -*`network.community_id`*:: +*`file.device`*:: + -- -A hash of source and destination IPs and ports, as well as the protocol used in a communication. This is a tool-agnostic standard to identify flows. -Learn more at https://github.com/corelight/community-id-spec. +Device that is the source of the file. type: keyword -example: 1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0= +example: sda -- -*`network.direction`*:: +*`file.directory`*:: + -- -Direction of the network traffic. -Recommended values are: - * inbound - * outbound - * internal - * external - * unknown - -When mapping events from a host-based monitoring context, populate this field from the host's point of view. -When mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter. +Directory where the file is located. It should include the drive letter, when appropriate. type: keyword -example: inbound +example: /home/alice -- -*`network.forwarded_ip`*:: +*`file.drive_letter`*:: + -- -Host IP address when the source IP address is the proxy. +Drive letter where the file is located. This field is only relevant on Windows. +The value should be uppercase, and not include the colon. -type: ip +type: keyword -example: 192.1.1.2 +example: C -- -*`network.iana_number`*:: +*`file.extension`*:: + -- -IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml). Standardized list of protocols. This aligns well with NetFlow and sFlow related logs which use the IANA Protocol Number. +File extension. type: keyword -example: 6 +example: png -- -*`network.inner`*:: +*`file.gid`*:: + -- -Network.inner fields are added in addition to network.vlan fields to describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed fields include vlan.id and vlan.name. Inner vlan fields are typically used when sending traffic with multiple 802.1q encapsulations to a network sensor (e.g. Zeek, Wireshark.) +Primary group ID (GID) of the file. -type: object +type: keyword + +example: 1001 -- -*`network.inner.vlan.id`*:: +*`file.group`*:: + -- -VLAN ID as reported by the observer. +Primary group name of the file. type: keyword -example: 10 +example: alice -- -*`network.inner.vlan.name`*:: +*`file.hash.md5`*:: + -- -Optional VLAN name as reported by the observer. +MD5 hash. type: keyword -example: outside +-- +*`file.hash.sha1`*:: ++ -- +SHA1 hash. -*`network.name`*:: +type: keyword + +-- + +*`file.hash.sha256`*:: + -- -Name given by operators to sections of their network. +SHA256 hash. type: keyword -example: Guest Wifi +-- +*`file.hash.sha512`*:: ++ -- +SHA512 hash. -*`network.packets`*:: +type: keyword + +-- + +*`file.inode`*:: + -- -Total packets transferred in both directions. -If `source.packets` and `destination.packets` are known, `network.packets` is their sum. +Inode representing the file in the filesystem. -type: long +type: keyword -example: 24 +example: 256383 -- -*`network.protocol`*:: +*`file.mime_type`*:: + -- -L7 Network protocol name. ex. http, lumberjack, transport protocol. -The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". +MIME type should identify the format of the file or stream of bytes using https://www.iana.org/assignments/media-types/media-types.xhtml[IANA official types], where possible. When more than one type is applicable, the most specific type should be used. type: keyword -example: http - -- -*`network.transport`*:: +*`file.mode`*:: + -- -Same as network.iana_number, but instead using the Keyword name of the transport layer (udp, tcp, ipv6-icmp, etc.) -The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". +Mode of the file in octal representation. type: keyword -example: tcp +example: 0640 -- -*`network.type`*:: +*`file.mtime`*:: + -- -In the OSI Model this would be the Network Layer. ipv4, ipv6, ipsec, pim, etc -The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". +Last time the file content was modified. + +type: date + +-- + +*`file.name`*:: ++ +-- +Name of the file including the extension, without the directory. type: keyword -example: ipv4 +example: example.png -- -*`network.vlan.id`*:: +*`file.owner`*:: + -- -VLAN ID as reported by the observer. +File owner's username. type: keyword -example: 10 +example: alice -- -*`network.vlan.name`*:: +*`file.path`*:: + -- -Optional VLAN name as reported by the observer. +Full path to the file, including the file name. It should include the drive letter, when appropriate. type: keyword -example: outside +example: /home/alice/example.png -- -[float] -=== observer +*`file.path.text`*:: ++ +-- +type: text -An observer is defined as a special network, security, or application device used to detect, observe, or create network, security, or application-related events and metrics. -This could be a custom hardware appliance or a server that has been configured to run special network, security, or application software. Examples include firewalls, web proxies, intrusion detection/prevention systems, network monitoring sensors, web application firewalls, data loss prevention systems, and APM servers. The observer.* fields shall be populated with details of the system, if any, that detects, observes and/or creates a network, security, or application event or metric. Message queues and ETL components used in processing events or metrics are not considered observers in ECS. +-- +*`file.pe.company`*:: ++ +-- +Internal company name of the file, provided at compile-time. -*`observer.egress`*:: +type: keyword + +example: Microsoft Corporation + +-- + +*`file.pe.description`*:: + -- -Observer.egress holds information like interface number and name, vlan, and zone information to classify egress traffic. Single armed monitoring such as a network sensor on a span port should only use observer.ingress to categorize traffic. +Internal description of the file, provided at compile-time. -type: object +type: keyword + +example: Paint -- -*`observer.egress.interface.alias`*:: +*`file.pe.file_version`*:: + -- -Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming. +Internal version of the file, provided at compile-time. type: keyword -example: outside +example: 6.3.9600.17415 -- -*`observer.egress.interface.id`*:: +*`file.pe.original_file_name`*:: + -- -Interface ID as reported by an observer (typically SNMP interface ID). +Internal name of the file, provided at compile-time. type: keyword -example: 10 +example: MSPAINT.EXE -- -*`observer.egress.interface.name`*:: +*`file.pe.product`*:: + -- -Interface name as reported by the system. +Internal product name of the file, provided at compile-time. type: keyword -example: eth0 +example: Microsoft® Windows® Operating System -- -*`observer.egress.vlan.id`*:: +*`file.size`*:: + -- -VLAN ID as reported by the observer. +File size in bytes. +Only relevant when `file.type` is "file". + +type: long + +example: 16384 + +-- + +*`file.target_path`*:: ++ +-- +Target path for symlinks. type: keyword -example: 10 +-- + +*`file.target_path.text`*:: ++ +-- +type: text -- -*`observer.egress.vlan.name`*:: +*`file.type`*:: + -- -Optional VLAN name as reported by the observer. +File type (file, dir, or symlink). type: keyword -example: outside +example: file -- -*`observer.egress.zone`*:: +*`file.uid`*:: + -- -Network zone of outbound traffic as reported by the observer to categorize the destination area of egress traffic, e.g. Internal, External, DMZ, HR, Legal, etc. +The user ID (UID) or security identifier (SID) of the file owner. type: keyword -example: Public_Internet +example: 1001 -- -*`observer.geo.city_name`*:: +[float] +=== geo + +Geo fields can carry data about a specific location related to an event. +This geolocation information can be derived from techniques such as Geo IP, or be user-supplied. + + +*`geo.city_name`*:: + -- City name. @@ -11127,7 +11038,7 @@ example: Montreal -- -*`observer.geo.continent_name`*:: +*`geo.continent_name`*:: + -- Name of the continent. @@ -11138,7 +11049,7 @@ example: North America -- -*`observer.geo.country_iso_code`*:: +*`geo.country_iso_code`*:: + -- Country ISO code. @@ -11149,7 +11060,7 @@ example: CA -- -*`observer.geo.country_name`*:: +*`geo.country_name`*:: + -- Country name. @@ -11160,7 +11071,7 @@ example: Canada -- -*`observer.geo.location`*:: +*`geo.location`*:: + -- Longitude and latitude. @@ -11171,7 +11082,7 @@ example: { "lon": -73.614830, "lat": 45.505918 } -- -*`observer.geo.name`*:: +*`geo.name`*:: + -- User-defined description of a location, at the level of granularity they care about. @@ -11184,7 +11095,7 @@ example: boston-dc -- -*`observer.geo.region_iso_code`*:: +*`geo.region_iso_code`*:: + -- Region ISO code. @@ -11195,7 +11106,7 @@ example: CA-QC -- -*`observer.geo.region_name`*:: +*`geo.region_name`*:: + -- Region name. @@ -11206,292 +11117,253 @@ example: Quebec -- -*`observer.hostname`*:: -+ --- -Hostname of the observer. +[float] +=== group -type: keyword +The group fields are meant to represent groups that are relevant to the event. --- -*`observer.ingress`*:: +*`group.domain`*:: + -- -Observer.ingress holds information like interface number and name, vlan, and zone information to classify ingress traffic. Single armed monitoring such as a network sensor on a span port should only use observer.ingress to categorize traffic. +Name of the directory the group is a member of. +For example, an LDAP or Active Directory domain name. -type: object +type: keyword -- -*`observer.ingress.interface.alias`*:: +*`group.id`*:: + -- -Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming. +Unique identifier for the group on the system/platform. type: keyword -example: outside - -- -*`observer.ingress.interface.id`*:: +*`group.name`*:: + -- -Interface ID as reported by an observer (typically SNMP interface ID). +Name of the group. type: keyword -example: 10 - -- -*`observer.ingress.interface.name`*:: +[float] +=== hash + +The hash fields represent different hash algorithms and their values. +Field names for common hashes (e.g. MD5, SHA1) are predefined. Add fields for other hashes by lowercasing the hash algorithm name and using underscore separators as appropriate (snake case, e.g. sha3_512). + + +*`hash.md5`*:: + -- -Interface name as reported by the system. +MD5 hash. type: keyword -example: eth0 - -- -*`observer.ingress.vlan.id`*:: +*`hash.sha1`*:: + -- -VLAN ID as reported by the observer. +SHA1 hash. type: keyword -example: 10 - -- -*`observer.ingress.vlan.name`*:: +*`hash.sha256`*:: + -- -Optional VLAN name as reported by the observer. +SHA256 hash. type: keyword -example: outside - -- -*`observer.ingress.zone`*:: +*`hash.sha512`*:: + -- -Network zone of incoming traffic as reported by the observer to categorize the source area of ingress traffic. e.g. internal, External, DMZ, HR, Legal, etc. +SHA512 hash. type: keyword -example: DMZ - -- -*`observer.ip`*:: -+ --- -IP addresses of the observer. +[float] +=== host -type: ip +A host is defined as a general computing instance. +ECS host.* fields should be populated with details about the host on which the event happened, or from which the measurement was taken. Host types include hardware, virtual machines, Docker containers, and Kubernetes nodes. --- -*`observer.mac`*:: +*`host.architecture`*:: + -- -MAC addresses of the observer +Operating system architecture. type: keyword +example: x86_64 + -- -*`observer.name`*:: +*`host.domain`*:: + -- -Custom name of the observer. -This is a name that can be given to an observer. This can be helpful for example if multiple firewalls of the same model are used in an organization. -If no custom name is needed, the field can be left empty. +Name of the domain of which the host is a member. +For example, on Windows this could be the host's Active Directory domain or NetBIOS domain name. For Linux this could be the domain of the host's LDAP provider. type: keyword -example: 1_proxySG +example: CONTOSO -- -*`observer.os.family`*:: +*`host.geo.city_name`*:: + -- -OS family (such as redhat, debian, freebsd, windows). +City name. type: keyword -example: debian +example: Montreal -- -*`observer.os.full`*:: +*`host.geo.continent_name`*:: + -- -Operating system name, including the version or code name. +Name of the continent. type: keyword -example: Mac OS Mojave - --- - -*`observer.os.full.text`*:: -+ --- -type: text +example: North America -- -*`observer.os.kernel`*:: +*`host.geo.country_iso_code`*:: + -- -Operating system kernel version as a raw string. +Country ISO code. type: keyword -example: 4.4.0-112-generic +example: CA -- -*`observer.os.name`*:: +*`host.geo.country_name`*:: + -- -Operating system name, without the version. +Country name. type: keyword -example: Mac OS X - --- - -*`observer.os.name.text`*:: -+ --- -type: text +example: Canada -- -*`observer.os.platform`*:: +*`host.geo.location`*:: + -- -Operating system platform (such centos, ubuntu, windows). +Longitude and latitude. -type: keyword +type: geo_point -example: darwin +example: { "lon": -73.614830, "lat": 45.505918 } -- -*`observer.os.version`*:: +*`host.geo.name`*:: + -- -Operating system version as a raw string. +User-defined description of a location, at the level of granularity they care about. +Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. +Not typically used in automated geolocation. type: keyword -example: 10.14.1 +example: boston-dc -- -*`observer.product`*:: +*`host.geo.region_iso_code`*:: + -- -The product name of the observer. +Region ISO code. type: keyword -example: s200 +example: CA-QC -- -*`observer.serial_number`*:: +*`host.geo.region_name`*:: + -- -Observer serial number. +Region name. type: keyword +example: Quebec + -- -*`observer.type`*:: +*`host.hostname`*:: + -- -The type of the observer the data is coming from. -There is no predefined list of observer types. Some examples are `forwarder`, `firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`. +Hostname of the host. +It normally contains what the `hostname` command returns on the host machine. type: keyword -example: firewall - -- -*`observer.vendor`*:: +*`host.id`*:: + -- -Vendor name of the observer. +Unique host id. +As hostname is not always unique, use values that are meaningful in your environment. +Example: The current usage of `beat.name`. type: keyword -example: Symantec - -- -*`observer.version`*:: +*`host.ip`*:: + -- -Observer version. +Host ip addresses. -type: keyword +type: ip -- -[float] -=== organization - -The organization fields enrich data with information about the company or entity the data is associated with. -These fields help you arrange or filter data stored in an index by one or multiple organizations. - - -*`organization.id`*:: +*`host.mac`*:: + -- -Unique identifier for the organization. +Host mac addresses. type: keyword -- -*`organization.name`*:: +*`host.name`*:: + -- -Organization name. +Name of the host. +It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use. type: keyword -- -*`organization.name.text`*:: -+ --- -type: text - --- - -[float] -=== os - -The OS fields contain information about the operating system. - - -*`os.family`*:: +*`host.os.family`*:: + -- OS family (such as redhat, debian, freebsd, windows). @@ -11502,7 +11374,7 @@ example: debian -- -*`os.full`*:: +*`host.os.full`*:: + -- Operating system name, including the version or code name. @@ -11513,14 +11385,14 @@ example: Mac OS Mojave -- -*`os.full.text`*:: +*`host.os.full.text`*:: + -- type: text -- -*`os.kernel`*:: +*`host.os.kernel`*:: + -- Operating system kernel version as a raw string. @@ -11531,7 +11403,7 @@ example: 4.4.0-112-generic -- -*`os.name`*:: +*`host.os.name`*:: + -- Operating system name, without the version. @@ -11542,14 +11414,14 @@ example: Mac OS X -- -*`os.name.text`*:: +*`host.os.name.text`*:: + -- type: text -- -*`os.platform`*:: +*`host.os.platform`*:: + -- Operating system platform (such centos, ubuntu, windows). @@ -11560,7 +11432,7 @@ example: darwin -- -*`os.version`*:: +*`host.os.version`*:: + -- Operating system version as a raw string. @@ -11571,2558 +11443,2590 @@ example: 10.14.1 -- -[float] -=== package - -These fields contain information about an installed software package. It contains general information about a package, such as name, version or size. It also contains installation details, such as time or location. - - -*`package.architecture`*:: +*`host.type`*:: + -- -Package architecture. +Type of host. +For Cloud providers this can be the machine type like `t2.medium`. If vm, this could be the container, for example, or other information meaningful in your environment. type: keyword -example: x86_64 - -- -*`package.build_version`*:: +*`host.uptime`*:: + -- -Additional information about the build version of the installed package. -For example use the commit SHA of a non-released package. +Seconds the host has been up. -type: keyword +type: long -example: 36f4f7e89dd61b0988b12ee000b98966867710cd +example: 1325 -- -*`package.checksum`*:: +*`host.user.domain`*:: + -- -Checksum of the installed package for verification. +Name of the directory the user is a member of. +For example, an LDAP or Active Directory domain name. type: keyword -example: 68b329da9893e34099c7d8ad5cb9c940 - -- -*`package.description`*:: +*`host.user.email`*:: + -- -Description of the package. +User email address. type: keyword -example: Open source programming language to build simple/reliable/efficient software. - -- -*`package.install_scope`*:: +*`host.user.full_name`*:: + -- -Indicating how the package was installed, e.g. user-local, global. +User's full name, if available. type: keyword -example: global +example: Albert Einstein -- -*`package.installed`*:: +*`host.user.full_name.text`*:: + -- -Time when package was installed. - -type: date +type: text -- -*`package.license`*:: +*`host.user.group.domain`*:: + -- -License under which the package was released. -Use a short name, e.g. the license identifier from SPDX License List where possible (https://spdx.org/licenses/). +Name of the directory the group is a member of. +For example, an LDAP or Active Directory domain name. type: keyword -example: Apache License 2.0 - -- -*`package.name`*:: +*`host.user.group.id`*:: + -- -Package name +Unique identifier for the group on the system/platform. type: keyword -example: go - -- -*`package.path`*:: +*`host.user.group.name`*:: + -- -Path where the package is installed. +Name of the group. type: keyword -example: /usr/local/Cellar/go/1.12.9/ - -- -*`package.reference`*:: +*`host.user.hash`*:: + -- -Home page or reference URL of the software in this package, if available. +Unique user hash to correlate information for a user in anonymized form. +Useful if `user.id` or `user.name` contain confidential information and cannot be used. type: keyword -example: https://golang.org - -- -*`package.size`*:: +*`host.user.id`*:: + -- -Package size in bytes. - -type: long - -example: 62231 +Unique identifiers of the user. -format: string +type: keyword -- -*`package.type`*:: +*`host.user.name`*:: + -- -Type of package. -This should contain the package file type, rather than the package manager name. Examples: rpm, dpkg, brew, npm, gem, nupkg, jar. +Short name or login of the user. type: keyword -example: rpm +example: albert -- -*`package.version`*:: +*`host.user.name.text`*:: + -- -Package version - -type: keyword - -example: 1.12.9 +type: text -- [float] -=== pe +=== http -These fields contain Windows Portable Executable (PE) metadata. +Fields related to HTTP activity. Use the `url` field set to store the url of the request. -*`pe.company`*:: +*`http.request.body.bytes`*:: + -- -Internal company name of the file, provided at compile-time. +Size in bytes of the request body. -type: keyword +type: long -example: Microsoft Corporation +example: 887 + +format: bytes -- -*`pe.description`*:: +*`http.request.body.content`*:: + -- -Internal description of the file, provided at compile-time. +The full HTTP request body. type: keyword -example: Paint +example: Hello world -- -*`pe.file_version`*:: +*`http.request.body.content.text`*:: + -- -Internal version of the file, provided at compile-time. - -type: keyword - -example: 6.3.9600.17415 +type: text -- -*`pe.original_file_name`*:: +*`http.request.bytes`*:: + -- -Internal name of the file, provided at compile-time. +Total size in bytes of the request (body and headers). -type: keyword +type: long -example: MSPAINT.EXE +example: 1437 + +format: bytes -- -*`pe.product`*:: +*`http.request.method`*:: + -- -Internal product name of the file, provided at compile-time. +HTTP request method. +The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". type: keyword -example: Microsoft® Windows® Operating System +example: get, post, put -- -[float] -=== process - -These fields contain information about a process. -These fields can help you correlate metrics information with a process id/name from a log message. The `process.pid` often stays in the metric itself and is copied to the global field for correlation. - - -*`process.args`*:: +*`http.request.referrer`*:: + -- -Array of process arguments, starting with the absolute path to the executable. -May be filtered to protect sensitive information. +Referrer for this HTTP request. type: keyword -example: ['/usr/bin/ssh', '-l', 'user', '10.0.0.16'] +example: https://blog.example.com/ -- -*`process.args_count`*:: +*`http.response.body.bytes`*:: + -- -Length of the process.args array. -This field can be useful for querying or performing bucket analysis on how many arguments were provided to start a process. More arguments may be an indication of suspicious activity. +Size in bytes of the response body. type: long -example: 4 - --- - -*`process.code_signature.exists`*:: -+ --- -Boolean to capture if a signature is present. - -type: boolean +example: 887 -example: true +format: bytes -- -*`process.code_signature.status`*:: +*`http.response.body.content`*:: + -- -Additional information about the certificate status. -This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. +The full HTTP response body. type: keyword -example: ERROR_UNTRUSTED_ROOT +example: Hello world -- -*`process.code_signature.subject_name`*:: +*`http.response.body.content.text`*:: + -- -Subject name of the code signer - -type: keyword - -example: Microsoft Corporation +type: text -- -*`process.code_signature.trusted`*:: +*`http.response.bytes`*:: + -- -Stores the trust status of the certificate chain. -Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. +Total size in bytes of the response (body and headers). -type: boolean +type: long -example: true +example: 1437 + +format: bytes -- -*`process.code_signature.valid`*:: +*`http.response.status_code`*:: + -- -Boolean to capture if the digital signature is verified against the binary content. -Leave unpopulated if a certificate was unchecked. +HTTP response status code. -type: boolean +type: long -example: true +example: 404 + +format: string -- -*`process.command_line`*:: +*`http.version`*:: + -- -Full command line that started the process, including the absolute path to the executable, and all arguments. -Some arguments may be filtered to protect sensitive information. +HTTP version. type: keyword -example: /usr/bin/ssh -l user 10.0.0.16 +example: 1.1 -- -*`process.command_line.text`*:: -+ --- -type: text +[float] +=== interface --- +The interface fields are used to record ingress and egress interface information when reported by an observer (e.g. firewall, router, load balancer) in the context of the observer handling a network connection. In the case of a single observer interface (e.g. network sensor on a span port) only the observer.ingress information should be populated. -*`process.entity_id`*:: + +*`interface.alias`*:: + -- -Unique identifier for the process. -The implementation of this is specified by the data source, but some examples of what could be used here are a process-generated UUID, Sysmon Process GUIDs, or a hash of some uniquely identifying components of a process. -Constructing a globally unique identifier is a common practice to mitigate PID reuse as well as to identify a specific process over time, across multiple monitored hosts. +Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming. type: keyword -example: c2c455d9f99375d +example: outside -- -*`process.executable`*:: +*`interface.id`*:: + -- -Absolute path to the process executable. +Interface ID as reported by an observer (typically SNMP interface ID). type: keyword -example: /usr/bin/ssh +example: 10 -- -*`process.executable.text`*:: +*`interface.name`*:: + -- -type: text +Interface name as reported by the system. --- +type: keyword + +example: eth0 -*`process.exit_code`*:: -+ -- -The exit code of the process, if this is a termination event. -The field should be absent if there is no exit code for the event (e.g. process start). -type: long +[float] +=== log -example: 137 +Details about the event's logging mechanism or logging transport. +The log.* fields are typically populated with details about the logging mechanism used to create and/or transport the event. For example, syslog details belong under `log.syslog.*`. +The details specific to your event source are typically not logged under `log.*`, but rather in `event.*` or in other ECS fields. --- -*`process.hash.md5`*:: +*`log.level`*:: + -- -MD5 hash. +Original log level of the log event. +If the source of the event provides a log level or textual severity, this is the one that goes in `log.level`. If your source doesn't specify one, you may put your event transport's severity here (e.g. Syslog severity). +Some examples are `warn`, `err`, `i`, `informational`. type: keyword +example: error + -- -*`process.hash.sha1`*:: +*`log.logger`*:: + -- -SHA1 hash. +The name of the logger inside an application. This is usually the name of the class which initialized the logger, or can be a custom name. type: keyword +example: org.elasticsearch.bootstrap.Bootstrap + -- -*`process.hash.sha256`*:: +*`log.origin.file.line`*:: + -- -SHA256 hash. +The line number of the file containing the source code which originated the log event. -type: keyword +type: integer + +example: 42 -- -*`process.hash.sha512`*:: +*`log.origin.file.name`*:: + -- -SHA512 hash. +The name of the file containing the source code which originated the log event. Note that this is not the name of the log file. type: keyword +example: Bootstrap.java + -- -*`process.name`*:: +*`log.origin.function`*:: + -- -Process name. -Sometimes called program name or similar. +The name of the function or method which originated the log event. type: keyword -example: ssh +example: init -- -*`process.name.text`*:: +*`log.original`*:: + -- -type: text +This is the original log message and contains the full log message before splitting it up in multiple parts. +In contrast to the `message` field which can contain an extracted part of the log message, this field contains the original, full log message. It can have already some modifications applied like encoding or new lines removed to clean up the log message. +This field is not indexed and doc_values are disabled so it can't be queried but the value can be retrieved from `_source`. + +type: keyword + +example: Sep 19 08:26:10 localhost My log -- -*`process.parent.args`*:: +*`log.syslog`*:: + -- -Array of process arguments. -May be filtered to protect sensitive information. - -type: keyword +The Syslog metadata of the event, if the event was transmitted via Syslog. Please see RFCs 5424 or 3164. -example: ['ssh', '-l', 'user', '10.0.0.16'] +type: object -- -*`process.parent.args_count`*:: +*`log.syslog.facility.code`*:: + -- -Length of the process.args array. -This field can be useful for querying or performing bucket analysis on how many arguments were provided to start a process. More arguments may be an indication of suspicious activity. +The Syslog numeric facility of the log event, if available. +According to RFCs 5424 and 3164, this value should be an integer between 0 and 23. type: long -example: 4 +example: 23 + +format: string -- -*`process.parent.code_signature.exists`*:: +*`log.syslog.facility.name`*:: + -- -Boolean to capture if a signature is present. +The Syslog text-based facility of the log event, if available. -type: boolean +type: keyword -example: true +example: local7 -- -*`process.parent.code_signature.status`*:: +*`log.syslog.priority`*:: + -- -Additional information about the certificate status. -This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. +Syslog numeric priority of the event, if available. +According to RFCs 5424 and 3164, the priority is 8 * facility + severity. This number is therefore expected to contain a value between 0 and 191. -type: keyword +type: long -example: ERROR_UNTRUSTED_ROOT +example: 135 + +format: string -- -*`process.parent.code_signature.subject_name`*:: +*`log.syslog.severity.code`*:: + -- -Subject name of the code signer +The Syslog numeric severity of the log event, if available. +If the event source publishing via Syslog provides a different numeric severity value (e.g. firewall, IDS), your source's numeric severity should go to `event.severity`. If the event source does not specify a distinct severity, you can optionally copy the Syslog severity to `event.severity`. -type: keyword +type: long -example: Microsoft Corporation +example: 3 -- -*`process.parent.code_signature.trusted`*:: +*`log.syslog.severity.name`*:: + -- -Stores the trust status of the certificate chain. -Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. - -type: boolean +The Syslog numeric severity of the log event, if available. +If the event source publishing via Syslog provides a different severity value (e.g. firewall, IDS), your source's text severity should go to `log.level`. If the event source does not specify a distinct severity, you can optionally copy the Syslog severity to `log.level`. -example: true +type: keyword --- +example: Error -*`process.parent.code_signature.valid`*:: -+ -- -Boolean to capture if the digital signature is verified against the binary content. -Leave unpopulated if a certificate was unchecked. -type: boolean +[float] +=== network -example: true +The network is defined as the communication path over which a host or network event happens. +The network.* fields should be populated with details about the network activity associated with an event. --- -*`process.parent.command_line`*:: +*`network.application`*:: + -- -Full command line that started the process, including the absolute path to the executable, and all arguments. -Some arguments may be filtered to protect sensitive information. +A name given to an application level protocol. This can be arbitrarily assigned for things like microservices, but also apply to things like skype, icq, facebook, twitter. This would be used in situations where the vendor or service can be decoded such as from the source/dest IP owners, ports, or wire format. +The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". type: keyword -example: /usr/bin/ssh -l user 10.0.0.16 +example: aim -- -*`process.parent.command_line.text`*:: +*`network.bytes`*:: + -- -type: text - --- +Total bytes transferred in both directions. +If `source.bytes` and `destination.bytes` are known, `network.bytes` is their sum. -*`process.parent.entity_id`*:: -+ --- -Unique identifier for the process. -The implementation of this is specified by the data source, but some examples of what could be used here are a process-generated UUID, Sysmon Process GUIDs, or a hash of some uniquely identifying components of a process. -Constructing a globally unique identifier is a common practice to mitigate PID reuse as well as to identify a specific process over time, across multiple monitored hosts. +type: long -type: keyword +example: 368 -example: c2c455d9f99375d +format: bytes -- -*`process.parent.executable`*:: +*`network.community_id`*:: + -- -Absolute path to the process executable. +A hash of source and destination IPs and ports, as well as the protocol used in a communication. This is a tool-agnostic standard to identify flows. +Learn more at https://github.com/corelight/community-id-spec. type: keyword -example: /usr/bin/ssh +example: 1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0= -- -*`process.parent.executable.text`*:: +*`network.direction`*:: + -- -type: text - --- +Direction of the network traffic. +Recommended values are: + * inbound + * outbound + * internal + * external + * unknown -*`process.parent.exit_code`*:: -+ --- -The exit code of the process, if this is a termination event. -The field should be absent if there is no exit code for the event (e.g. process start). +When mapping events from a host-based monitoring context, populate this field from the host's point of view. +When mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter. -type: long +type: keyword -example: 137 +example: inbound -- -*`process.parent.hash.md5`*:: +*`network.forwarded_ip`*:: + -- -MD5 hash. +Host IP address when the source IP address is the proxy. -type: keyword +type: ip + +example: 192.1.1.2 -- -*`process.parent.hash.sha1`*:: +*`network.iana_number`*:: + -- -SHA1 hash. +IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml). Standardized list of protocols. This aligns well with NetFlow and sFlow related logs which use the IANA Protocol Number. type: keyword +example: 6 + -- -*`process.parent.hash.sha256`*:: +*`network.inner`*:: + -- -SHA256 hash. +Network.inner fields are added in addition to network.vlan fields to describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed fields include vlan.id and vlan.name. Inner vlan fields are typically used when sending traffic with multiple 802.1q encapsulations to a network sensor (e.g. Zeek, Wireshark.) -type: keyword +type: object -- -*`process.parent.hash.sha512`*:: +*`network.inner.vlan.id`*:: + -- -SHA512 hash. +VLAN ID as reported by the observer. type: keyword +example: 10 + -- -*`process.parent.name`*:: +*`network.inner.vlan.name`*:: + -- -Process name. -Sometimes called program name or similar. +Optional VLAN name as reported by the observer. type: keyword -example: ssh +example: outside -- -*`process.parent.name.text`*:: +*`network.name`*:: + -- -type: text +Name given by operators to sections of their network. + +type: keyword + +example: Guest Wifi -- -*`process.parent.pgid`*:: +*`network.packets`*:: + -- -Identifier of the group of processes the process belongs to. +Total packets transferred in both directions. +If `source.packets` and `destination.packets` are known, `network.packets` is their sum. type: long -format: string +example: 24 -- -*`process.parent.pid`*:: +*`network.protocol`*:: + -- -Process id. - -type: long +L7 Network protocol name. ex. http, lumberjack, transport protocol. +The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". -example: 4242 +type: keyword -format: string +example: http -- -*`process.parent.ppid`*:: +*`network.transport`*:: + -- -Parent process' pid. - -type: long +Same as network.iana_number, but instead using the Keyword name of the transport layer (udp, tcp, ipv6-icmp, etc.) +The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". -example: 4241 +type: keyword -format: string +example: tcp -- -*`process.parent.start`*:: +*`network.type`*:: + -- -The time the process started. +In the OSI Model this would be the Network Layer. ipv4, ipv6, ipsec, pim, etc +The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS". -type: date +type: keyword -example: 2016-05-23T08:05:34.853Z +example: ipv4 -- -*`process.parent.thread.id`*:: +*`network.vlan.id`*:: + -- -Thread ID. - -type: long +VLAN ID as reported by the observer. -example: 4242 +type: keyword -format: string +example: 10 -- -*`process.parent.thread.name`*:: +*`network.vlan.name`*:: + -- -Thread name. +Optional VLAN name as reported by the observer. type: keyword -example: thread-0 +example: outside -- -*`process.parent.title`*:: +[float] +=== observer + +An observer is defined as a special network, security, or application device used to detect, observe, or create network, security, or application-related events and metrics. +This could be a custom hardware appliance or a server that has been configured to run special network, security, or application software. Examples include firewalls, web proxies, intrusion detection/prevention systems, network monitoring sensors, web application firewalls, data loss prevention systems, and APM servers. The observer.* fields shall be populated with details of the system, if any, that detects, observes and/or creates a network, security, or application event or metric. Message queues and ETL components used in processing events or metrics are not considered observers in ECS. + + +*`observer.egress`*:: + -- -Process title. -The proctitle, some times the same as process name. Can also be different: for example a browser setting its title to the web page currently opened. +Observer.egress holds information like interface number and name, vlan, and zone information to classify egress traffic. Single armed monitoring such as a network sensor on a span port should only use observer.ingress to categorize traffic. -type: keyword +type: object -- -*`process.parent.title.text`*:: +*`observer.egress.interface.alias`*:: + -- -type: text +Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming. + +type: keyword + +example: outside -- -*`process.parent.uptime`*:: +*`observer.egress.interface.id`*:: + -- -Seconds the process has been up. +Interface ID as reported by an observer (typically SNMP interface ID). -type: long +type: keyword -example: 1325 +example: 10 -- -*`process.parent.working_directory`*:: +*`observer.egress.interface.name`*:: + -- -The working directory of the process. +Interface name as reported by the system. type: keyword -example: /home/alice +example: eth0 -- -*`process.parent.working_directory.text`*:: +*`observer.egress.vlan.id`*:: + -- -type: text +VLAN ID as reported by the observer. + +type: keyword + +example: 10 -- -*`process.pe.company`*:: +*`observer.egress.vlan.name`*:: + -- -Internal company name of the file, provided at compile-time. +Optional VLAN name as reported by the observer. type: keyword -example: Microsoft Corporation +example: outside -- -*`process.pe.description`*:: +*`observer.egress.zone`*:: + -- -Internal description of the file, provided at compile-time. +Network zone of outbound traffic as reported by the observer to categorize the destination area of egress traffic, e.g. Internal, External, DMZ, HR, Legal, etc. type: keyword -example: Paint +example: Public_Internet -- -*`process.pe.file_version`*:: +*`observer.geo.city_name`*:: + -- -Internal version of the file, provided at compile-time. +City name. type: keyword -example: 6.3.9600.17415 +example: Montreal -- -*`process.pe.original_file_name`*:: +*`observer.geo.continent_name`*:: + -- -Internal name of the file, provided at compile-time. +Name of the continent. type: keyword -example: MSPAINT.EXE +example: North America -- -*`process.pe.product`*:: +*`observer.geo.country_iso_code`*:: + -- -Internal product name of the file, provided at compile-time. +Country ISO code. type: keyword -example: Microsoft® Windows® Operating System +example: CA -- -*`process.pgid`*:: +*`observer.geo.country_name`*:: + -- -Identifier of the group of processes the process belongs to. +Country name. -type: long +type: keyword -format: string +example: Canada -- -*`process.pid`*:: +*`observer.geo.location`*:: + -- -Process id. - -type: long - -example: 4242 - -format: string - --- - -*`process.ppid`*:: -+ --- -Parent process' pid. - -type: long +Longitude and latitude. -example: 4241 +type: geo_point -format: string +example: { "lon": -73.614830, "lat": 45.505918 } -- -*`process.start`*:: +*`observer.geo.name`*:: + -- -The time the process started. +User-defined description of a location, at the level of granularity they care about. +Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. +Not typically used in automated geolocation. -type: date +type: keyword -example: 2016-05-23T08:05:34.853Z +example: boston-dc -- -*`process.thread.id`*:: +*`observer.geo.region_iso_code`*:: + -- -Thread ID. - -type: long +Region ISO code. -example: 4242 +type: keyword -format: string +example: CA-QC -- -*`process.thread.name`*:: +*`observer.geo.region_name`*:: + -- -Thread name. +Region name. type: keyword -example: thread-0 +example: Quebec -- -*`process.title`*:: +*`observer.hostname`*:: + -- -Process title. -The proctitle, some times the same as process name. Can also be different: for example a browser setting its title to the web page currently opened. +Hostname of the observer. type: keyword -- -*`process.title.text`*:: +*`observer.ingress`*:: + -- -type: text +Observer.ingress holds information like interface number and name, vlan, and zone information to classify ingress traffic. Single armed monitoring such as a network sensor on a span port should only use observer.ingress to categorize traffic. + +type: object -- -*`process.uptime`*:: +*`observer.ingress.interface.alias`*:: + -- -Seconds the process has been up. +Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming. -type: long +type: keyword -example: 1325 +example: outside -- -*`process.working_directory`*:: +*`observer.ingress.interface.id`*:: + -- -The working directory of the process. +Interface ID as reported by an observer (typically SNMP interface ID). type: keyword -example: /home/alice +example: 10 -- -*`process.working_directory.text`*:: +*`observer.ingress.interface.name`*:: + -- -type: text - --- +Interface name as reported by the system. -[float] -=== registry +type: keyword -Fields related to Windows Registry operations. +example: eth0 +-- -*`registry.data.bytes`*:: +*`observer.ingress.vlan.id`*:: + -- -Original bytes written with base64 encoding. -For Windows registry operations, such as SetValueEx and RegQueryValueEx, this corresponds to the data pointed by `lp_data`. This is optional but provides better recoverability and should be populated for REG_BINARY encoded values. +VLAN ID as reported by the observer. type: keyword -example: ZQBuAC0AVQBTAAAAZQBuAAAAAAA= +example: 10 -- -*`registry.data.strings`*:: +*`observer.ingress.vlan.name`*:: + -- -Content when writing string types. -Populated as an array when writing string data to the registry. For single string registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with one string. For sequences of string with REG_MULTI_SZ, this array will be variable length. For numeric data, such as REG_DWORD and REG_QWORD, this should be populated with the decimal representation (e.g `"1"`). +Optional VLAN name as reported by the observer. type: keyword -example: ["C:\rta\red_ttp\bin\myapp.exe"] +example: outside -- -*`registry.data.type`*:: +*`observer.ingress.zone`*:: + -- -Standard registry type for encoding contents +Network zone of incoming traffic as reported by the observer to categorize the source area of ingress traffic. e.g. internal, External, DMZ, HR, Legal, etc. type: keyword -example: REG_SZ +example: DMZ -- -*`registry.hive`*:: +*`observer.ip`*:: + -- -Abbreviated name for the hive. - -type: keyword +IP addresses of the observer. -example: HKLM +type: ip -- -*`registry.key`*:: +*`observer.mac`*:: + -- -Hive-relative path of keys. +MAC addresses of the observer type: keyword -example: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\winword.exe - -- -*`registry.path`*:: +*`observer.name`*:: + -- -Full path, including hive, key and value +Custom name of the observer. +This is a name that can be given to an observer. This can be helpful for example if multiple firewalls of the same model are used in an organization. +If no custom name is needed, the field can be left empty. type: keyword -example: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\winword.exe\Debugger +example: 1_proxySG -- -*`registry.value`*:: +*`observer.os.family`*:: + -- -Name of the value written. +OS family (such as redhat, debian, freebsd, windows). type: keyword -example: Debugger +example: debian -- -[float] -=== related - -This field set is meant to facilitate pivoting around a piece of data. -Some pieces of information can be seen in many places in an ECS event. To facilitate searching for them, store an array of all seen values to their corresponding field in `related.`. -A concrete example is IP addresses, which can be under host, observer, source, destination, client, server, and network.forwarded_ip. If you append all IPs to `related.ip`, you can then search for a given IP trivially, no matter where it appeared, by querying `related.ip:192.0.2.15`. - - -*`related.hash`*:: +*`observer.os.full`*:: + -- -All the hashes seen on your event. Populating this field, then using it to search for hashes can help in situations where you're unsure what the hash algorithm is (and therefore which key name to search). +Operating system name, including the version or code name. type: keyword --- - -*`related.ip`*:: -+ --- -All of the IPs seen on your event. - -type: ip +example: Mac OS Mojave -- -*`related.user`*:: +*`observer.os.full.text`*:: + -- -All the user names seen on your event. - -type: keyword +type: text -- -[float] -=== rule - -Rule fields are used to capture the specifics of any observer or agent rules that generate alerts or other notable events. -Examples of data sources that would populate the rule fields include: network admission control platforms, network or host IDS/IPS, network firewalls, web application firewalls, url filters, endpoint detection and response (EDR) systems, etc. - - -*`rule.author`*:: +*`observer.os.kernel`*:: + -- -Name, organization, or pseudonym of the author or authors who created the rule used to generate this event. +Operating system kernel version as a raw string. type: keyword -example: ['Star-Lord'] +example: 4.4.0-112-generic -- -*`rule.category`*:: +*`observer.os.name`*:: + -- -A categorization value keyword used by the entity using the rule for detection of this event. +Operating system name, without the version. type: keyword -example: Attempted Information Leak +example: Mac OS X -- -*`rule.description`*:: +*`observer.os.name.text`*:: + -- -The description of the rule generating the event. - -type: keyword - -example: Block requests to public DNS over HTTPS / TLS protocols +type: text -- -*`rule.id`*:: +*`observer.os.platform`*:: + -- -A rule ID that is unique within the scope of an agent, observer, or other entity using the rule for detection of this event. +Operating system platform (such centos, ubuntu, windows). type: keyword -example: 101 +example: darwin -- -*`rule.license`*:: +*`observer.os.version`*:: + -- -Name of the license under which the rule used to generate this event is made available. +Operating system version as a raw string. type: keyword -example: Apache 2.0 +example: 10.14.1 -- -*`rule.name`*:: +*`observer.product`*:: + -- -The name of the rule or signature generating the event. +The product name of the observer. type: keyword -example: BLOCK_DNS_over_TLS +example: s200 -- -*`rule.reference`*:: +*`observer.serial_number`*:: + -- -Reference URL to additional information about the rule used to generate this event. -The URL can point to the vendor's documentation about the rule. If that's not available, it can also be a link to a more general page describing this type of alert. +Observer serial number. type: keyword -example: https://en.wikipedia.org/wiki/DNS_over_TLS - -- -*`rule.ruleset`*:: +*`observer.type`*:: + -- -Name of the ruleset, policy, group, or parent category in which the rule used to generate this event is a member. +The type of the observer the data is coming from. +There is no predefined list of observer types. Some examples are `forwarder`, `firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`. type: keyword -example: Standard_Protocol_Filters +example: firewall -- -*`rule.uuid`*:: +*`observer.vendor`*:: + -- -A rule ID that is unique within the scope of a set or group of agents, observers, or other entities using the rule for detection of this event. +Vendor name of the observer. type: keyword -example: 1100110011 +example: Symantec -- -*`rule.version`*:: +*`observer.version`*:: + -- -The version / revision of the rule being used for analysis. +Observer version. type: keyword -example: 1.1 - -- [float] -=== server +=== organization -A Server is defined as the responder in a network connection for events regarding sessions, connections, or bidirectional flow records. -For TCP events, the server is the receiver of the initial SYN packet(s) of the TCP connection. For other protocols, the server is generally the responder in the network transaction. Some systems actually use the term "responder" to refer the server in TCP connections. The server fields describe details about the system acting as the server in the network event. Server fields are usually populated in conjunction with client fields. Server fields are generally not populated for packet-level events. -Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately. +The organization fields enrich data with information about the company or entity the data is associated with. +These fields help you arrange or filter data stored in an index by one or multiple organizations. -*`server.address`*:: +*`organization.id`*:: + -- -Some event server addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. -Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. +Unique identifier for the organization. type: keyword -- -*`server.as.number`*:: +*`organization.name`*:: + -- -Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. - -type: long +Organization name. -example: 15169 +type: keyword -- -*`server.as.organization.name`*:: +*`organization.name.text`*:: + -- -Organization name. +type: text -type: keyword +-- -example: Google LLC +[float] +=== os --- +The OS fields contain information about the operating system. -*`server.as.organization.name.text`*:: + +*`os.family`*:: + -- -type: text +OS family (such as redhat, debian, freebsd, windows). + +type: keyword + +example: debian -- -*`server.bytes`*:: +*`os.full`*:: + -- -Bytes sent from the server to the client. +Operating system name, including the version or code name. -type: long +type: keyword -example: 184 +example: Mac OS Mojave -format: bytes +-- +*`os.full.text`*:: ++ -- +type: text -*`server.domain`*:: +-- + +*`os.kernel`*:: + -- -Server domain. +Operating system kernel version as a raw string. type: keyword +example: 4.4.0-112-generic + -- -*`server.geo.city_name`*:: +*`os.name`*:: + -- -City name. +Operating system name, without the version. type: keyword -example: Montreal +example: Mac OS X -- -*`server.geo.continent_name`*:: +*`os.name.text`*:: + -- -Name of the continent. +type: text + +-- + +*`os.platform`*:: ++ +-- +Operating system platform (such centos, ubuntu, windows). type: keyword -example: North America +example: darwin -- -*`server.geo.country_iso_code`*:: +*`os.version`*:: + -- -Country ISO code. +Operating system version as a raw string. type: keyword -example: CA +example: 10.14.1 -- -*`server.geo.country_name`*:: +[float] +=== package + +These fields contain information about an installed software package. It contains general information about a package, such as name, version or size. It also contains installation details, such as time or location. + + +*`package.architecture`*:: + -- -Country name. +Package architecture. type: keyword -example: Canada +example: x86_64 -- -*`server.geo.location`*:: +*`package.build_version`*:: + -- -Longitude and latitude. +Additional information about the build version of the installed package. +For example use the commit SHA of a non-released package. -type: geo_point +type: keyword -example: { "lon": -73.614830, "lat": 45.505918 } +example: 36f4f7e89dd61b0988b12ee000b98966867710cd -- -*`server.geo.name`*:: +*`package.checksum`*:: + -- -User-defined description of a location, at the level of granularity they care about. -Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. -Not typically used in automated geolocation. +Checksum of the installed package for verification. type: keyword -example: boston-dc +example: 68b329da9893e34099c7d8ad5cb9c940 -- -*`server.geo.region_iso_code`*:: +*`package.description`*:: + -- -Region ISO code. +Description of the package. type: keyword -example: CA-QC +example: Open source programming language to build simple/reliable/efficient software. -- -*`server.geo.region_name`*:: +*`package.install_scope`*:: + -- -Region name. +Indicating how the package was installed, e.g. user-local, global. type: keyword -example: Quebec +example: global -- -*`server.ip`*:: +*`package.installed`*:: + -- -IP address of the server. -Can be one or multiple IPv4 or IPv6 addresses. +Time when package was installed. -type: ip +type: date -- -*`server.mac`*:: +*`package.license`*:: + -- -MAC address of the server. +License under which the package was released. +Use a short name, e.g. the license identifier from SPDX License List where possible (https://spdx.org/licenses/). type: keyword +example: Apache License 2.0 + -- -*`server.nat.ip`*:: +*`package.name`*:: + -- -Translated ip of destination based NAT sessions (e.g. internet to private DMZ) -Typically used with load balancers, firewalls, or routers. +Package name -type: ip +type: keyword + +example: go -- -*`server.nat.port`*:: +*`package.path`*:: + -- -Translated port of destination based NAT sessions (e.g. internet to private DMZ) -Typically used with load balancers, firewalls, or routers. +Path where the package is installed. -type: long +type: keyword -format: string +example: /usr/local/Cellar/go/1.12.9/ -- -*`server.packets`*:: +*`package.reference`*:: + -- -Packets sent from the server to the client. +Home page or reference URL of the software in this package, if available. -type: long +type: keyword -example: 12 +example: https://golang.org -- -*`server.port`*:: +*`package.size`*:: + -- -Port of the server. +Package size in bytes. type: long +example: 62231 + format: string -- -*`server.registered_domain`*:: +*`package.type`*:: + -- -The highest registered server domain, stripped of the subdomain. -For example, the registered domain for "foo.google.com" is "google.com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". +Type of package. +This should contain the package file type, rather than the package manager name. Examples: rpm, dpkg, brew, npm, gem, nupkg, jar. type: keyword -example: google.com +example: rpm -- -*`server.top_level_domain`*:: +*`package.version`*:: + -- -The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". +Package version type: keyword -example: co.uk +example: 1.12.9 -- -*`server.user.domain`*:: +[float] +=== pe + +These fields contain Windows Portable Executable (PE) metadata. + + +*`pe.company`*:: + -- -Name of the directory the user is a member of. -For example, an LDAP or Active Directory domain name. +Internal company name of the file, provided at compile-time. type: keyword +example: Microsoft Corporation + -- -*`server.user.email`*:: +*`pe.description`*:: + -- -User email address. +Internal description of the file, provided at compile-time. type: keyword +example: Paint + -- -*`server.user.full_name`*:: +*`pe.file_version`*:: + -- -User's full name, if available. +Internal version of the file, provided at compile-time. type: keyword -example: Albert Einstein +example: 6.3.9600.17415 -- -*`server.user.full_name.text`*:: +*`pe.original_file_name`*:: + -- -type: text +Internal name of the file, provided at compile-time. + +type: keyword + +example: MSPAINT.EXE -- -*`server.user.group.domain`*:: +*`pe.product`*:: + -- -Name of the directory the group is a member of. -For example, an LDAP or Active Directory domain name. +Internal product name of the file, provided at compile-time. type: keyword +example: Microsoft® Windows® Operating System + -- -*`server.user.group.id`*:: +[float] +=== process + +These fields contain information about a process. +These fields can help you correlate metrics information with a process id/name from a log message. The `process.pid` often stays in the metric itself and is copied to the global field for correlation. + + +*`process.args`*:: + -- -Unique identifier for the group on the system/platform. +Array of process arguments, starting with the absolute path to the executable. +May be filtered to protect sensitive information. type: keyword +example: ['/usr/bin/ssh', '-l', 'user', '10.0.0.16'] + -- -*`server.user.group.name`*:: +*`process.args_count`*:: + -- -Name of the group. +Length of the process.args array. +This field can be useful for querying or performing bucket analysis on how many arguments were provided to start a process. More arguments may be an indication of suspicious activity. -type: keyword +type: long + +example: 4 -- -*`server.user.hash`*:: +*`process.code_signature.exists`*:: + -- -Unique user hash to correlate information for a user in anonymized form. -Useful if `user.id` or `user.name` contain confidential information and cannot be used. +Boolean to capture if a signature is present. -type: keyword +type: boolean + +example: true -- -*`server.user.id`*:: +*`process.code_signature.status`*:: + -- -Unique identifiers of the user. +Additional information about the certificate status. +This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. type: keyword +example: ERROR_UNTRUSTED_ROOT + -- -*`server.user.name`*:: +*`process.code_signature.subject_name`*:: + -- -Short name or login of the user. +Subject name of the code signer type: keyword -example: albert +example: Microsoft Corporation -- -*`server.user.name.text`*:: +*`process.code_signature.trusted`*:: + -- -type: text +Stores the trust status of the certificate chain. +Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. --- +type: boolean -[float] -=== service - -The service fields describe the service for or from which the data was collected. -These fields help you find and correlate logs for a specific service and version. +example: true +-- -*`service.ephemeral_id`*:: +*`process.code_signature.valid`*:: + -- -Ephemeral identifier of this service (if one exists). -This id normally changes across restarts, but `service.id` does not. +Boolean to capture if the digital signature is verified against the binary content. +Leave unpopulated if a certificate was unchecked. -type: keyword +type: boolean -example: 8a4f500f +example: true -- -*`service.id`*:: +*`process.command_line`*:: + -- -Unique identifier of the running service. If the service is comprised of many nodes, the `service.id` should be the same for all nodes. -This id should uniquely identify the service. This makes it possible to correlate logs and metrics for one specific service, no matter which particular node emitted the event. -Note that if you need to see the events from one specific host of the service, you should filter on that `host.name` or `host.id` instead. +Full command line that started the process, including the absolute path to the executable, and all arguments. +Some arguments may be filtered to protect sensitive information. type: keyword -example: d37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6 +example: /usr/bin/ssh -l user 10.0.0.16 -- -*`service.name`*:: +*`process.command_line.text`*:: + -- -Name of the service data is collected from. -The name of the service is normally user given. This allows for distributed services that run on multiple hosts to correlate the related instances based on the name. -In the case of Elasticsearch the `service.name` could contain the cluster name. For Beats the `service.name` is by default a copy of the `service.type` field if no name is specified. - -type: keyword - -example: elasticsearch-metrics +type: text -- -*`service.node.name`*:: +*`process.entity_id`*:: + -- -Name of a service node. -This allows for two nodes of the same service running on the same host to be differentiated. Therefore, `service.node.name` should typically be unique across nodes of a given service. -In the case of Elasticsearch, the `service.node.name` could contain the unique node name within the Elasticsearch cluster. In cases where the service doesn't have the concept of a node name, the host name or container name can be used to distinguish running instances that make up this service. If those do not provide uniqueness (e.g. multiple instances of the service running on the same host) - the node name can be manually set. +Unique identifier for the process. +The implementation of this is specified by the data source, but some examples of what could be used here are a process-generated UUID, Sysmon Process GUIDs, or a hash of some uniquely identifying components of a process. +Constructing a globally unique identifier is a common practice to mitigate PID reuse as well as to identify a specific process over time, across multiple monitored hosts. type: keyword -example: instance-0000000016 +example: c2c455d9f99375d -- -*`service.state`*:: +*`process.executable`*:: + -- -Current state of the service. +Absolute path to the process executable. type: keyword +example: /usr/bin/ssh + -- -*`service.type`*:: +*`process.executable.text`*:: + -- -The type of the service data is collected from. -The type can be used to group and correlate logs and metrics from one service type. -Example: If logs or metrics are collected from Elasticsearch, `service.type` would be `elasticsearch`. - -type: keyword - -example: elasticsearch +type: text -- -*`service.version`*:: +*`process.exit_code`*:: + -- -Version of the service the data was collected from. -This allows to look at a data set only for a specific version of a service. +The exit code of the process, if this is a termination event. +The field should be absent if there is no exit code for the event (e.g. process start). -type: keyword +type: long -example: 3.2.4 +example: 137 -- -[float] -=== source - -Source fields describe details about the source of a packet/event. -Source fields are usually populated in conjunction with destination fields. - - -*`source.address`*:: +*`process.hash.md5`*:: + -- -Some event source addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. -Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. +MD5 hash. type: keyword -- -*`source.as.number`*:: +*`process.hash.sha1`*:: + -- -Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. - -type: long +SHA1 hash. -example: 15169 +type: keyword -- -*`source.as.organization.name`*:: +*`process.hash.sha256`*:: + -- -Organization name. +SHA256 hash. type: keyword -example: Google LLC - -- -*`source.as.organization.name.text`*:: +*`process.hash.sha512`*:: + -- -type: text +SHA512 hash. + +type: keyword -- -*`source.bytes`*:: +*`process.name`*:: + -- -Bytes sent from the source to the destination. - -type: long +Process name. +Sometimes called program name or similar. -example: 184 +type: keyword -format: bytes +example: ssh -- -*`source.domain`*:: +*`process.name.text`*:: + -- -Source domain. - -type: keyword +type: text -- -*`source.geo.city_name`*:: +*`process.parent.args`*:: + -- -City name. +Array of process arguments. +May be filtered to protect sensitive information. type: keyword -example: Montreal +example: ['ssh', '-l', 'user', '10.0.0.16'] -- -*`source.geo.continent_name`*:: +*`process.parent.args_count`*:: + -- -Name of the continent. +Length of the process.args array. +This field can be useful for querying or performing bucket analysis on how many arguments were provided to start a process. More arguments may be an indication of suspicious activity. -type: keyword +type: long -example: North America +example: 4 -- -*`source.geo.country_iso_code`*:: +*`process.parent.code_signature.exists`*:: + -- -Country ISO code. +Boolean to capture if a signature is present. -type: keyword +type: boolean -example: CA +example: true -- -*`source.geo.country_name`*:: +*`process.parent.code_signature.status`*:: + -- -Country name. +Additional information about the certificate status. +This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked. type: keyword -example: Canada +example: ERROR_UNTRUSTED_ROOT -- -*`source.geo.location`*:: +*`process.parent.code_signature.subject_name`*:: + -- -Longitude and latitude. +Subject name of the code signer -type: geo_point +type: keyword -example: { "lon": -73.614830, "lat": 45.505918 } +example: Microsoft Corporation -- -*`source.geo.name`*:: +*`process.parent.code_signature.trusted`*:: + -- -User-defined description of a location, at the level of granularity they care about. -Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. -Not typically used in automated geolocation. +Stores the trust status of the certificate chain. +Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status. -type: keyword +type: boolean -example: boston-dc +example: true -- -*`source.geo.region_iso_code`*:: +*`process.parent.code_signature.valid`*:: + -- -Region ISO code. +Boolean to capture if the digital signature is verified against the binary content. +Leave unpopulated if a certificate was unchecked. -type: keyword +type: boolean -example: CA-QC +example: true -- -*`source.geo.region_name`*:: +*`process.parent.command_line`*:: + -- -Region name. +Full command line that started the process, including the absolute path to the executable, and all arguments. +Some arguments may be filtered to protect sensitive information. type: keyword -example: Quebec +example: /usr/bin/ssh -l user 10.0.0.16 -- -*`source.ip`*:: +*`process.parent.command_line.text`*:: + -- -IP address of the source. -Can be one or multiple IPv4 or IPv6 addresses. - -type: ip +type: text -- -*`source.mac`*:: +*`process.parent.entity_id`*:: + -- -MAC address of the source. +Unique identifier for the process. +The implementation of this is specified by the data source, but some examples of what could be used here are a process-generated UUID, Sysmon Process GUIDs, or a hash of some uniquely identifying components of a process. +Constructing a globally unique identifier is a common practice to mitigate PID reuse as well as to identify a specific process over time, across multiple monitored hosts. type: keyword +example: c2c455d9f99375d + -- -*`source.nat.ip`*:: +*`process.parent.executable`*:: + -- -Translated ip of source based NAT sessions (e.g. internal client to internet) -Typically connections traversing load balancers, firewalls, or routers. +Absolute path to the process executable. -type: ip +type: keyword + +example: /usr/bin/ssh -- -*`source.nat.port`*:: +*`process.parent.executable.text`*:: + -- -Translated port of source based NAT sessions. (e.g. internal client to internet) -Typically used with load balancers, firewalls, or routers. - -type: long - -format: string +type: text -- -*`source.packets`*:: +*`process.parent.exit_code`*:: + -- -Packets sent from the source to the destination. +The exit code of the process, if this is a termination event. +The field should be absent if there is no exit code for the event (e.g. process start). type: long -example: 12 +example: 137 -- -*`source.port`*:: +*`process.parent.hash.md5`*:: + -- -Port of the source. - -type: long +MD5 hash. -format: string +type: keyword -- -*`source.registered_domain`*:: +*`process.parent.hash.sha1`*:: + -- -The highest registered source domain, stripped of the subdomain. -For example, the registered domain for "foo.google.com" is "google.com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". +SHA1 hash. type: keyword -example: google.com - -- -*`source.top_level_domain`*:: +*`process.parent.hash.sha256`*:: + -- -The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". +SHA256 hash. type: keyword -example: co.uk - -- -*`source.user.domain`*:: +*`process.parent.hash.sha512`*:: + -- -Name of the directory the user is a member of. -For example, an LDAP or Active Directory domain name. +SHA512 hash. type: keyword -- -*`source.user.email`*:: +*`process.parent.name`*:: + -- -User email address. +Process name. +Sometimes called program name or similar. type: keyword +example: ssh + -- -*`source.user.full_name`*:: +*`process.parent.name.text`*:: + -- -User's full name, if available. - -type: keyword - -example: Albert Einstein +type: text -- -*`source.user.full_name.text`*:: +*`process.parent.pgid`*:: + -- -type: text +Identifier of the group of processes the process belongs to. + +type: long + +format: string -- -*`source.user.group.domain`*:: +*`process.parent.pid`*:: + -- -Name of the directory the group is a member of. -For example, an LDAP or Active Directory domain name. +Process id. -type: keyword +type: long + +example: 4242 + +format: string -- -*`source.user.group.id`*:: +*`process.parent.ppid`*:: + -- -Unique identifier for the group on the system/platform. +Parent process' pid. -type: keyword +type: long + +example: 4241 + +format: string -- -*`source.user.group.name`*:: +*`process.parent.start`*:: + -- -Name of the group. +The time the process started. -type: keyword +type: date + +example: 2016-05-23T08:05:34.853Z -- -*`source.user.hash`*:: +*`process.parent.thread.id`*:: + -- -Unique user hash to correlate information for a user in anonymized form. -Useful if `user.id` or `user.name` contain confidential information and cannot be used. +Thread ID. -type: keyword +type: long + +example: 4242 + +format: string -- -*`source.user.id`*:: +*`process.parent.thread.name`*:: + -- -Unique identifiers of the user. +Thread name. type: keyword +example: thread-0 + -- -*`source.user.name`*:: +*`process.parent.title`*:: + -- -Short name or login of the user. +Process title. +The proctitle, some times the same as process name. Can also be different: for example a browser setting its title to the web page currently opened. type: keyword -example: albert - -- -*`source.user.name.text`*:: +*`process.parent.title.text`*:: + -- type: text -- -[float] -=== threat - -Fields to classify events and alerts according to a threat taxonomy such as the Mitre ATT&CK framework. -These fields are for users to classify alerts from all of their sources (e.g. IDS, NGFW, etc.) within a common taxonomy. The threat.tactic.* are meant to capture the high level category of the threat (e.g. "impact"). The threat.technique.* fields are meant to capture which kind of approach is used by this detected threat, to accomplish the goal (e.g. "endpoint denial of service"). - - -*`threat.framework`*:: +*`process.parent.uptime`*:: + -- -Name of the threat framework used to further categorize and classify the tactic and technique of the reported threat. Framework classification can be provided by detecting systems, evaluated at ingest time, or retrospectively tagged to events. +Seconds the process has been up. -type: keyword +type: long -example: MITRE ATT&CK +example: 1325 -- -*`threat.tactic.id`*:: +*`process.parent.working_directory`*:: + -- -The id of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ ) +The working directory of the process. type: keyword -example: TA0040 +example: /home/alice -- -*`threat.tactic.name`*:: +*`process.parent.working_directory.text`*:: + -- -Name of the type of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ ) - -type: keyword - -example: impact +type: text -- -*`threat.tactic.reference`*:: +*`process.pe.company`*:: + -- -The reference url of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ ) +Internal company name of the file, provided at compile-time. type: keyword -example: https://attack.mitre.org/tactics/TA0040/ +example: Microsoft Corporation -- -*`threat.technique.id`*:: +*`process.pe.description`*:: + -- -The id of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ ) +Internal description of the file, provided at compile-time. type: keyword -example: T1499 +example: Paint -- -*`threat.technique.name`*:: +*`process.pe.file_version`*:: + -- -The name of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ ) +Internal version of the file, provided at compile-time. type: keyword -example: endpoint denial of service +example: 6.3.9600.17415 -- -*`threat.technique.name.text`*:: +*`process.pe.original_file_name`*:: + -- -type: text +Internal name of the file, provided at compile-time. + +type: keyword + +example: MSPAINT.EXE -- -*`threat.technique.reference`*:: +*`process.pe.product`*:: + -- -The reference url of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ ) +Internal product name of the file, provided at compile-time. type: keyword -example: https://attack.mitre.org/techniques/T1499/ +example: Microsoft® Windows® Operating System -- -[float] -=== tls - -Fields related to a TLS connection. These fields focus on the TLS protocol itself and intentionally avoids in-depth analysis of the related x.509 certificate files. - - -*`tls.cipher`*:: +*`process.pgid`*:: + -- -String indicating the cipher used during the current connection. +Identifier of the group of processes the process belongs to. -type: keyword +type: long -example: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 +format: string -- -*`tls.client.certificate`*:: +*`process.pid`*:: + -- -PEM-encoded stand-alone certificate offered by the client. This is usually mutually-exclusive of `client.certificate_chain` since this value also exists in that list. +Process id. -type: keyword +type: long -example: MII... +example: 4242 + +format: string -- -*`tls.client.certificate_chain`*:: +*`process.ppid`*:: + -- -Array of PEM-encoded certificates that make up the certificate chain offered by the client. This is usually mutually-exclusive of `client.certificate` since that value should be the first certificate in the chain. +Parent process' pid. -type: keyword +type: long -example: ['MII...', 'MII...'] +example: 4241 + +format: string -- -*`tls.client.hash.md5`*:: +*`process.start`*:: + -- -Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. +The time the process started. -type: keyword +type: date -example: 0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC +example: 2016-05-23T08:05:34.853Z -- -*`tls.client.hash.sha1`*:: +*`process.thread.id`*:: + -- -Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. +Thread ID. -type: keyword +type: long -example: 9E393D93138888D288266C2D915214D1D1CCEB2A +example: 4242 + +format: string -- -*`tls.client.hash.sha256`*:: +*`process.thread.name`*:: + -- -Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. +Thread name. type: keyword -example: 0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0 +example: thread-0 -- -*`tls.client.issuer`*:: +*`process.title`*:: + -- -Distinguished name of subject of the issuer of the x.509 certificate presented by the client. +Process title. +The proctitle, some times the same as process name. Can also be different: for example a browser setting its title to the web page currently opened. type: keyword -example: CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com - -- -*`tls.client.ja3`*:: +*`process.title.text`*:: + -- -A hash that identifies clients based on how they perform an SSL/TLS handshake. - -type: keyword - -example: d4e5b18d6b55c71272893221c96ba240 +type: text -- -*`tls.client.not_after`*:: +*`process.uptime`*:: + -- -Date/Time indicating when client certificate is no longer considered valid. +Seconds the process has been up. -type: date +type: long -example: 2021-01-01T00:00:00.000Z +example: 1325 -- -*`tls.client.not_before`*:: +*`process.working_directory`*:: + -- -Date/Time indicating when client certificate is first considered valid. +The working directory of the process. -type: date +type: keyword -example: 1970-01-01T00:00:00.000Z +example: /home/alice -- -*`tls.client.server_name`*:: +*`process.working_directory.text`*:: + -- -Also called an SNI, this tells the server which hostname to which the client is attempting to connect. When this value is available, it should get copied to `destination.domain`. +type: text -type: keyword +-- -example: www.elastic.co +[float] +=== registry --- +Fields related to Windows Registry operations. -*`tls.client.subject`*:: + +*`registry.data.bytes`*:: + -- -Distinguished name of subject of the x.509 certificate presented by the client. +Original bytes written with base64 encoding. +For Windows registry operations, such as SetValueEx and RegQueryValueEx, this corresponds to the data pointed by `lp_data`. This is optional but provides better recoverability and should be populated for REG_BINARY encoded values. type: keyword -example: CN=myclient, OU=Documentation Team, DC=mydomain, DC=com +example: ZQBuAC0AVQBTAAAAZQBuAAAAAAA= -- -*`tls.client.supported_ciphers`*:: +*`registry.data.strings`*:: + -- -Array of ciphers offered by the client during the client hello. +Content when writing string types. +Populated as an array when writing string data to the registry. For single string registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with one string. For sequences of string with REG_MULTI_SZ, this array will be variable length. For numeric data, such as REG_DWORD and REG_QWORD, this should be populated with the decimal representation (e.g `"1"`). type: keyword -example: ['TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', '...'] +example: ["C:\rta\red_ttp\bin\myapp.exe"] -- -*`tls.curve`*:: +*`registry.data.type`*:: + -- -String indicating the curve used for the given cipher, when applicable. +Standard registry type for encoding contents type: keyword -example: secp256r1 +example: REG_SZ -- -*`tls.established`*:: +*`registry.hive`*:: + -- -Boolean flag indicating if the TLS negotiation was successful and transitioned to an encrypted tunnel. +Abbreviated name for the hive. -type: boolean +type: keyword + +example: HKLM -- -*`tls.next_protocol`*:: +*`registry.key`*:: + -- -String indicating the protocol being tunneled. Per the values in the IANA registry (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids), this string should be lower case. +Hive-relative path of keys. type: keyword -example: http/1.1 +example: SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\winword.exe -- -*`tls.resumed`*:: +*`registry.path`*:: + -- -Boolean flag indicating if this TLS connection was resumed from an existing TLS negotiation. +Full path, including hive, key and value -type: boolean +type: keyword + +example: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\winword.exe\Debugger -- -*`tls.server.certificate`*:: +*`registry.value`*:: + -- -PEM-encoded stand-alone certificate offered by the server. This is usually mutually-exclusive of `server.certificate_chain` since this value also exists in that list. +Name of the value written. type: keyword -example: MII... +example: Debugger -- -*`tls.server.certificate_chain`*:: +[float] +=== related + +This field set is meant to facilitate pivoting around a piece of data. +Some pieces of information can be seen in many places in an ECS event. To facilitate searching for them, store an array of all seen values to their corresponding field in `related.`. +A concrete example is IP addresses, which can be under host, observer, source, destination, client, server, and network.forwarded_ip. If you append all IPs to `related.ip`, you can then search for a given IP trivially, no matter where it appeared, by querying `related.ip:192.0.2.15`. + + +*`related.hash`*:: + -- -Array of PEM-encoded certificates that make up the certificate chain offered by the server. This is usually mutually-exclusive of `server.certificate` since that value should be the first certificate in the chain. +All the hashes seen on your event. Populating this field, then using it to search for hashes can help in situations where you're unsure what the hash algorithm is (and therefore which key name to search). type: keyword -example: ['MII...', 'MII...'] - -- -*`tls.server.hash.md5`*:: +*`related.ip`*:: + -- -Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. - -type: keyword +All of the IPs seen on your event. -example: 0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC +type: ip -- -*`tls.server.hash.sha1`*:: +*`related.user`*:: + -- -Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. +All the user names seen on your event. type: keyword -example: 9E393D93138888D288266C2D915214D1D1CCEB2A - -- -*`tls.server.hash.sha256`*:: +[float] +=== rule + +Rule fields are used to capture the specifics of any observer or agent rules that generate alerts or other notable events. +Examples of data sources that would populate the rule fields include: network admission control platforms, network or host IDS/IPS, network firewalls, web application firewalls, url filters, endpoint detection and response (EDR) systems, etc. + + +*`rule.author`*:: + -- -Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. +Name, organization, or pseudonym of the author or authors who created the rule used to generate this event. type: keyword -example: 0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0 +example: ['Star-Lord'] -- -*`tls.server.issuer`*:: +*`rule.category`*:: + -- -Subject of the issuer of the x.509 certificate presented by the server. +A categorization value keyword used by the entity using the rule for detection of this event. type: keyword -example: CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com +example: Attempted Information Leak -- -*`tls.server.ja3s`*:: +*`rule.description`*:: + -- -A hash that identifies servers based on how they perform an SSL/TLS handshake. +The description of the rule generating the event. type: keyword -example: 394441ab65754e2207b1e1b457b3641d +example: Block requests to public DNS over HTTPS / TLS protocols -- -*`tls.server.not_after`*:: +*`rule.id`*:: + -- -Timestamp indicating when server certificate is no longer considered valid. +A rule ID that is unique within the scope of an agent, observer, or other entity using the rule for detection of this event. -type: date +type: keyword -example: 2021-01-01T00:00:00.000Z +example: 101 -- -*`tls.server.not_before`*:: +*`rule.license`*:: + -- -Timestamp indicating when server certificate is first considered valid. +Name of the license under which the rule used to generate this event is made available. -type: date +type: keyword -example: 1970-01-01T00:00:00.000Z +example: Apache 2.0 -- -*`tls.server.subject`*:: +*`rule.name`*:: + -- -Subject of the x.509 certificate presented by the server. +The name of the rule or signature generating the event. type: keyword -example: CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com +example: BLOCK_DNS_over_TLS -- -*`tls.version`*:: +*`rule.reference`*:: + -- -Numeric part of the version parsed from the original string. +Reference URL to additional information about the rule used to generate this event. +The URL can point to the vendor's documentation about the rule. If that's not available, it can also be a link to a more general page describing this type of alert. type: keyword -example: 1.2 +example: https://en.wikipedia.org/wiki/DNS_over_TLS -- -*`tls.version_protocol`*:: +*`rule.ruleset`*:: + -- -Normalized lowercase protocol name parsed from original string. +Name of the ruleset, policy, group, or parent category in which the rule used to generate this event is a member. type: keyword -example: tls +example: Standard_Protocol_Filters -- -[float] -=== tracing - -Distributed tracing makes it possible to analyze performance throughout a microservice architecture all in one view. This is accomplished by tracing all of the requests - from the initial web request in the front-end service - to queries made through multiple back-end services. - - -*`tracing.trace.id`*:: +*`rule.uuid`*:: + -- -Unique identifier of the trace. -A trace groups multiple events like transactions that belong together. For example, a user request handled by multiple inter-connected services. +A rule ID that is unique within the scope of a set or group of agents, observers, or other entities using the rule for detection of this event. type: keyword -example: 4bf92f3577b34da6a3ce929d0e0e4736 +example: 1100110011 -- -*`tracing.transaction.id`*:: +*`rule.version`*:: + -- -Unique identifier of the transaction. -A transaction is the highest level of work measured within a service, such as a request to a server. +The version / revision of the rule being used for analysis. type: keyword -example: 00f067aa0ba902b7 +example: 1.1 -- [float] -=== url +=== server -URL fields provide support for complete or partial URLs, and supports the breaking down into scheme, domain, path, and so on. +A Server is defined as the responder in a network connection for events regarding sessions, connections, or bidirectional flow records. +For TCP events, the server is the receiver of the initial SYN packet(s) of the TCP connection. For other protocols, the server is generally the responder in the network transaction. Some systems actually use the term "responder" to refer the server in TCP connections. The server fields describe details about the system acting as the server in the network event. Server fields are usually populated in conjunction with client fields. Server fields are generally not populated for packet-level events. +Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately. -*`url.domain`*:: +*`server.address`*:: + -- -Domain of the url, such as "www.elastic.co". -In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the `domain` field. +Some event server addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. +Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. type: keyword -example: www.elastic.co - -- -*`url.extension`*:: +*`server.as.number`*:: + -- -The field contains the file extension from the original request url. -The file extension is only set if it exists, as not every url has a file extension. -The leading period must not be included. For example, the value must be "png", not ".png". - -type: keyword - -example: png - --- +Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. -*`url.fragment`*:: -+ --- -Portion of the url after the `#`, such as "top". -The `#` is not part of the fragment. +type: long -type: keyword +example: 15169 -- -*`url.full`*:: +*`server.as.organization.name`*:: + -- -If full URLs are important to your use case, they should be stored in `url.full`, whether this field is reconstructed or present in the event source. +Organization name. type: keyword -example: https://www.elastic.co:443/search?q=elasticsearch#top +example: Google LLC -- -*`url.full.text`*:: +*`server.as.organization.name.text`*:: + -- type: text -- -*`url.original`*:: +*`server.bytes`*:: + -- -Unmodified original url as seen in the event source. -Note that in network monitoring, the observed URL may be a full URL, whereas in access logs, the URL is often just represented as a path. -This field is meant to represent the URL as it was observed, complete or not. - -type: keyword +Bytes sent from the server to the client. -example: https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch +type: long --- +example: 184 -*`url.original.text`*:: -+ --- -type: text +format: bytes -- -*`url.password`*:: +*`server.domain`*:: + -- -Password of the request. +Server domain. type: keyword -- -*`url.path`*:: +*`server.geo.city_name`*:: + -- -Path of the request, such as "/search". +City name. type: keyword +example: Montreal + -- -*`url.port`*:: +*`server.geo.continent_name`*:: + -- -Port of the request, such as 443. - -type: long +Name of the continent. -example: 443 +type: keyword -format: string +example: North America -- -*`url.query`*:: +*`server.geo.country_iso_code`*:: + -- -The query field describes the query string of the request, such as "q=elasticsearch". -The `?` is excluded from the query string. If a URL contains no `?`, there is no query field. If there is a `?` but no query, the query field exists with an empty string. The `exists` query can be used to differentiate between the two cases. +Country ISO code. type: keyword +example: CA + -- -*`url.registered_domain`*:: +*`server.geo.country_name`*:: + -- -The highest registered url domain, stripped of the subdomain. -For example, the registered domain for "foo.google.com" is "google.com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". +Country name. type: keyword -example: google.com +example: Canada -- -*`url.scheme`*:: +*`server.geo.location`*:: + -- -Scheme of the request, such as "https". -Note: The `:` is not part of the scheme. +Longitude and latitude. -type: keyword +type: geo_point -example: https +example: { "lon": -73.614830, "lat": 45.505918 } -- -*`url.top_level_domain`*:: +*`server.geo.name`*:: + -- -The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". -This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". +User-defined description of a location, at the level of granularity they care about. +Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. +Not typically used in automated geolocation. type: keyword -example: co.uk +example: boston-dc -- -*`url.username`*:: +*`server.geo.region_iso_code`*:: + -- -Username of the request. +Region ISO code. type: keyword +example: CA-QC + -- -[float] -=== user +*`server.geo.region_name`*:: ++ +-- +Region name. -The user fields describe information about the user that is relevant to the event. -Fields can have one entry or multiple entries. If a user has more than one id, provide an array that includes all of them. +type: keyword +example: Quebec -*`user.domain`*:: +-- + +*`server.ip`*:: ++ +-- +IP address of the server. +Can be one or multiple IPv4 or IPv6 addresses. + +type: ip + +-- + +*`server.mac`*:: ++ +-- +MAC address of the server. + +type: keyword + +-- + +*`server.nat.ip`*:: ++ +-- +Translated ip of destination based NAT sessions (e.g. internet to private DMZ) +Typically used with load balancers, firewalls, or routers. + +type: ip + +-- + +*`server.nat.port`*:: ++ +-- +Translated port of destination based NAT sessions (e.g. internet to private DMZ) +Typically used with load balancers, firewalls, or routers. + +type: long + +format: string + +-- + +*`server.packets`*:: ++ +-- +Packets sent from the server to the client. + +type: long + +example: 12 + +-- + +*`server.port`*:: ++ +-- +Port of the server. + +type: long + +format: string + +-- + +*`server.registered_domain`*:: ++ +-- +The highest registered server domain, stripped of the subdomain. +For example, the registered domain for "foo.google.com" is "google.com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". + +type: keyword + +example: google.com + +-- + +*`server.top_level_domain`*:: ++ +-- +The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". + +type: keyword + +example: co.uk + +-- + +*`server.user.domain`*:: + -- Name of the directory the user is a member of. @@ -14132,7 +14036,7 @@ type: keyword -- -*`user.email`*:: +*`server.user.email`*:: + -- User email address. @@ -14141,7 +14045,7 @@ type: keyword -- -*`user.full_name`*:: +*`server.user.full_name`*:: + -- User's full name, if available. @@ -14152,14 +14056,14 @@ example: Albert Einstein -- -*`user.full_name.text`*:: +*`server.user.full_name.text`*:: + -- type: text -- -*`user.group.domain`*:: +*`server.user.group.domain`*:: + -- Name of the directory the group is a member of. @@ -14169,7 +14073,7 @@ type: keyword -- -*`user.group.id`*:: +*`server.user.group.id`*:: + -- Unique identifier for the group on the system/platform. @@ -14178,7 +14082,7 @@ type: keyword -- -*`user.group.name`*:: +*`server.user.group.name`*:: + -- Name of the group. @@ -14187,7 +14091,7 @@ type: keyword -- -*`user.hash`*:: +*`server.user.hash`*:: + -- Unique user hash to correlate information for a user in anonymized form. @@ -14197,7 +14101,7 @@ type: keyword -- -*`user.id`*:: +*`server.user.id`*:: + -- Unique identifiers of the user. @@ -14206,7 +14110,7 @@ type: keyword -- -*`user.name`*:: +*`server.user.name`*:: + -- Short name or login of the user. @@ -14217,7 +14121,7 @@ example: albert -- -*`user.name.text`*:: +*`server.user.name.text`*:: + -- type: text @@ -14225,1126 +14129,3482 @@ type: text -- [float] -=== user_agent +=== service -The user_agent fields normally come from a browser request. -They often show up in web service logs coming from the parsed user agent string. +The service fields describe the service for or from which the data was collected. +These fields help you find and correlate logs for a specific service and version. -*`user_agent.device.name`*:: +*`service.ephemeral_id`*:: + -- -Name of the device. +Ephemeral identifier of this service (if one exists). +This id normally changes across restarts, but `service.id` does not. type: keyword -example: iPhone +example: 8a4f500f -- -*`user_agent.name`*:: +*`service.id`*:: + -- -Name of the user agent. +Unique identifier of the running service. If the service is comprised of many nodes, the `service.id` should be the same for all nodes. +This id should uniquely identify the service. This makes it possible to correlate logs and metrics for one specific service, no matter which particular node emitted the event. +Note that if you need to see the events from one specific host of the service, you should filter on that `host.name` or `host.id` instead. type: keyword -example: Safari +example: d37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6 -- -*`user_agent.original`*:: +*`service.name`*:: + -- -Unparsed user_agent string. +Name of the service data is collected from. +The name of the service is normally user given. This allows for distributed services that run on multiple hosts to correlate the related instances based on the name. +In the case of Elasticsearch the `service.name` could contain the cluster name. For Beats the `service.name` is by default a copy of the `service.type` field if no name is specified. type: keyword -example: Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1 +example: elasticsearch-metrics -- -*`user_agent.original.text`*:: +*`service.node.name`*:: + -- -type: text +Name of a service node. +This allows for two nodes of the same service running on the same host to be differentiated. Therefore, `service.node.name` should typically be unique across nodes of a given service. +In the case of Elasticsearch, the `service.node.name` could contain the unique node name within the Elasticsearch cluster. In cases where the service doesn't have the concept of a node name, the host name or container name can be used to distinguish running instances that make up this service. If those do not provide uniqueness (e.g. multiple instances of the service running on the same host) - the node name can be manually set. + +type: keyword + +example: instance-0000000016 -- -*`user_agent.os.family`*:: +*`service.state`*:: + -- -OS family (such as redhat, debian, freebsd, windows). +Current state of the service. type: keyword -example: debian +-- +*`service.type`*:: ++ -- +The type of the service data is collected from. +The type can be used to group and correlate logs and metrics from one service type. +Example: If logs or metrics are collected from Elasticsearch, `service.type` would be `elasticsearch`. -*`user_agent.os.full`*:: +type: keyword + +example: elasticsearch + +-- + +*`service.version`*:: + -- -Operating system name, including the version or code name. +Version of the service the data was collected from. +This allows to look at a data set only for a specific version of a service. type: keyword -example: Mac OS Mojave +example: 3.2.4 -- -*`user_agent.os.full.text`*:: +[float] +=== source + +Source fields describe details about the source of a packet/event. +Source fields are usually populated in conjunction with destination fields. + + +*`source.address`*:: ++ +-- +Some event source addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. +Then it should be duplicated to `.ip` or `.domain`, depending on which one it is. + +type: keyword + +-- + +*`source.as.number`*:: ++ +-- +Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. + +type: long + +example: 15169 + +-- + +*`source.as.organization.name`*:: ++ +-- +Organization name. + +type: keyword + +example: Google LLC + +-- + +*`source.as.organization.name.text`*:: + -- type: text -- -*`user_agent.os.kernel`*:: +*`source.bytes`*:: ++ +-- +Bytes sent from the source to the destination. + +type: long + +example: 184 + +format: bytes + +-- + +*`source.domain`*:: ++ +-- +Source domain. + +type: keyword + +-- + +*`source.geo.city_name`*:: ++ +-- +City name. + +type: keyword + +example: Montreal + +-- + +*`source.geo.continent_name`*:: ++ +-- +Name of the continent. + +type: keyword + +example: North America + +-- + +*`source.geo.country_iso_code`*:: ++ +-- +Country ISO code. + +type: keyword + +example: CA + +-- + +*`source.geo.country_name`*:: ++ +-- +Country name. + +type: keyword + +example: Canada + +-- + +*`source.geo.location`*:: ++ +-- +Longitude and latitude. + +type: geo_point + +example: { "lon": -73.614830, "lat": 45.505918 } + +-- + +*`source.geo.name`*:: ++ +-- +User-defined description of a location, at the level of granularity they care about. +Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. +Not typically used in automated geolocation. + +type: keyword + +example: boston-dc + +-- + +*`source.geo.region_iso_code`*:: ++ +-- +Region ISO code. + +type: keyword + +example: CA-QC + +-- + +*`source.geo.region_name`*:: ++ +-- +Region name. + +type: keyword + +example: Quebec + +-- + +*`source.ip`*:: ++ +-- +IP address of the source. +Can be one or multiple IPv4 or IPv6 addresses. + +type: ip + +-- + +*`source.mac`*:: ++ +-- +MAC address of the source. + +type: keyword + +-- + +*`source.nat.ip`*:: ++ +-- +Translated ip of source based NAT sessions (e.g. internal client to internet) +Typically connections traversing load balancers, firewalls, or routers. + +type: ip + +-- + +*`source.nat.port`*:: ++ +-- +Translated port of source based NAT sessions. (e.g. internal client to internet) +Typically used with load balancers, firewalls, or routers. + +type: long + +format: string + +-- + +*`source.packets`*:: ++ +-- +Packets sent from the source to the destination. + +type: long + +example: 12 + +-- + +*`source.port`*:: ++ +-- +Port of the source. + +type: long + +format: string + +-- + +*`source.registered_domain`*:: ++ +-- +The highest registered source domain, stripped of the subdomain. +For example, the registered domain for "foo.google.com" is "google.com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". + +type: keyword + +example: google.com + +-- + +*`source.top_level_domain`*:: ++ +-- +The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". + +type: keyword + +example: co.uk + +-- + +*`source.user.domain`*:: ++ +-- +Name of the directory the user is a member of. +For example, an LDAP or Active Directory domain name. + +type: keyword + +-- + +*`source.user.email`*:: ++ +-- +User email address. + +type: keyword + +-- + +*`source.user.full_name`*:: ++ +-- +User's full name, if available. + +type: keyword + +example: Albert Einstein + +-- + +*`source.user.full_name.text`*:: ++ +-- +type: text + +-- + +*`source.user.group.domain`*:: ++ +-- +Name of the directory the group is a member of. +For example, an LDAP or Active Directory domain name. + +type: keyword + +-- + +*`source.user.group.id`*:: ++ +-- +Unique identifier for the group on the system/platform. + +type: keyword + +-- + +*`source.user.group.name`*:: ++ +-- +Name of the group. + +type: keyword + +-- + +*`source.user.hash`*:: ++ +-- +Unique user hash to correlate information for a user in anonymized form. +Useful if `user.id` or `user.name` contain confidential information and cannot be used. + +type: keyword + +-- + +*`source.user.id`*:: ++ +-- +Unique identifiers of the user. + +type: keyword + +-- + +*`source.user.name`*:: ++ +-- +Short name or login of the user. + +type: keyword + +example: albert + +-- + +*`source.user.name.text`*:: ++ +-- +type: text + +-- + +[float] +=== threat + +Fields to classify events and alerts according to a threat taxonomy such as the Mitre ATT&CK framework. +These fields are for users to classify alerts from all of their sources (e.g. IDS, NGFW, etc.) within a common taxonomy. The threat.tactic.* are meant to capture the high level category of the threat (e.g. "impact"). The threat.technique.* fields are meant to capture which kind of approach is used by this detected threat, to accomplish the goal (e.g. "endpoint denial of service"). + + +*`threat.framework`*:: ++ +-- +Name of the threat framework used to further categorize and classify the tactic and technique of the reported threat. Framework classification can be provided by detecting systems, evaluated at ingest time, or retrospectively tagged to events. + +type: keyword + +example: MITRE ATT&CK + +-- + +*`threat.tactic.id`*:: ++ +-- +The id of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ ) + +type: keyword + +example: TA0040 + +-- + +*`threat.tactic.name`*:: ++ +-- +Name of the type of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ ) + +type: keyword + +example: impact + +-- + +*`threat.tactic.reference`*:: ++ +-- +The reference url of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ ) + +type: keyword + +example: https://attack.mitre.org/tactics/TA0040/ + +-- + +*`threat.technique.id`*:: ++ +-- +The id of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ ) + +type: keyword + +example: T1499 + +-- + +*`threat.technique.name`*:: ++ +-- +The name of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ ) + +type: keyword + +example: endpoint denial of service + +-- + +*`threat.technique.name.text`*:: ++ +-- +type: text + +-- + +*`threat.technique.reference`*:: ++ +-- +The reference url of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ ) + +type: keyword + +example: https://attack.mitre.org/techniques/T1499/ + +-- + +[float] +=== tls + +Fields related to a TLS connection. These fields focus on the TLS protocol itself and intentionally avoids in-depth analysis of the related x.509 certificate files. + + +*`tls.cipher`*:: ++ +-- +String indicating the cipher used during the current connection. + +type: keyword + +example: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + +-- + +*`tls.client.certificate`*:: ++ +-- +PEM-encoded stand-alone certificate offered by the client. This is usually mutually-exclusive of `client.certificate_chain` since this value also exists in that list. + +type: keyword + +example: MII... + +-- + +*`tls.client.certificate_chain`*:: ++ +-- +Array of PEM-encoded certificates that make up the certificate chain offered by the client. This is usually mutually-exclusive of `client.certificate` since that value should be the first certificate in the chain. + +type: keyword + +example: ['MII...', 'MII...'] + +-- + +*`tls.client.hash.md5`*:: ++ +-- +Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. + +type: keyword + +example: 0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC + +-- + +*`tls.client.hash.sha1`*:: ++ +-- +Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. + +type: keyword + +example: 9E393D93138888D288266C2D915214D1D1CCEB2A + +-- + +*`tls.client.hash.sha256`*:: ++ +-- +Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash. + +type: keyword + +example: 0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0 + +-- + +*`tls.client.issuer`*:: ++ +-- +Distinguished name of subject of the issuer of the x.509 certificate presented by the client. + +type: keyword + +example: CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com + +-- + +*`tls.client.ja3`*:: ++ +-- +A hash that identifies clients based on how they perform an SSL/TLS handshake. + +type: keyword + +example: d4e5b18d6b55c71272893221c96ba240 + +-- + +*`tls.client.not_after`*:: ++ +-- +Date/Time indicating when client certificate is no longer considered valid. + +type: date + +example: 2021-01-01T00:00:00.000Z + +-- + +*`tls.client.not_before`*:: ++ +-- +Date/Time indicating when client certificate is first considered valid. + +type: date + +example: 1970-01-01T00:00:00.000Z + +-- + +*`tls.client.server_name`*:: ++ +-- +Also called an SNI, this tells the server which hostname to which the client is attempting to connect. When this value is available, it should get copied to `destination.domain`. + +type: keyword + +example: www.elastic.co + +-- + +*`tls.client.subject`*:: ++ +-- +Distinguished name of subject of the x.509 certificate presented by the client. + +type: keyword + +example: CN=myclient, OU=Documentation Team, DC=mydomain, DC=com + +-- + +*`tls.client.supported_ciphers`*:: ++ +-- +Array of ciphers offered by the client during the client hello. + +type: keyword + +example: ['TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', '...'] + +-- + +*`tls.curve`*:: ++ +-- +String indicating the curve used for the given cipher, when applicable. + +type: keyword + +example: secp256r1 + +-- + +*`tls.established`*:: ++ +-- +Boolean flag indicating if the TLS negotiation was successful and transitioned to an encrypted tunnel. + +type: boolean + +-- + +*`tls.next_protocol`*:: ++ +-- +String indicating the protocol being tunneled. Per the values in the IANA registry (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids), this string should be lower case. + +type: keyword + +example: http/1.1 + +-- + +*`tls.resumed`*:: ++ +-- +Boolean flag indicating if this TLS connection was resumed from an existing TLS negotiation. + +type: boolean + +-- + +*`tls.server.certificate`*:: ++ +-- +PEM-encoded stand-alone certificate offered by the server. This is usually mutually-exclusive of `server.certificate_chain` since this value also exists in that list. + +type: keyword + +example: MII... + +-- + +*`tls.server.certificate_chain`*:: ++ +-- +Array of PEM-encoded certificates that make up the certificate chain offered by the server. This is usually mutually-exclusive of `server.certificate` since that value should be the first certificate in the chain. + +type: keyword + +example: ['MII...', 'MII...'] + +-- + +*`tls.server.hash.md5`*:: ++ +-- +Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. + +type: keyword + +example: 0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC + +-- + +*`tls.server.hash.sha1`*:: ++ +-- +Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. + +type: keyword + +example: 9E393D93138888D288266C2D915214D1D1CCEB2A + +-- + +*`tls.server.hash.sha256`*:: ++ +-- +Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash. + +type: keyword + +example: 0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0 + +-- + +*`tls.server.issuer`*:: ++ +-- +Subject of the issuer of the x.509 certificate presented by the server. + +type: keyword + +example: CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com + +-- + +*`tls.server.ja3s`*:: ++ +-- +A hash that identifies servers based on how they perform an SSL/TLS handshake. + +type: keyword + +example: 394441ab65754e2207b1e1b457b3641d + +-- + +*`tls.server.not_after`*:: ++ +-- +Timestamp indicating when server certificate is no longer considered valid. + +type: date + +example: 2021-01-01T00:00:00.000Z + +-- + +*`tls.server.not_before`*:: ++ +-- +Timestamp indicating when server certificate is first considered valid. + +type: date + +example: 1970-01-01T00:00:00.000Z + +-- + +*`tls.server.subject`*:: ++ +-- +Subject of the x.509 certificate presented by the server. + +type: keyword + +example: CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com + +-- + +*`tls.version`*:: ++ +-- +Numeric part of the version parsed from the original string. + +type: keyword + +example: 1.2 + +-- + +*`tls.version_protocol`*:: ++ +-- +Normalized lowercase protocol name parsed from original string. + +type: keyword + +example: tls + +-- + +[float] +=== tracing + +Distributed tracing makes it possible to analyze performance throughout a microservice architecture all in one view. This is accomplished by tracing all of the requests - from the initial web request in the front-end service - to queries made through multiple back-end services. + + +*`tracing.trace.id`*:: ++ +-- +Unique identifier of the trace. +A trace groups multiple events like transactions that belong together. For example, a user request handled by multiple inter-connected services. + +type: keyword + +example: 4bf92f3577b34da6a3ce929d0e0e4736 + +-- + +*`tracing.transaction.id`*:: ++ +-- +Unique identifier of the transaction. +A transaction is the highest level of work measured within a service, such as a request to a server. + +type: keyword + +example: 00f067aa0ba902b7 + +-- + +[float] +=== url + +URL fields provide support for complete or partial URLs, and supports the breaking down into scheme, domain, path, and so on. + + +*`url.domain`*:: ++ +-- +Domain of the url, such as "www.elastic.co". +In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the `domain` field. + +type: keyword + +example: www.elastic.co + +-- + +*`url.extension`*:: ++ +-- +The field contains the file extension from the original request url. +The file extension is only set if it exists, as not every url has a file extension. +The leading period must not be included. For example, the value must be "png", not ".png". + +type: keyword + +example: png + +-- + +*`url.fragment`*:: ++ +-- +Portion of the url after the `#`, such as "top". +The `#` is not part of the fragment. + +type: keyword + +-- + +*`url.full`*:: ++ +-- +If full URLs are important to your use case, they should be stored in `url.full`, whether this field is reconstructed or present in the event source. + +type: keyword + +example: https://www.elastic.co:443/search?q=elasticsearch#top + +-- + +*`url.full.text`*:: ++ +-- +type: text + +-- + +*`url.original`*:: ++ +-- +Unmodified original url as seen in the event source. +Note that in network monitoring, the observed URL may be a full URL, whereas in access logs, the URL is often just represented as a path. +This field is meant to represent the URL as it was observed, complete or not. + +type: keyword + +example: https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch + +-- + +*`url.original.text`*:: ++ +-- +type: text + +-- + +*`url.password`*:: ++ +-- +Password of the request. + +type: keyword + +-- + +*`url.path`*:: ++ +-- +Path of the request, such as "/search". + +type: keyword + +-- + +*`url.port`*:: ++ +-- +Port of the request, such as 443. + +type: long + +example: 443 + +format: string + +-- + +*`url.query`*:: ++ +-- +The query field describes the query string of the request, such as "q=elasticsearch". +The `?` is excluded from the query string. If a URL contains no `?`, there is no query field. If there is a `?` but no query, the query field exists with an empty string. The `exists` query can be used to differentiate between the two cases. + +type: keyword + +-- + +*`url.registered_domain`*:: ++ +-- +The highest registered url domain, stripped of the subdomain. +For example, the registered domain for "foo.google.com" is "google.com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk". + +type: keyword + +example: google.com + +-- + +*`url.scheme`*:: ++ +-- +Scheme of the request, such as "https". +Note: The `:` is not part of the scheme. + +type: keyword + +example: https + +-- + +*`url.top_level_domain`*:: ++ +-- +The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". +This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk". + +type: keyword + +example: co.uk + +-- + +*`url.username`*:: ++ +-- +Username of the request. + +type: keyword + +-- + +[float] +=== user + +The user fields describe information about the user that is relevant to the event. +Fields can have one entry or multiple entries. If a user has more than one id, provide an array that includes all of them. + + +*`user.domain`*:: ++ +-- +Name of the directory the user is a member of. +For example, an LDAP or Active Directory domain name. + +type: keyword + +-- + +*`user.email`*:: ++ +-- +User email address. + +type: keyword + +-- + +*`user.full_name`*:: ++ +-- +User's full name, if available. + +type: keyword + +example: Albert Einstein + +-- + +*`user.full_name.text`*:: ++ +-- +type: text + +-- + +*`user.group.domain`*:: ++ +-- +Name of the directory the group is a member of. +For example, an LDAP or Active Directory domain name. + +type: keyword + +-- + +*`user.group.id`*:: ++ +-- +Unique identifier for the group on the system/platform. + +type: keyword + +-- + +*`user.group.name`*:: ++ +-- +Name of the group. + +type: keyword + +-- + +*`user.hash`*:: ++ +-- +Unique user hash to correlate information for a user in anonymized form. +Useful if `user.id` or `user.name` contain confidential information and cannot be used. + +type: keyword + +-- + +*`user.id`*:: ++ +-- +Unique identifiers of the user. + +type: keyword + +-- + +*`user.name`*:: ++ +-- +Short name or login of the user. + +type: keyword + +example: albert + +-- + +*`user.name.text`*:: ++ +-- +type: text + +-- + +[float] +=== user_agent + +The user_agent fields normally come from a browser request. +They often show up in web service logs coming from the parsed user agent string. + + +*`user_agent.device.name`*:: ++ +-- +Name of the device. + +type: keyword + +example: iPhone + +-- + +*`user_agent.name`*:: ++ +-- +Name of the user agent. + +type: keyword + +example: Safari + +-- + +*`user_agent.original`*:: ++ +-- +Unparsed user_agent string. + +type: keyword + +example: Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1 + +-- + +*`user_agent.original.text`*:: ++ +-- +type: text + +-- + +*`user_agent.os.family`*:: ++ +-- +OS family (such as redhat, debian, freebsd, windows). + +type: keyword + +example: debian + +-- + +*`user_agent.os.full`*:: ++ +-- +Operating system name, including the version or code name. + +type: keyword + +example: Mac OS Mojave + +-- + +*`user_agent.os.full.text`*:: ++ +-- +type: text + +-- + +*`user_agent.os.kernel`*:: ++ +-- +Operating system kernel version as a raw string. + +type: keyword + +example: 4.4.0-112-generic + +-- + +*`user_agent.os.name`*:: ++ +-- +Operating system name, without the version. + +type: keyword + +example: Mac OS X + +-- + +*`user_agent.os.name.text`*:: ++ +-- +type: text + +-- + +*`user_agent.os.platform`*:: ++ +-- +Operating system platform (such centos, ubuntu, windows). + +type: keyword + +example: darwin + +-- + +*`user_agent.os.version`*:: ++ +-- +Operating system version as a raw string. + +type: keyword + +example: 10.14.1 + +-- + +*`user_agent.version`*:: ++ +-- +Version of the user agent. + +type: keyword + +example: 12.0 + +-- + +[float] +=== vlan + +The VLAN fields are used to identify 802.1q tag(s) of a packet, as well as ingress and egress VLAN associations of an observer in relation to a specific packet or connection. +Network.vlan fields are used to record a single VLAN tag, or the outer tag in the case of q-in-q encapsulations, for a packet or connection as observed, typically provided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic. +Network.inner VLAN fields are used to report inner q-in-q 802.1q tags (multiple 802.1q encapsulations) as observed, typically provided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic. Network.inner VLAN fields should only be used in addition to network.vlan fields to indicate q-in-q tagging. +Observer.ingress and observer.egress VLAN values are used to record observer specific information when observer events contain discrete ingress and egress VLAN information, typically provided by firewalls, routers, or load balancers. + + +*`vlan.id`*:: ++ +-- +VLAN ID as reported by the observer. + +type: keyword + +example: 10 + +-- + +*`vlan.name`*:: ++ +-- +Optional VLAN name as reported by the observer. + +type: keyword + +example: outside + +-- + +[float] +=== vulnerability + +The vulnerability fields describe information about a vulnerability that is relevant to an event. + + +*`vulnerability.category`*:: ++ +-- +The type of system or architecture that the vulnerability affects. These may be platform-specific (for example, Debian or SUSE) or general (for example, Database or Firewall). For example (https://qualysguard.qualys.com/qwebhelp/fo_portal/knowledgebase/vulnerability_categories.htm[Qualys vulnerability categories]) +This field must be an array. + +type: keyword + +example: ["Firewall"] + +-- + +*`vulnerability.classification`*:: ++ +-- +The classification of the vulnerability scoring system. For example (https://www.first.org/cvss/) + +type: keyword + +example: CVSS + +-- + +*`vulnerability.description`*:: ++ +-- +The description of the vulnerability that provides additional context of the vulnerability. For example (https://cve.mitre.org/about/faqs.html#cve_entry_descriptions_created[Common Vulnerabilities and Exposure CVE description]) + +type: keyword + +example: In macOS before 2.12.6, there is a vulnerability in the RPC... + +-- + +*`vulnerability.description.text`*:: ++ +-- +type: text + +-- + +*`vulnerability.enumeration`*:: ++ +-- +The type of identifier used for this vulnerability. For example (https://cve.mitre.org/about/) + +type: keyword + +example: CVE + +-- + +*`vulnerability.id`*:: ++ +-- +The identification (ID) is the number portion of a vulnerability entry. It includes a unique identification number for the vulnerability. For example (https://cve.mitre.org/about/faqs.html#what_is_cve_id)[Common Vulnerabilities and Exposure CVE ID] + +type: keyword + +example: CVE-2019-00001 + +-- + +*`vulnerability.reference`*:: ++ +-- +A resource that provides additional information, context, and mitigations for the identified vulnerability. + +type: keyword + +example: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6111 + +-- + +*`vulnerability.report_id`*:: ++ +-- +The report or scan identification number. + +type: keyword + +example: 20191018.0001 + +-- + +*`vulnerability.scanner.vendor`*:: ++ +-- +The name of the vulnerability scanner vendor. + +type: keyword + +example: Tenable + +-- + +*`vulnerability.score.base`*:: ++ +-- +Scores can range from 0.0 to 10.0, with 10.0 being the most severe. +Base scores cover an assessment for exploitability metrics (attack vector, complexity, privileges, and user interaction), impact metrics (confidentiality, integrity, and availability), and scope. For example (https://www.first.org/cvss/specification-document) + +type: float + +example: 5.5 + +-- + +*`vulnerability.score.environmental`*:: ++ +-- +Scores can range from 0.0 to 10.0, with 10.0 being the most severe. +Environmental scores cover an assessment for any modified Base metrics, confidentiality, integrity, and availability requirements. For example (https://www.first.org/cvss/specification-document) + +type: float + +example: 5.5 + +-- + +*`vulnerability.score.temporal`*:: ++ +-- +Scores can range from 0.0 to 10.0, with 10.0 being the most severe. +Temporal scores cover an assessment for code maturity, remediation level, and confidence. For example (https://www.first.org/cvss/specification-document) + +type: float + +-- + +*`vulnerability.score.version`*:: ++ +-- +The National Vulnerability Database (NVD) provides qualitative severity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score ranges in addition to the severity ratings for CVSS v3.0 as they are defined in the CVSS v3.0 specification. +CVSS is owned and managed by FIRST.Org, Inc. (FIRST), a US-based non-profit organization, whose mission is to help computer security incident response teams across the world. For example (https://nvd.nist.gov/vuln-metrics/cvss) + +type: keyword + +example: 2.0 + +-- + +*`vulnerability.severity`*:: ++ +-- +The severity of the vulnerability can help with metrics and internal prioritization regarding remediation. For example (https://nvd.nist.gov/vuln-metrics/cvss) + +type: keyword + +example: Critical + +-- + +[[exported-fields-elasticsearch]] +== Elasticsearch fields + +Elasticsearch module + + + +[float] +=== elasticsearch + + + + +*`elasticsearch.cluster.name`*:: ++ +-- +Elasticsearch cluster name. + + +type: keyword + +-- + +*`elasticsearch.cluster.id`*:: ++ +-- +Elasticsearch cluster id. + + +type: keyword + +-- + +*`elasticsearch.cluster.state.id`*:: ++ +-- +Elasticsearch state id. + + +type: keyword + +-- + +*`elasticsearch.node.id`*:: ++ +-- +Node ID + + +type: keyword + +-- + +*`elasticsearch.node.name`*:: ++ +-- +Node name. + + +type: keyword + +-- + +[float] +=== ccr + +Cross-cluster replication stats + + + + +*`elasticsearch.ccr.leader.index`*:: ++ +-- +Name of leader index + + +type: keyword + +-- + +*`elasticsearch.ccr.leader.max_seq_no`*:: ++ +-- +Maximum sequence number of operation on the leader shard + + +type: long + +-- + + +*`elasticsearch.ccr.follower.index`*:: ++ +-- +Name of follower index + + +type: keyword + +-- + +*`elasticsearch.ccr.follower.shard.number`*:: ++ +-- +Number of the shard within the index + + +type: long + +-- + +*`elasticsearch.ccr.follower.operations_written`*:: ++ +-- +Number of operations indexed (replicated) into the follower shard from the leader shard + + +type: long + +-- + +*`elasticsearch.ccr.follower.time_since_last_read.ms`*:: ++ +-- +Time, in ms, since the follower last fetched from the leader + + +type: long + +-- + +*`elasticsearch.ccr.follower.global_checkpoint`*:: ++ +-- +Global checkpoint value on follower shard + + +type: long + +-- + +[float] +=== cluster.stats + +Cluster stats + + + +*`elasticsearch.cluster.stats.status`*:: ++ +-- +Cluster status (green, yellow, red). + + +type: keyword + +-- + +[float] +=== nodes + +Nodes statistics. + + + +*`elasticsearch.cluster.stats.nodes.count`*:: ++ +-- +Total number of nodes in cluster. + + +type: long + +-- + +*`elasticsearch.cluster.stats.nodes.master`*:: ++ +-- +Number of master-eligible nodes in cluster. + + +type: long + +-- + +*`elasticsearch.cluster.stats.nodes.data`*:: ++ +-- +Number of data nodes in cluster. + + +type: long + +-- + +[float] +=== indices + +Indices statistics. + + + +*`elasticsearch.cluster.stats.indices.count`*:: ++ +-- +Total number of indices in cluster. + + +type: long + +-- + +[float] +=== shards + +Shard statistics. + + + +*`elasticsearch.cluster.stats.indices.shards.count`*:: ++ +-- +Total number of shards in cluster. + + +type: long + +-- + +*`elasticsearch.cluster.stats.indices.shards.primaries`*:: ++ +-- +Total number of primary shards in cluster. + + +type: long + +-- + +*`elasticsearch.cluster.stats.indices.fielddata.memory.bytes`*:: ++ +-- +Memory used for fielddata. + + +type: long + +-- + +[float] +=== enrich + +Enrich stats + + + +*`elasticsearch.enrich.queue.size`*:: ++ +-- +Number of search requests in the queue. + + +type: long + +-- + + +*`elasticsearch.enrich.remote_requests.current`*:: ++ +-- +Current number of outstanding remote requests. + + +type: long + +-- + +*`elasticsearch.enrich.remote_requests.total`*:: ++ +-- +Number of outstanding remote requests executed since node startup. + + +type: long + +-- + +*`elasticsearch.enrich.executed_searches.total`*:: ++ +-- +Number of search requests that enrich processors have executed since node startup. + + +type: long + +-- + +[float] +=== index + +index + + + +*`elasticsearch.index.name`*:: ++ +-- +Index name. + + +type: keyword + +-- + + +*`elasticsearch.index.total.docs.count`*:: ++ +-- +Total number of documents in the index. + + +type: long + +-- + +*`elasticsearch.index.total.docs.deleted`*:: ++ +-- +Total number of deleted documents in the index. + + +type: long + +-- + +*`elasticsearch.index.total.store.size.bytes`*:: ++ +-- +Total size of the index in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.index.total.segments.count`*:: ++ +-- +Total number of index segments. + + +type: long + +-- + +*`elasticsearch.index.total.segments.memory.bytes`*:: ++ +-- +Total number of memory used by the segments in bytes. + + +type: long + +format: bytes + +-- + +[float] +=== index.recovery + +index + + + +*`elasticsearch.index.recovery.id`*:: ++ +-- +Shard recovery id. + + +type: long + +-- + +*`elasticsearch.index.recovery.type`*:: ++ +-- +Shard recovery type. + + +type: keyword + +-- + +*`elasticsearch.index.recovery.primary`*:: ++ +-- +True if primary shard. + + +type: boolean + +-- + +*`elasticsearch.index.recovery.stage`*:: ++ +-- +Recovery stage. + + +type: keyword + +-- + +*`elasticsearch.index.recovery.target.id`*:: ++ +-- +Target node id. + + +type: keyword + +-- + +*`elasticsearch.index.recovery.target.host`*:: ++ +-- +Target node host address (could be IP address or hostname). + + +type: keyword + +-- + +*`elasticsearch.index.recovery.target.name`*:: ++ +-- +Target node name. + + +type: keyword + +-- + +*`elasticsearch.index.recovery.source.id`*:: ++ +-- +Source node id. + + +type: keyword + +-- + +*`elasticsearch.index.recovery.source.host`*:: ++ +-- +Source node host address (could be IP address or hostname). + + +type: keyword + +-- + +*`elasticsearch.index.recovery.source.name`*:: ++ +-- +Source node name. + + +type: keyword + +-- + +[float] +=== index.summary + +index + + + + +*`elasticsearch.index.summary.primaries.docs.count`*:: ++ +-- +Total number of documents in the index. + + +type: long + +-- + +*`elasticsearch.index.summary.primaries.docs.deleted`*:: ++ +-- +Total number of deleted documents in the index. + + +type: long + +-- + +*`elasticsearch.index.summary.primaries.store.size.bytes`*:: ++ +-- +Total size of the index in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.index.summary.primaries.segments.count`*:: ++ +-- +Total number of index segments. + + +type: long + +-- + +*`elasticsearch.index.summary.primaries.segments.memory.bytes`*:: ++ +-- +Total number of memory used by the segments in bytes. + + +type: long + +format: bytes + +-- + + +*`elasticsearch.index.summary.total.docs.count`*:: ++ +-- +Total number of documents in the index. + + +type: long + +-- + +*`elasticsearch.index.summary.total.docs.deleted`*:: ++ +-- +Total number of deleted documents in the index. + + +type: long + +-- + +*`elasticsearch.index.summary.total.store.size.bytes`*:: ++ +-- +Total size of the index in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.index.summary.total.segments.count`*:: ++ +-- +Total number of index segments. + + +type: long + +-- + +*`elasticsearch.index.summary.total.segments.memory.bytes`*:: ++ +-- +Total number of memory used by the segments in bytes. + + +type: long + +format: bytes + +-- + +[float] +=== ml.job + +ml + + + +*`elasticsearch.ml.job.id`*:: ++ +-- +Unique ml job id. + + +type: keyword + +-- + +*`elasticsearch.ml.job.state`*:: ++ +-- +Job state. + + +type: keyword + +-- + +*`elasticsearch.ml.job.data_counts.processed_record_count`*:: ++ +-- +Processed data events. + + +type: long + +-- + +*`elasticsearch.ml.job.data_counts.invalid_date_count`*:: ++ +-- +The number of records with either a missing date field or a date that could not be parsed. + + +type: long + +-- + +[float] +=== node + +node + + + +*`elasticsearch.node.version`*:: ++ +-- +Node version. + + +type: keyword + +-- + +[float] +=== jvm + +JVM Info. + + + +*`elasticsearch.node.jvm.version`*:: ++ +-- +JVM version. + + +type: keyword + +-- + +*`elasticsearch.node.jvm.memory.heap.init.bytes`*:: ++ +-- +Heap init used by the JVM in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.node.jvm.memory.heap.max.bytes`*:: ++ +-- +Heap max used by the JVM in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.node.jvm.memory.nonheap.init.bytes`*:: ++ +-- +Non-Heap init used by the JVM in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.node.jvm.memory.nonheap.max.bytes`*:: ++ +-- +Non-Heap max used by the JVM in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.node.process.mlockall`*:: ++ +-- +If process locked in memory. + + +type: boolean + +-- + +[float] +=== node.stats + +node_stats + + + +[float] +=== indices + +Node indices stats + + + +*`elasticsearch.node.stats.indices.docs.count`*:: ++ +-- +Total number of existing documents. + + +type: long + +-- + +*`elasticsearch.node.stats.indices.docs.deleted`*:: ++ +-- +Total number of deleted documents. + + +type: long + +-- + +*`elasticsearch.node.stats.indices.segments.count`*:: ++ +-- +Total number of segments. + + +type: long + +-- + +*`elasticsearch.node.stats.indices.segments.memory.bytes`*:: ++ +-- +Total size of segments in bytes. + + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.indices.store.size.bytes`*:: ++ +-- +Total size of the store in bytes. + + +type: long + +-- + +[float] +=== jvm.mem.pools + +JVM memory pool stats + + + +[float] +=== old + +Old memory pool stats. + + + +*`elasticsearch.node.stats.jvm.mem.pools.old.max.bytes`*:: ++ +-- +Max bytes. + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes`*:: ++ +-- +Peak bytes. + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes`*:: ++ +-- +Peak max bytes. + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.jvm.mem.pools.old.used.bytes`*:: ++ +-- +Used bytes. + +type: long + +format: bytes + +-- + +[float] +=== young + +Young memory pool stats. + + + +*`elasticsearch.node.stats.jvm.mem.pools.young.max.bytes`*:: ++ +-- +Max bytes. + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes`*:: ++ +-- +Peak bytes. + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes`*:: ++ +-- +Peak max bytes. + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.jvm.mem.pools.young.used.bytes`*:: ++ +-- +Used bytes. + +type: long + +format: bytes + +-- + +[float] +=== survivor + +Survivor memory pool stats. + + + +*`elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes`*:: ++ +-- +Max bytes. + +type: long + +format: bytes + +-- + +*`elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes`*:: + -- -Operating system kernel version as a raw string. +Peak bytes. -type: keyword +type: long -example: 4.4.0-112-generic +format: bytes -- -*`user_agent.os.name`*:: +*`elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes`*:: + -- -Operating system name, without the version. +Peak max bytes. -type: keyword +type: long -example: Mac OS X +format: bytes -- -*`user_agent.os.name.text`*:: +*`elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes`*:: + -- -type: text +Used bytes. --- +type: long + +format: bytes -*`user_agent.os.platform`*:: -+ -- -Operating system platform (such centos, ubuntu, windows). -type: keyword +[float] +=== jvm.gc.collectors -example: darwin +GC collector stats. --- -*`user_agent.os.version`*:: + +[float] +=== old.collection + +Old collection gc. + + + +*`elasticsearch.node.stats.jvm.gc.collectors.old.collection.count`*:: + -- -Operating system version as a raw string. -type: keyword -example: 10.14.1 +type: long -- -*`user_agent.version`*:: +*`elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms`*:: + -- -Version of the user agent. -type: keyword -example: 12.0 +type: long -- [float] -=== vlan +=== young.collection -The VLAN fields are used to identify 802.1q tag(s) of a packet, as well as ingress and egress VLAN associations of an observer in relation to a specific packet or connection. -Network.vlan fields are used to record a single VLAN tag, or the outer tag in the case of q-in-q encapsulations, for a packet or connection as observed, typically provided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic. -Network.inner VLAN fields are used to report inner q-in-q 802.1q tags (multiple 802.1q encapsulations) as observed, typically provided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic. Network.inner VLAN fields should only be used in addition to network.vlan fields to indicate q-in-q tagging. -Observer.ingress and observer.egress VLAN values are used to record observer specific information when observer events contain discrete ingress and egress VLAN information, typically provided by firewalls, routers, or load balancers. +Young collection gc. -*`vlan.id`*:: + +*`elasticsearch.node.stats.jvm.gc.collectors.young.collection.count`*:: + -- -VLAN ID as reported by the observer. -type: keyword -example: 10 +type: long -- -*`vlan.name`*:: +*`elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms`*:: + -- -Optional VLAN name as reported by the observer. -type: keyword -example: outside +type: long -- [float] -=== vulnerability +=== fs.summary -The vulnerability fields describe information about a vulnerability that is relevant to an event. +File system summary -*`vulnerability.category`*:: + +*`elasticsearch.node.stats.fs.summary.total.bytes`*:: + -- -The type of system or architecture that the vulnerability affects. These may be platform-specific (for example, Debian or SUSE) or general (for example, Database or Firewall). For example (https://qualysguard.qualys.com/qwebhelp/fo_portal/knowledgebase/vulnerability_categories.htm[Qualys vulnerability categories]) -This field must be an array. -type: keyword -example: ["Firewall"] +type: long + +format: bytes -- -*`vulnerability.classification`*:: +*`elasticsearch.node.stats.fs.summary.free.bytes`*:: + -- -The classification of the vulnerability scoring system. For example (https://www.first.org/cvss/) -type: keyword -example: CVSS +type: long + +format: bytes -- -*`vulnerability.description`*:: +*`elasticsearch.node.stats.fs.summary.available.bytes`*:: + -- -The description of the vulnerability that provides additional context of the vulnerability. For example (https://cve.mitre.org/about/faqs.html#cve_entry_descriptions_created[Common Vulnerabilities and Exposure CVE description]) -type: keyword -example: In macOS before 2.12.6, there is a vulnerability in the RPC... +type: long --- +format: bytes -*`vulnerability.description.text`*:: -+ -- -type: text --- +[float] +=== cluster.pending_task -*`vulnerability.enumeration`*:: +`cluster.pending_task` contains a pending task description. + + + +*`elasticsearch.cluster.pending_task.insert_order`*:: + -- -The type of identifier used for this vulnerability. For example (https://cve.mitre.org/about/) +Insert order -type: keyword -example: CVE +type: long -- -*`vulnerability.id`*:: +*`elasticsearch.cluster.pending_task.priority`*:: + -- -The identification (ID) is the number portion of a vulnerability entry. It includes a unique identification number for the vulnerability. For example (https://cve.mitre.org/about/faqs.html#what_is_cve_id)[Common Vulnerabilities and Exposure CVE ID] +Priority -type: keyword -example: CVE-2019-00001 +type: long -- -*`vulnerability.reference`*:: +*`elasticsearch.cluster.pending_task.source`*:: + -- -A resource that provides additional information, context, and mitigations for the identified vulnerability. +Source. For example: put-mapping -type: keyword -example: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6111 +type: keyword -- -*`vulnerability.report_id`*:: +*`elasticsearch.cluster.pending_task.time_in_queue.ms`*:: + -- -The report or scan identification number. +Time in queue -type: keyword -example: 20191018.0001 +type: long -- -*`vulnerability.scanner.vendor`*:: +[float] +=== shard + +shard fields + + + +*`elasticsearch.shard.primary`*:: + -- -The name of the vulnerability scanner vendor. +True if this is the primary shard. -type: keyword -example: Tenable +type: boolean -- -*`vulnerability.score.base`*:: +*`elasticsearch.shard.number`*:: + -- -Scores can range from 0.0 to 10.0, with 10.0 being the most severe. -Base scores cover an assessment for exploitability metrics (attack vector, complexity, privileges, and user interaction), impact metrics (confidentiality, integrity, and availability), and scope. For example (https://www.first.org/cvss/specification-document) +The number of this shard. -type: float -example: 5.5 +type: long -- -*`vulnerability.score.environmental`*:: +*`elasticsearch.shard.state`*:: + -- -Scores can range from 0.0 to 10.0, with 10.0 being the most severe. -Environmental scores cover an assessment for any modified Base metrics, confidentiality, integrity, and availability requirements. For example (https://www.first.org/cvss/specification-document) +The state of this shard. -type: float -example: 5.5 +type: keyword -- -*`vulnerability.score.temporal`*:: +*`elasticsearch.shard.relocating_node.name`*:: + -- -Scores can range from 0.0 to 10.0, with 10.0 being the most severe. -Temporal scores cover an assessment for code maturity, remediation level, and confidence. For example (https://www.first.org/cvss/specification-document) +The node the shard was relocated from. -type: float + +type: keyword -- -*`vulnerability.score.version`*:: +[[exported-fields-envoyproxy]] +== envoyproxy fields + +envoyproxy module + + + +[float] +=== envoyproxy + + + + +[float] +=== server + +Contains envoy proxy server stats + + + + +*`envoyproxy.server.cluster_manager.active_clusters`*:: + -- -The National Vulnerability Database (NVD) provides qualitative severity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score ranges in addition to the severity ratings for CVSS v3.0 as they are defined in the CVSS v3.0 specification. -CVSS is owned and managed by FIRST.Org, Inc. (FIRST), a US-based non-profit organization, whose mission is to help computer security incident response teams across the world. For example (https://nvd.nist.gov/vuln-metrics/cvss) +Number of currently active (warmed) clusters -type: keyword -example: 2.0 +type: integer -- -*`vulnerability.severity`*:: +*`envoyproxy.server.cluster_manager.cluster_added`*:: + -- -The severity of the vulnerability can help with metrics and internal prioritization regarding remediation. For example (https://nvd.nist.gov/vuln-metrics/cvss) +Total clusters added (either via static config or CDS) -type: keyword -example: Critical +type: integer -- -[[exported-fields-elasticsearch]] -== Elasticsearch fields +*`envoyproxy.server.cluster_manager.cluster_modified`*:: ++ +-- +Total clusters modified (via CDS) -Elasticsearch module +type: integer +-- -[float] -=== elasticsearch +*`envoyproxy.server.cluster_manager.cluster_removed`*:: ++ +-- +Total clusters removed (via CDS) +type: integer +-- -*`elasticsearch.cluster.name`*:: +*`envoyproxy.server.cluster_manager.warming_clusters`*:: + -- -Elasticsearch cluster name. +Number of currently warming (not active) clusters -type: keyword +type: integer -- -*`elasticsearch.cluster.id`*:: +*`envoyproxy.server.cluster_manager.cluster_updated`*:: + -- -Elasticsearch cluster id. +Total cluster updates -type: keyword +type: integer -- -*`elasticsearch.cluster.state.id`*:: +*`envoyproxy.server.cluster_manager.cluster_updated_via_merge`*:: + -- -Elasticsearch state id. +Total cluster updates applied as merged updates -type: keyword +type: integer -- -*`elasticsearch.node.id`*:: +*`envoyproxy.server.cluster_manager.update_merge_cancelled`*:: + -- -Node ID +Total merged updates that got cancelled and delivered early -type: keyword +type: integer -- -*`elasticsearch.node.name`*:: +*`envoyproxy.server.cluster_manager.update_out_of_merge_window`*:: + -- -Node name. +Total updates which arrived out of a merge window -type: keyword +type: integer -- -[float] -=== ccr -Cross-cluster replication stats +*`envoyproxy.server.filesystem.flushed_by_timer`*:: ++ +-- +Total number of times internal flush buffers are written to a file due to flush timeout +type: integer +-- -*`elasticsearch.ccr.leader.index`*:: +*`envoyproxy.server.filesystem.reopen_failed`*:: + -- -Name of leader index +Total number of times a file was failed to be opened -type: keyword +type: integer -- -*`elasticsearch.ccr.leader.max_seq_no`*:: +*`envoyproxy.server.filesystem.write_buffered`*:: + -- -Maximum sequence number of operation on the leader shard +Total number of times file data is moved to Envoys internal flush buffer -type: long +type: integer -- - -*`elasticsearch.ccr.follower.index`*:: +*`envoyproxy.server.filesystem.write_completed`*:: + -- -Name of follower index +Total number of times a file was written -type: keyword +type: integer -- -*`elasticsearch.ccr.follower.shard.number`*:: +*`envoyproxy.server.filesystem.write_total_buffered`*:: + -- -Number of the shard within the index +Current total size of internal flush buffer in bytes -type: long +type: integer -- -*`elasticsearch.ccr.follower.operations_written`*:: +*`envoyproxy.server.filesystem.write_failed`*:: + -- -Number of operations indexed (replicated) into the follower shard from the leader shard +Total number of times an error occurred during a file write operation -type: long +type: integer -- -*`elasticsearch.ccr.follower.time_since_last_read.ms`*:: + +*`envoyproxy.server.runtime.load_error`*:: + -- -Time, in ms, since the follower last fetched from the leader +Total number of load attempts that resulted in an error in any layer -type: long +type: integer -- -*`elasticsearch.ccr.follower.global_checkpoint`*:: +*`envoyproxy.server.runtime.load_success`*:: + -- -Global checkpoint value on follower shard +Total number of load attempts that were successful at all layers -type: long +type: integer -- -[float] -=== cluster.stats +*`envoyproxy.server.runtime.num_keys`*:: ++ +-- +Number of keys currently loaded -Cluster stats +type: integer +-- -*`elasticsearch.cluster.stats.status`*:: +*`envoyproxy.server.runtime.override_dir_exists`*:: + -- -Cluster status (green, yellow, red). +Total number of loads that did use an override directory -type: keyword +type: integer -- -[float] -=== nodes +*`envoyproxy.server.runtime.override_dir_not_exists`*:: ++ +-- +Total number of loads that did not use an override directory -Nodes statistics. +type: integer +-- -*`elasticsearch.cluster.stats.nodes.count`*:: +*`envoyproxy.server.runtime.admin_overrides_active`*:: + -- -Total number of nodes in cluster. +1 if any admin overrides are active otherwise 0 -type: long +type: integer -- -*`elasticsearch.cluster.stats.nodes.master`*:: +*`envoyproxy.server.runtime.deprecated_feature_use`*:: + -- -Number of master-eligible nodes in cluster. +Total number of times deprecated features were used. -type: long +type: integer -- -*`elasticsearch.cluster.stats.nodes.data`*:: +*`envoyproxy.server.runtime.num_layers`*:: + -- -Number of data nodes in cluster. +Number of layers currently active (without loading errors) -type: long +type: integer -- -[float] -=== indices - -Indices statistics. - - -*`elasticsearch.cluster.stats.indices.count`*:: +*`envoyproxy.server.listener_manager.listener_added`*:: + -- -Total number of indices in cluster. +Total listeners added (either via static config or LDS) -type: long +type: integer -- -[float] -=== shards +*`envoyproxy.server.listener_manager.listener_create_failure`*:: ++ +-- +Total failed listener object additions to workers -Shard statistics. +type: integer +-- -*`elasticsearch.cluster.stats.indices.shards.count`*:: +*`envoyproxy.server.listener_manager.listener_create_success`*:: + -- -Total number of shards in cluster. +Total listener objects successfully added to workers -type: long +type: integer -- -*`elasticsearch.cluster.stats.indices.shards.primaries`*:: +*`envoyproxy.server.listener_manager.listener_modified`*:: + -- -Total number of primary shards in cluster. +Total listeners modified (via LDS) -type: long +type: integer -- -*`elasticsearch.cluster.stats.indices.fielddata.memory.bytes`*:: +*`envoyproxy.server.listener_manager.listener_removed`*:: + -- -Memory used for fielddata. +Total listeners removed (via LDS) -type: long +type: integer -- -[float] -=== enrich +*`envoyproxy.server.listener_manager.total_listeners_active`*:: ++ +-- +Number of currently active listeners -Enrich stats +type: integer +-- -*`elasticsearch.enrich.queue.size`*:: +*`envoyproxy.server.listener_manager.total_listeners_draining`*:: + -- -Number of search requests in the queue. +Number of currently draining listeners -type: long +type: integer -- - -*`elasticsearch.enrich.remote_requests.current`*:: +*`envoyproxy.server.listener_manager.total_listeners_warming`*:: + -- -Current number of outstanding remote requests. +Number of currently warming listeners -type: long +type: integer -- -*`elasticsearch.enrich.remote_requests.total`*:: +*`envoyproxy.server.listener_manager.listener_stopped`*:: + -- -Number of outstanding remote requests executed since node startup. +Total listeners stopped -type: long +type: integer -- -*`elasticsearch.enrich.executed_searches.total`*:: + +*`envoyproxy.server.stats.overflow`*:: + -- -Number of search requests that enrich processors have executed since node startup. +Total number of times Envoy cannot allocate a statistic due to a shortage of shared memory -type: long +type: integer -- -[float] -=== index -index +*`envoyproxy.server.server.days_until_first_cert_expiring`*:: ++ +-- +Number of days until the next certificate being managed will expire + +type: integer +-- -*`elasticsearch.index.name`*:: +*`envoyproxy.server.server.live`*:: + -- -Index name. +1 if the server is not currently draining, 0 otherwise -type: keyword +type: integer -- - -*`elasticsearch.index.total.docs.count`*:: +*`envoyproxy.server.server.memory_allocated`*:: + -- -Total number of documents in the index. +Current amount of allocated memory in bytes -type: long +type: integer -- -*`elasticsearch.index.total.docs.deleted`*:: +*`envoyproxy.server.server.memory_heap_size`*:: + -- -Total number of deleted documents in the index. +Current reserved heap size in bytes -type: long +type: integer -- -*`elasticsearch.index.total.store.size.bytes`*:: +*`envoyproxy.server.server.parent_connections`*:: + -- -Total size of the index in bytes. - +Total connections of the old Envoy process on hot restart -type: long -format: bytes +type: integer -- -*`elasticsearch.index.total.segments.count`*:: +*`envoyproxy.server.server.total_connections`*:: + -- -Total number of index segments. +Total connections of both new and old Envoy processes -type: long +type: integer -- -*`elasticsearch.index.total.segments.memory.bytes`*:: +*`envoyproxy.server.server.uptime`*:: + -- -Total number of memory used by the segments in bytes. - +Current server uptime in seconds -type: long -format: bytes +type: integer -- -[float] -=== index.recovery +*`envoyproxy.server.server.version`*:: ++ +-- +Integer represented version number based on SCM revision -index +type: integer +-- -*`elasticsearch.index.recovery.id`*:: +*`envoyproxy.server.server.watchdog_mega_miss`*:: + -- -Shard recovery id. +type: integer +-- -type: long +*`envoyproxy.server.server.watchdog_miss`*:: ++ +-- +type: integer -- -*`elasticsearch.index.recovery.type`*:: +*`envoyproxy.server.server.hot_restart_epoch`*:: + -- -Shard recovery type. +Current hot restart epoch -type: keyword +type: integer -- -*`elasticsearch.index.recovery.primary`*:: +*`envoyproxy.server.server.concurrency`*:: + -- -True if primary shard. +Number of worker threads -type: boolean +type: integer -- -*`elasticsearch.index.recovery.stage`*:: +*`envoyproxy.server.server.debug_assertion_failures`*:: + -- -Recovery stage. - - -type: keyword +type: integer -- -*`elasticsearch.index.recovery.target.id`*:: +*`envoyproxy.server.server.dynamic_unknown_fields`*:: + -- -Target node id. +Number of messages in dynamic configuration with unknown fields -type: keyword +type: integer -- -*`elasticsearch.index.recovery.target.host`*:: +*`envoyproxy.server.server.state`*:: + -- -Target node host address (could be IP address or hostname). +Current state of the Server -type: keyword +type: integer -- -*`elasticsearch.index.recovery.target.name`*:: +*`envoyproxy.server.server.static_unknown_fields`*:: + -- -Target node name. +Number of messages in static configuration with unknown fields -type: keyword +type: integer -- -*`elasticsearch.index.recovery.source.id`*:: +*`envoyproxy.server.server.stats_recent_lookups`*:: + -- -Source node id. - - -type: keyword +type: integer -- -*`elasticsearch.index.recovery.source.host`*:: + +*`envoyproxy.server.http2.header_overflow`*:: + -- -Source node host address (could be IP address or hostname). +Total number of connections reset due to the headers being larger than Envoy::Http::Http2::ConnectionImpl::StreamImpl::MAX_HEADER_SIZE (63k) -type: keyword +type: integer -- -*`elasticsearch.index.recovery.source.name`*:: +*`envoyproxy.server.http2.headers_cb_no_stream`*:: + -- -Source node name. +Total number of errors where a header callback is called without an associated stream. This tracks an unexpected occurrence due to an as yet undiagnosed bug -type: keyword +type: integer -- -[float] -=== index.summary - -index +*`envoyproxy.server.http2.rx_messaging_error`*:: ++ +-- +Total number of invalid received frames that violated section 8 of the HTTP/2 spec. This will result in a tx_reset +type: integer +-- -*`elasticsearch.index.summary.primaries.docs.count`*:: +*`envoyproxy.server.http2.rx_reset`*:: + -- -Total number of documents in the index. +Total number of reset stream frames received by Envoy -type: long +type: integer -- -*`elasticsearch.index.summary.primaries.docs.deleted`*:: +*`envoyproxy.server.http2.too_many_header_frames`*:: + -- -Total number of deleted documents in the index. +Total number of times an HTTP2 connection is reset due to receiving too many headers frames. Envoy currently supports proxying at most one header frame for 100-Continue one non-100 response code header frame and one frame with trailers -type: long +type: integer -- -*`elasticsearch.index.summary.primaries.store.size.bytes`*:: +*`envoyproxy.server.http2.trailers`*:: + -- -Total size of the index in bytes. - +Total number of trailers seen on requests coming from downstream -type: long -format: bytes +type: integer -- -*`elasticsearch.index.summary.primaries.segments.count`*:: +*`envoyproxy.server.http2.tx_reset`*:: + -- -Total number of index segments. +Total number of reset stream frames transmitted by Envoy -type: long +type: integer -- -*`elasticsearch.index.summary.primaries.segments.memory.bytes`*:: -+ --- -Total number of memory used by the segments in bytes. +[[exported-fields-etcd]] +== Etcd fields +etcd Module -type: long -format: bytes --- +[float] +=== etcd + +`etcd` contains statistics that were read from Etcd -*`elasticsearch.index.summary.total.docs.count`*:: + +*`etcd.api_version`*:: + -- -Total number of documents in the index. - +Etcd API version for metrics retrieval -type: long --- +type: keyword -*`elasticsearch.index.summary.total.docs.deleted`*:: -+ -- -Total number of deleted documents in the index. +[float] +=== leader -type: long +Contains etcd leader statistics. --- -*`elasticsearch.index.summary.total.store.size.bytes`*:: -+ --- -Total size of the index in bytes. +[float] +=== followers.counts -type: long +The number of failed and successful Raft RPC requests. -format: bytes --- -*`elasticsearch.index.summary.total.segments.count`*:: +*`etcd.leader.followers.counts.followers.counts.success`*:: + -- -Total number of index segments. - +successful Raft RPC requests -type: long +type: integer -- -*`elasticsearch.index.summary.total.segments.memory.bytes`*:: +*`etcd.leader.followers.counts.followers.counts.fail`*:: + -- -Total number of memory used by the segments in bytes. - - -type: long +failed Raft RPC requests -format: bytes +type: integer -- [float] -=== ml.job +=== followers.latency -ml +latency to each peer in the cluster -*`elasticsearch.ml.job.id`*:: +*`etcd.leader.followers.latency.followers.latency.average`*:: + -- -Unique ml job id. - - -type: keyword +type: scaled_float -- -*`elasticsearch.ml.job.state`*:: +*`etcd.leader.followers.latency.followers.latency.current`*:: + -- -Job state. +type: scaled_float +-- -type: keyword +*`etcd.leader.followers.latency.followers.latency.maximum`*:: ++ +-- +type: scaled_float -- -*`elasticsearch.ml.job.data_counts.processed_record_count`*:: +*`etcd.leader.followers.latency.followers.latency.minimum`*:: + -- -Processed data events. +type: integer +-- -type: long +*`etcd.leader.followers.latency.follower.latency.standardDeviation`*:: ++ +-- +type: scaled_float -- -*`elasticsearch.ml.job.data_counts.invalid_date_count`*:: +*`etcd.leader.leader`*:: + -- -The number of records with either a missing date field or a date that could not be parsed. - +ID of actual leader -type: long +type: keyword -- [float] -=== node +=== server -node +Server metrics from the Etcd V3 /metrics endpoint -*`elasticsearch.node.version`*:: +*`etcd.server.has_leader`*:: + -- -Node version. +Whether a leader exists in the cluster -type: keyword +type: byte -- -[float] -=== jvm +*`etcd.server.leader_changes.count`*:: ++ +-- +Number of leader changes seen at the cluster -JVM Info. +type: long +-- -*`elasticsearch.node.jvm.version`*:: +*`etcd.server.proposals_committed.count`*:: + -- -JVM version. +Number of consensus proposals commited -type: keyword +type: long -- -*`elasticsearch.node.jvm.memory.heap.init.bytes`*:: +*`etcd.server.proposals_pending.count`*:: + -- -Heap init used by the JVM in bytes. +Number of consensus proposals pending type: long -format: bytes - -- -*`elasticsearch.node.jvm.memory.heap.max.bytes`*:: +*`etcd.server.proposals_failed.count`*:: + -- -Heap max used by the JVM in bytes. +Number of consensus proposals failed type: long -format: bytes - -- -*`elasticsearch.node.jvm.memory.nonheap.init.bytes`*:: +*`etcd.server.grpc_started.count`*:: + -- -Non-Heap init used by the JVM in bytes. +Number of sent gRPC requests type: long -format: bytes - -- -*`elasticsearch.node.jvm.memory.nonheap.max.bytes`*:: +*`etcd.server.grpc_handled.count`*:: + -- -Non-Heap max used by the JVM in bytes. +Number of received gRPC requests type: long -format: bytes - -- -*`elasticsearch.node.process.mlockall`*:: -+ --- -If process locked in memory. +[float] +=== disk + +Disk metrics from the Etcd V3 /metrics endpoint -type: boolean +*`etcd.disk.mvcc_db_total_size.bytes`*:: ++ -- +Size of stored data at MVCC -[float] -=== node.stats -node_stats +type: long +format: bytes +-- -[float] -=== indices +*`etcd.disk.wal_fsync_duration.ns.bucket.*`*:: ++ +-- +Latency for writing ahead logs to disk -Node indices stats +type: object +-- -*`elasticsearch.node.stats.indices.docs.count`*:: +*`etcd.disk.wal_fsync_duration.ns.count`*:: + -- -Total number of existing documents. +Write ahead logs count type: long -- -*`elasticsearch.node.stats.indices.docs.deleted`*:: +*`etcd.disk.wal_fsync_duration.ns.sum`*:: + -- -Total number of deleted documents. +Write ahead logs latency sum type: long -- -*`elasticsearch.node.stats.indices.segments.count`*:: +*`etcd.disk.backend_commit_duration.ns.bucket.*`*:: + -- -Total number of segments. +Latency for writing backend changes to disk -type: long +type: object -- -*`elasticsearch.node.stats.indices.segments.memory.bytes`*:: +*`etcd.disk.backend_commit_duration.ns.count`*:: + -- -Total size of segments in bytes. +Backend commits count type: long -format: bytes - -- -*`elasticsearch.node.stats.indices.store.size.bytes`*:: +*`etcd.disk.backend_commit_duration.ns.sum`*:: + -- -Total size of the store in bytes. +Backend commits latency sum type: long @@ -15352,23 +17612,36 @@ type: long -- [float] -=== jvm.mem.pools +=== memory -JVM memory pool stats +Memory metrics from the Etcd V3 /metrics endpoint + + + +*`etcd.memory.go_memstats_alloc.bytes`*:: ++ +-- +Memory allocated bytes as of MemStats Go + + +type: long +format: bytes +-- [float] -=== old +=== network -Old memory pool stats. +Network metrics from the Etcd V3 /metrics endpoint -*`elasticsearch.node.stats.jvm.mem.pools.old.max.bytes`*:: +*`etcd.network.client_grpc_sent.bytes`*:: + -- -Max bytes. +gRPC sent bytes total + type: long @@ -15376,10 +17649,11 @@ format: bytes -- -*`elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes`*:: +*`etcd.network.client_grpc_received.bytes`*:: + -- -Peak bytes. +gRPC received bytes total + type: long @@ -15387,1797 +17661,1823 @@ format: bytes -- -*`elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes`*:: +[float] +=== self + +Contains etcd self statistics. + + + +*`etcd.self.id`*:: + -- -Peak max bytes. +the unique identifier for the member -type: long -format: bytes +type: keyword -- -*`elasticsearch.node.stats.jvm.mem.pools.old.used.bytes`*:: +*`etcd.self.leaderinfo.leader`*:: + -- -Used bytes. +id of the current leader member -type: long -format: bytes +type: keyword -- -[float] -=== young +*`etcd.self.leaderinfo.starttime`*:: ++ +-- +the time when this node was started -Young memory pool stats. +type: keyword +-- -*`elasticsearch.node.stats.jvm.mem.pools.young.max.bytes`*:: +*`etcd.self.leaderinfo.uptime`*:: + -- -Max bytes. +amount of time the leader has been leader -type: long -format: bytes +type: keyword -- -*`elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes`*:: +*`etcd.self.name`*:: + -- -Peak bytes. +this member's name -type: long -format: bytes +type: keyword -- -*`elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes`*:: +*`etcd.self.recv.appendrequest.count`*:: + -- -Peak max bytes. +number of append requests this node has processed -type: long -format: bytes +type: integer -- -*`elasticsearch.node.stats.jvm.mem.pools.young.used.bytes`*:: +*`etcd.self.recv.bandwidthrate`*:: + -- -Used bytes. +number of bytes per second this node is receiving (follower only) -type: long -format: bytes +type: scaled_float -- -[float] -=== survivor +*`etcd.self.recv.pkgrate`*:: ++ +-- +number of requests per second this node is receiving (follower only) -Survivor memory pool stats. +type: scaled_float +-- -*`elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes`*:: +*`etcd.self.send.appendrequest.count`*:: + -- -Max bytes. +number of requests that this node has sent -type: long -format: bytes +type: integer -- -*`elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes`*:: +*`etcd.self.send.bandwidthrate`*:: + -- -Peak bytes. +number of bytes per second this node is sending (leader only). This value is undefined on single member clusters. -type: long -format: bytes +type: scaled_float -- -*`elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes`*:: +*`etcd.self.send.pkgrate`*:: + -- -Peak max bytes. +number of requests per second this node is sending (leader only). This value is undefined on single member clusters. -type: long -format: bytes +type: scaled_float -- -*`elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes`*:: +*`etcd.self.starttime`*:: + -- -Used bytes. +the time when this node was started -type: long -format: bytes +type: keyword -- -[float] -=== jvm.gc.collectors +*`etcd.self.state`*:: ++ +-- +either leader or follower -GC collector stats. +type: keyword +-- [float] -=== old.collection +=== store -Old collection gc. +The store statistics include information about the operations that this node has handled. -*`elasticsearch.node.stats.jvm.gc.collectors.old.collection.count`*:: +*`etcd.store.gets.success`*:: + -- +type: integer +-- -type: long +*`etcd.store.gets.fail`*:: ++ +-- +type: integer -- -*`elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms`*:: +*`etcd.store.sets.success`*:: + -- +type: integer +-- -type: long +*`etcd.store.sets.fail`*:: ++ +-- +type: integer -- -[float] -=== young.collection +*`etcd.store.delete.success`*:: ++ +-- +type: integer -Young collection gc. +-- +*`etcd.store.delete.fail`*:: ++ +-- +type: integer +-- -*`elasticsearch.node.stats.jvm.gc.collectors.young.collection.count`*:: +*`etcd.store.update.success`*:: + -- +type: integer +-- -type: long +*`etcd.store.update.fail`*:: ++ +-- +type: integer -- -*`elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms`*:: +*`etcd.store.create.success`*:: + -- +type: integer +-- -type: long +*`etcd.store.create.fail`*:: ++ +-- +type: integer -- -[float] -=== fs.summary +*`etcd.store.compareandswap.success`*:: ++ +-- +type: integer -File system summary +-- +*`etcd.store.compareandswap.fail`*:: ++ +-- +type: integer +-- -*`elasticsearch.node.stats.fs.summary.total.bytes`*:: +*`etcd.store.compareanddelete.success`*:: + -- +type: integer +-- -type: long +*`etcd.store.compareanddelete.fail`*:: ++ +-- +type: integer -format: bytes +-- + +*`etcd.store.expire.count`*:: ++ +-- +type: integer -- -*`elasticsearch.node.stats.fs.summary.free.bytes`*:: +*`etcd.store.watchers`*:: + -- +type: integer +-- -type: long +[[exported-fields-golang]] +== Golang fields -format: bytes +Golang module --- -*`elasticsearch.node.stats.fs.summary.available.bytes`*:: -+ --- +[float] +=== golang -type: long -format: bytes --- [float] -=== cluster.pending_task +=== expvar -`cluster.pending_task` contains a pending task description. +expvar -*`elasticsearch.cluster.pending_task.insert_order`*:: +*`golang.expvar.cmdline`*:: + -- -Insert order +The cmdline of this Go program start with. -type: long +type: keyword -- -*`elasticsearch.cluster.pending_task.priority`*:: -+ --- -Priority +[float] +=== heap +The Go program heap information exposed by expvar. -type: long --- -*`elasticsearch.cluster.pending_task.source`*:: +*`golang.heap.cmdline`*:: + -- -Source. For example: put-mapping +The cmdline of this Go program start with. type: keyword -- -*`elasticsearch.cluster.pending_task.time_in_queue.ms`*:: -+ --- -Time in queue +[float] +=== gc +Garbage collector summary. -type: long --- [float] -=== shard +=== total_pause -shard fields +Total GC pause duration over lifetime of process. -*`elasticsearch.shard.primary`*:: +*`golang.heap.gc.total_pause.ns`*:: + -- -True if this is the primary shard. +Duration in Ns. -type: boolean +type: long -- -*`elasticsearch.shard.number`*:: +*`golang.heap.gc.total_count`*:: + -- -The number of this shard. +Total number of GC was happened. type: long -- -*`elasticsearch.shard.state`*:: +*`golang.heap.gc.next_gc_limit`*:: + -- -The state of this shard. +Next collection will happen when HeapAlloc > this amount. -type: keyword +type: long + +format: bytes -- -*`elasticsearch.shard.relocating_node.name`*:: +*`golang.heap.gc.cpu_fraction`*:: + -- -The node the shard was relocated from. +Fraction of CPU time used by GC. -type: keyword +type: float -- -[[exported-fields-envoyproxy]] -== envoyproxy fields - -envoyproxy module - - - -[float] -=== envoyproxy - - - - [float] -=== server - -Contains envoy proxy server stats +=== pause +Last GC pause durations during the monitoring period. -*`envoyproxy.server.cluster_manager.active_clusters`*:: +*`golang.heap.gc.pause.count`*:: + -- -Number of currently active (warmed) clusters +Count of GC pause duration during this collect period. -type: integer +type: long -- -*`envoyproxy.server.cluster_manager.cluster_added`*:: -+ --- -Total clusters added (either via static config or CDS) +[float] +=== sum +Total GC pause duration during this collect period. -type: integer --- -*`envoyproxy.server.cluster_manager.cluster_modified`*:: +*`golang.heap.gc.pause.sum.ns`*:: + -- -Total clusters modified (via CDS) +Duration in Ns. -type: integer +type: long -- -*`envoyproxy.server.cluster_manager.cluster_removed`*:: -+ --- -Total clusters removed (via CDS) +[float] +=== max +Max GC pause duration during this collect period. -type: integer --- -*`envoyproxy.server.cluster_manager.warming_clusters`*:: +*`golang.heap.gc.pause.max.ns`*:: + -- -Number of currently warming (not active) clusters +Duration in Ns. -type: integer +type: long -- -*`envoyproxy.server.cluster_manager.cluster_updated`*:: -+ --- -Total cluster updates +[float] +=== avg +Average GC pause duration during this collect period. -type: integer --- -*`envoyproxy.server.cluster_manager.cluster_updated_via_merge`*:: +*`golang.heap.gc.pause.avg.ns`*:: + -- -Total cluster updates applied as merged updates +Duration in Ns. -type: integer +type: long -- -*`envoyproxy.server.cluster_manager.update_merge_cancelled`*:: -+ --- -Total merged updates that got cancelled and delivered early +[float] +=== system +Heap summary,which bytes was obtained from system. -type: integer --- -*`envoyproxy.server.cluster_manager.update_out_of_merge_window`*:: +*`golang.heap.system.total`*:: + -- -Total updates which arrived out of a merge window +Total bytes obtained from system (sum of XxxSys below). -type: integer +type: long --- +format: bytes +-- -*`envoyproxy.server.filesystem.flushed_by_timer`*:: +*`golang.heap.system.obtained`*:: + -- -Total number of times internal flush buffers are written to a file due to flush timeout +Via HeapSys, bytes obtained from system. heap_sys = heap_idle + heap_inuse. -type: integer +type: long + +format: bytes -- -*`envoyproxy.server.filesystem.reopen_failed`*:: +*`golang.heap.system.stack`*:: + -- -Total number of times a file was failed to be opened +Bytes used by stack allocator, and these bytes was obtained from system. -type: integer +type: long + +format: bytes -- -*`envoyproxy.server.filesystem.write_buffered`*:: +*`golang.heap.system.released`*:: + -- -Total number of times file data is moved to Envoys internal flush buffer +Bytes released to the OS. -type: integer +type: long --- +format: bytes -*`envoyproxy.server.filesystem.write_completed`*:: -+ -- -Total number of times a file was written +[float] +=== allocations + +Heap allocations summary. -type: integer --- -*`envoyproxy.server.filesystem.write_total_buffered`*:: +*`golang.heap.allocations.mallocs`*:: + -- -Current total size of internal flush buffer in bytes +Number of mallocs. -type: integer +type: long -- -*`envoyproxy.server.filesystem.write_failed`*:: +*`golang.heap.allocations.frees`*:: + -- -Total number of times an error occurred during a file write operation +Number of frees. -type: integer +type: long -- - -*`envoyproxy.server.runtime.load_error`*:: +*`golang.heap.allocations.objects`*:: + -- -Total number of load attempts that resulted in an error in any layer +Total number of allocated objects. -type: integer +type: long -- -*`envoyproxy.server.runtime.load_success`*:: +*`golang.heap.allocations.total`*:: + -- -Total number of load attempts that were successful at all layers +Bytes allocated (even if freed) throughout the lifetime. -type: integer +type: long + +format: bytes -- -*`envoyproxy.server.runtime.num_keys`*:: +*`golang.heap.allocations.allocated`*:: + -- -Number of keys currently loaded +Bytes allocated and not yet freed (same as Alloc above). -type: integer +type: long + +format: bytes -- -*`envoyproxy.server.runtime.override_dir_exists`*:: +*`golang.heap.allocations.idle`*:: + -- -Total number of loads that did use an override directory +Bytes in idle spans. -type: integer +type: long + +format: bytes -- -*`envoyproxy.server.runtime.override_dir_not_exists`*:: +*`golang.heap.allocations.active`*:: + -- -Total number of loads that did not use an override directory +Bytes in non-idle span. -type: integer +type: long --- +format: bytes -*`envoyproxy.server.runtime.admin_overrides_active`*:: -+ -- -1 if any admin overrides are active otherwise 0 - - -type: integer --- +[[exported-fields-googlecloud]] +== Google Cloud Platform fields -*`envoyproxy.server.runtime.deprecated_feature_use`*:: -+ --- -Total number of times deprecated features were used. +GCP module -type: integer --- -*`envoyproxy.server.runtime.num_layers`*:: +*`googlecloud.labels`*:: + -- -Number of layers currently active (without loading errors) - - -type: integer +type: object -- - -*`envoyproxy.server.listener_manager.listener_added`*:: +*`googlecloud.stackdriver.*.*.*.*`*:: + -- -Total listeners added (either via static config or LDS) +Metrics that returned from StackDriver API query. -type: integer +type: object -- -*`envoyproxy.server.listener_manager.listener_create_failure`*:: -+ --- -Total failed listener object additions to workers +[float] +=== compute +Google Cloud Compute metrics -type: integer --- -*`envoyproxy.server.listener_manager.listener_create_success`*:: + +*`googlecloud.compute.instance.firewall.dropped_bytes_count.value`*:: + -- -Total listener objects successfully added to workers - +Incoming bytes dropped by the firewall -type: integer +type: long -- -*`envoyproxy.server.listener_manager.listener_modified`*:: +*`googlecloud.compute.instance.firewall.dropped_packets_count.value`*:: + -- -Total listeners modified (via LDS) +Incoming packets dropped by the firewall - -type: integer +type: long -- -*`envoyproxy.server.listener_manager.listener_removed`*:: + +*`googlecloud.compute.instance.cpu.reserved_cores.value`*:: + -- -Total listeners removed (via LDS) +Number of cores reserved on the host of the instance - -type: integer +type: double -- -*`envoyproxy.server.listener_manager.total_listeners_active`*:: +*`googlecloud.compute.instance.cpu.utilization.value`*:: + -- -Number of currently active listeners - +The fraction of the allocated CPU that is currently in use on the instance -type: integer +type: double -- -*`envoyproxy.server.listener_manager.total_listeners_draining`*:: +*`googlecloud.compute.instance.cpu.usage_time.value`*:: + -- -Number of currently draining listeners +Usage for all cores in seconds - -type: integer +type: double -- -*`envoyproxy.server.listener_manager.total_listeners_warming`*:: + +*`googlecloud.compute.instance.disk.read_bytes_count.value`*:: + -- -Number of currently warming listeners +Count of bytes read from disk - -type: integer +type: long -- -*`envoyproxy.server.listener_manager.listener_stopped`*:: +*`googlecloud.compute.instance.disk.read_ops_count.value`*:: + -- -Total listeners stopped - +Count of disk read IO operations -type: integer +type: long -- - -*`envoyproxy.server.stats.overflow`*:: +*`googlecloud.compute.instance.disk.write_bytes_count.value`*:: + -- -Total number of times Envoy cannot allocate a statistic due to a shortage of shared memory - +Count of bytes written to disk -type: integer +type: long -- - -*`envoyproxy.server.server.days_until_first_cert_expiring`*:: +*`googlecloud.compute.instance.disk.write_ops_count.value`*:: + -- -Number of days until the next certificate being managed will expire - +Count of disk write IO operations -type: integer +type: long -- -*`envoyproxy.server.server.live`*:: +*`googlecloud.compute.instance.uptime.value`*:: + -- -1 if the server is not currently draining, 0 otherwise +How long the VM has been running, in seconds - -type: integer +type: long -- -*`envoyproxy.server.server.memory_allocated`*:: + +*`googlecloud.compute.instance.network.received_bytes_count.value`*:: + -- -Current amount of allocated memory in bytes +Count of bytes received from the network - -type: integer +type: long -- -*`envoyproxy.server.server.memory_heap_size`*:: +*`googlecloud.compute.instance.network.received_packets_count.value`*:: + -- -Current reserved heap size in bytes - +Count of packets received from the network -type: integer +type: long -- -*`envoyproxy.server.server.parent_connections`*:: +*`googlecloud.compute.instance.network.sent_bytes_count.value`*:: + -- -Total connections of the old Envoy process on hot restart +Count of bytes sent over the network - -type: integer +type: long -- -*`envoyproxy.server.server.total_connections`*:: +*`googlecloud.compute.instance.network.sent_packets_count.value`*:: + -- -Total connections of both new and old Envoy processes - +Count of packets sent over the network -type: integer +type: long -- -*`envoyproxy.server.server.uptime`*:: -+ --- -Current server uptime in seconds +[float] +=== loadbalancing +Google Cloud Load Balancing metrics -type: integer --- +[float] +=== https -*`envoyproxy.server.server.version`*:: -+ --- -Integer represented version number based on SCM revision +Google Cloud Load Balancing metrics -type: integer +[float] +=== backend_latencies --- +A distribution of the latency calculated from when the request was sent by the proxy to the backend until the proxy received from the backend the last byte of response. -*`envoyproxy.server.server.watchdog_mega_miss`*:: + +*`googlecloud.loadbalancing.https.backend_latencies.count.value`*:: + -- -type: integer +type: long -- -*`envoyproxy.server.server.watchdog_miss`*:: +*`googlecloud.loadbalancing.https.backend_latencies.mean.value`*:: + -- -type: integer +type: long -- -*`envoyproxy.server.server.hot_restart_epoch`*:: +*`googlecloud.loadbalancing.https.backend_latencies.bucket_counts.value`*:: + -- -Current hot restart epoch +type: long + +-- -type: integer --- -*`envoyproxy.server.server.concurrency`*:: +*`googlecloud.loadbalancing.https.backend_latencies.bucket_options.Options.ExponentialBuckets.growth_factor.value`*:: + -- -Number of worker threads - - -type: integer +type: double -- -*`envoyproxy.server.server.debug_assertion_failures`*:: +*`googlecloud.loadbalancing.https.backend_latencies.bucket_options.Options.ExponentialBuckets.scale.value`*:: + -- -type: integer +type: long -- -*`envoyproxy.server.server.dynamic_unknown_fields`*:: +*`googlecloud.loadbalancing.https.backend_latencies.bucket_options.Options.ExponentialBuckets.num_finite_buckets.value`*:: + -- -Number of messages in dynamic configuration with unknown fields - - -type: integer +type: long -- -*`envoyproxy.server.server.state`*:: +*`googlecloud.loadbalancing.https.backend_request_bytes_count.value`*:: + -- -Current state of the Server +The number of bytes sent as requests from HTTP/S load balancer to backends. - -type: integer +type: long -- -*`envoyproxy.server.server.static_unknown_fields`*:: +*`googlecloud.loadbalancing.https.backend_request_count.value`*:: + -- -Number of messages in static configuration with unknown fields - +The number of requests served by backends of HTTP/S load balancer. -type: integer +type: long -- -*`envoyproxy.server.server.stats_recent_lookups`*:: +*`googlecloud.loadbalancing.https.backend_response_bytes_count.value`*:: + -- -type: integer - --- +The number of bytes sent as responses from backends (or cache) to HTTP/S load balancer. +type: long -*`envoyproxy.server.http2.header_overflow`*:: -+ -- -Total number of connections reset due to the headers being larger than Envoy::Http::Http2::ConnectionImpl::StreamImpl::MAX_HEADER_SIZE (63k) +[float] +=== frontend_tcp_rtt -type: integer +A distribution of the RTT measured for each connection between client and proxy. --- -*`envoyproxy.server.http2.headers_cb_no_stream`*:: +*`googlecloud.loadbalancing.https.frontend_tcp_rtt.count.value`*:: + -- -Total number of errors where a header callback is called without an associated stream. This tracks an unexpected occurrence due to an as yet undiagnosed bug +type: long +-- -type: integer +*`googlecloud.loadbalancing.https.frontend_tcp_rtt.mean.value`*:: ++ +-- +type: long -- -*`envoyproxy.server.http2.rx_messaging_error`*:: +*`googlecloud.loadbalancing.https.frontend_tcp_rtt.bucket_counts.value`*:: + -- -Total number of invalid received frames that violated section 8 of the HTTP/2 spec. This will result in a tx_reset +type: long +-- -type: integer --- -*`envoyproxy.server.http2.rx_reset`*:: + +*`googlecloud.loadbalancing.https.frontend_tcp_rtt.bucket_options.Options.ExponentialBuckets.growth_factor.value`*:: + -- -Total number of reset stream frames received by Envoy +type: double +-- -type: integer +*`googlecloud.loadbalancing.https.frontend_tcp_rtt.bucket_options.Options.ExponentialBuckets.scale.value`*:: ++ +-- +type: long -- -*`envoyproxy.server.http2.too_many_header_frames`*:: +*`googlecloud.loadbalancing.https.frontend_tcp_rtt.bucket_options.Options.ExponentialBuckets.num_finite_buckets.value`*:: + -- -Total number of times an HTTP2 connection is reset due to receiving too many headers frames. Envoy currently supports proxying at most one header frame for 100-Continue one non-100 response code header frame and one frame with trailers +type: long +-- -type: integer --- +[float] +=== backend_latencies -*`envoyproxy.server.http2.trailers`*:: -+ --- -Total number of trailers seen on requests coming from downstream +A distribution of the latency calculated from when the request was sent by the proxy to the backend until the proxy received from the backend the last byte of response. -type: integer +*`googlecloud.loadbalancing.https.internal.backend_latencies.count.value`*:: ++ +-- +type: long -- -*`envoyproxy.server.http2.tx_reset`*:: +*`googlecloud.loadbalancing.https.internal.backend_latencies.mean.value`*:: + -- -Total number of reset stream frames transmitted by Envoy +type: long +-- -type: integer +*`googlecloud.loadbalancing.https.internal.backend_latencies.bucket_counts.value`*:: ++ +-- +type: long -- -[[exported-fields-etcd]] -== Etcd fields -etcd Module +*`googlecloud.loadbalancing.https.internal.backend_latencies.bucket_options.Options.ExponentialBuckets.growth_factor.value`*:: ++ +-- +type: double -[float] -=== etcd +-- -`etcd` contains statistics that were read from Etcd +*`googlecloud.loadbalancing.https.internal.backend_latencies.bucket_options.Options.ExponentialBuckets.scale.value`*:: ++ +-- +type: long +-- +*`googlecloud.loadbalancing.https.internal.backend_latencies.bucket_options.Options.ExponentialBuckets.num_finite_buckets.value`*:: ++ +-- +type: long -*`etcd.api_version`*:: +-- + +*`googlecloud.loadbalancing.https.internal.request_bytes_count.value`*:: + -- -Etcd API version for metrics retrieval +The number of bytes sent as requests from clients to HTTP/S load balancer. +type: long -type: keyword +-- +*`googlecloud.loadbalancing.https.internal.request_count.value`*:: ++ -- +The number of requests served by HTTP/S load balancer. -[float] -=== leader +type: long -Contains etcd leader statistics. +-- +*`googlecloud.loadbalancing.https.internal.response_bytes_count.value`*:: ++ +-- +The number of bytes sent as responses from HTTP/S load balancer to clients. +type: long -[float] -=== followers.counts +-- -The number of failed and successful Raft RPC requests. +[float] +=== total_latencies +A distribution of the latency calculated from when the request was received by the proxy until the proxy got ACK from client on last response byte. -*`etcd.leader.followers.counts.followers.counts.success`*:: +*`googlecloud.loadbalancing.https.internal.total_latencies.count.value`*:: + -- -successful Raft RPC requests - -type: integer +type: long -- -*`etcd.leader.followers.counts.followers.counts.fail`*:: +*`googlecloud.loadbalancing.https.internal.total_latencies.mean.value`*:: + -- -failed Raft RPC requests +type: long -type: integer +-- +*`googlecloud.loadbalancing.https.internal.total_latencies.bucket_counts.value`*:: ++ -- +type: long -[float] -=== followers.latency +-- -latency to each peer in the cluster -*`etcd.leader.followers.latency.followers.latency.average`*:: +*`googlecloud.loadbalancing.https.internal.total_latencies.bucket_options.Options.ExponentialBuckets.growth_factor.value`*:: + -- -type: scaled_float +type: double -- -*`etcd.leader.followers.latency.followers.latency.current`*:: +*`googlecloud.loadbalancing.https.internal.total_latencies.bucket_options.Options.ExponentialBuckets.scale.value`*:: + -- -type: scaled_float +type: long -- -*`etcd.leader.followers.latency.followers.latency.maximum`*:: +*`googlecloud.loadbalancing.https.internal.total_latencies.bucket_options.Options.ExponentialBuckets.num_finite_buckets.value`*:: + -- -type: scaled_float +type: long -- -*`etcd.leader.followers.latency.followers.latency.minimum`*:: +*`googlecloud.loadbalancing.https.request_bytes_count.value`*:: + -- -type: integer +The number of bytes sent as requests from clients to HTTP/S load balancer. + +type: long -- -*`etcd.leader.followers.latency.follower.latency.standardDeviation`*:: +*`googlecloud.loadbalancing.https.request_count.value`*:: + -- -type: scaled_float +The number of requests served by HTTP/S load balancer. + +type: long -- -*`etcd.leader.leader`*:: +*`googlecloud.loadbalancing.https.response_bytes_count.value`*:: + -- -ID of actual leader +The number of bytes sent as responses from HTTP/S load balancer to clients. -type: keyword +type: long -- [float] -=== server - -Server metrics from the Etcd V3 /metrics endpoint +=== total_latencies +A distribution of the latency calculated from when the request was received by the proxy until the proxy got ACK from client on last response byte. -*`etcd.server.has_leader`*:: +*`googlecloud.loadbalancing.https.total_latencies.count.value`*:: + -- -Whether a leader exists in the cluster - - -type: byte +type: long -- -*`etcd.server.leader_changes.count`*:: +*`googlecloud.loadbalancing.https.total_latencies.mean.value`*:: + -- -Number of leader changes seen at the cluster - - type: long -- -*`etcd.server.proposals_committed.count`*:: +*`googlecloud.loadbalancing.https.total_latencies.bucket_counts.value`*:: + -- -Number of consensus proposals commited - - type: long -- -*`etcd.server.proposals_pending.count`*:: -+ --- -Number of consensus proposals pending - -type: long --- -*`etcd.server.proposals_failed.count`*:: +*`googlecloud.loadbalancing.https.total_latencies.bucket_options.Options.ExponentialBuckets.growth_factor.value`*:: + -- -Number of consensus proposals failed - - -type: long +type: double -- -*`etcd.server.grpc_started.count`*:: +*`googlecloud.loadbalancing.https.total_latencies.bucket_options.Options.ExponentialBuckets.scale.value`*:: + -- -Number of sent gRPC requests - - type: long -- -*`etcd.server.grpc_handled.count`*:: +*`googlecloud.loadbalancing.https.total_latencies.bucket_options.Options.ExponentialBuckets.num_finite_buckets.value`*:: + -- -Number of received gRPC requests - - type: long -- [float] -=== disk - -Disk metrics from the Etcd V3 /metrics endpoint +=== l3.internal +Google Cloud Load Balancing metrics -*`etcd.disk.mvcc_db_total_size.bytes`*:: +*`googlecloud.loadbalancing.l3.internal.egress_bytes_count.value`*:: + -- -Size of stored data at MVCC - +The number of bytes sent from ILB backend to client (for TCP flows it's counting bytes on application stream only). type: long -format: bytes - -- -*`etcd.disk.wal_fsync_duration.ns.bucket.*`*:: +*`googlecloud.loadbalancing.l3.internal.egress_packets_count.value`*:: + -- -Latency for writing ahead logs to disk +The number of packets sent from ILB backend to client of the flow. - -type: object +type: long -- -*`etcd.disk.wal_fsync_duration.ns.count`*:: +*`googlecloud.loadbalancing.l3.internal.ingress_bytes_count.value`*:: + -- -Write ahead logs count - +The number of bytes sent from client to ILB backend (for TCP flows it's counting bytes on application stream only). type: long -- -*`etcd.disk.wal_fsync_duration.ns.sum`*:: +*`googlecloud.loadbalancing.l3.internal.ingress_packets_count.value`*:: + -- -Write ahead logs latency sum - +The number of packets sent from client to ILB backend. type: long -- -*`etcd.disk.backend_commit_duration.ns.bucket.*`*:: -+ --- -Latency for writing backend changes to disk - +[float] +=== rtt_latencies -type: object +A distribution of RTT measured over TCP connections for ILB flows. --- -*`etcd.disk.backend_commit_duration.ns.count`*:: +*`googlecloud.loadbalancing.l3.internal.rtt_latencies.count.value`*:: + -- -Backend commits count - - type: long -- -*`etcd.disk.backend_commit_duration.ns.sum`*:: +*`googlecloud.loadbalancing.l3.internal.rtt_latencies.mean.value`*:: + -- -Backend commits latency sum +type: long +-- +*`googlecloud.loadbalancing.l3.internal.rtt_latencies.bucket_counts.value`*:: ++ +-- type: long -- -[float] -=== memory - -Memory metrics from the Etcd V3 /metrics endpoint -*`etcd.memory.go_memstats_alloc.bytes`*:: +*`googlecloud.loadbalancing.l3.internal.rtt_latencies.bucket_options.Options.ExponentialBuckets.growth_factor.value`*:: + -- -Memory allocated bytes as of MemStats Go +type: double +-- +*`googlecloud.loadbalancing.l3.internal.rtt_latencies.bucket_options.Options.ExponentialBuckets.scale.value`*:: ++ +-- type: long -format: bytes +-- + +*`googlecloud.loadbalancing.l3.internal.rtt_latencies.bucket_options.Options.ExponentialBuckets.num_finite_buckets.value`*:: ++ +-- +type: long -- [float] -=== network - -Network metrics from the Etcd V3 /metrics endpoint +=== tcp_ssl_proxy +Google Cloud Load Balancing metrics -*`etcd.network.client_grpc_sent.bytes`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.closed_connections.value`*:: + -- -gRPC sent bytes total - +Number of connections that were terminated over TCP/SSL proxy. type: long -format: bytes - -- -*`etcd.network.client_grpc_received.bytes`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.egress_bytes_count.value`*:: + -- -gRPC received bytes total - +Number of bytes sent from VM to client using proxy. type: long -format: bytes - -- [float] -=== self - -Contains etcd self statistics. +=== frontend_tcp_rtt +A distribution of the smoothed RTT (in ms) measured by the proxy's TCP stack, each minute application layer bytes pass from proxy to client. -*`etcd.self.id`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.frontend_tcp_rtt.count.value`*:: + -- -the unique identifier for the member +type: long +-- -type: keyword +*`googlecloud.loadbalancing.tcp_ssl_proxy.frontend_tcp_rtt.mean.value`*:: ++ +-- +type: long -- -*`etcd.self.leaderinfo.leader`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.frontend_tcp_rtt.bucket_counts.value`*:: + -- -id of the current leader member +type: long +-- -type: keyword --- -*`etcd.self.leaderinfo.starttime`*:: + +*`googlecloud.loadbalancing.tcp_ssl_proxy.frontend_tcp_rtt.bucket_options.Options.ExponentialBuckets.growth_factor.value`*:: + -- -the time when this node was started - - -type: keyword +type: double -- -*`etcd.self.leaderinfo.uptime`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.frontend_tcp_rtt.bucket_options.Options.ExponentialBuckets.scale.value`*:: + -- -amount of time the leader has been leader +type: long +-- -type: keyword +*`googlecloud.loadbalancing.tcp_ssl_proxy.frontend_tcp_rtt.bucket_options.Options.ExponentialBuckets.num_finite_buckets.value`*:: ++ +-- +type: long -- -*`etcd.self.name`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.ingress_bytes_count.value`*:: + -- -this member's name +Number of bytes sent from client to VM using proxy. - -type: keyword +type: long -- -*`etcd.self.recv.appendrequest.count`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.new_connections.value`*:: + -- -number of append requests this node has processed - +Number of connections that were created over TCP/SSL proxy. -type: integer +type: long -- -*`etcd.self.recv.bandwidthrate`*:: +*`googlecloud.loadbalancing.tcp_ssl_proxy.open_connections.value`*:: + -- -number of bytes per second this node is receiving (follower only) +Current number of outstanding connections through the TCP/SSL proxy. - -type: scaled_float +type: long -- -*`etcd.self.recv.pkgrate`*:: +[float] +=== pubsub + +Google Cloud PubSub metrics + + +[float] +=== subscription + +Suscription related metrics + + +*`googlecloud.pubsub.subscription.ack_message_count.value`*:: + -- -number of requests per second this node is receiving (follower only) - +Cumulative count of messages acknowledged by Acknowledge requests, grouped by delivery type. -type: scaled_float +type: long -- -*`etcd.self.send.appendrequest.count`*:: +*`googlecloud.pubsub.subscription.backlog_bytes.value`*:: + -- -number of requests that this node has sent +Total byte size of the unacknowledged messages (a.k.a. backlog messages) in a subscription. - -type: integer +type: long -- -*`etcd.self.send.bandwidthrate`*:: +*`googlecloud.pubsub.subscription.num_outstanding_messages.value`*:: + -- -number of bytes per second this node is sending (leader only). This value is undefined on single member clusters. - +Number of messages delivered to a subscription's push endpoint, but not yet acknowledged. -type: scaled_float +type: long -- -*`etcd.self.send.pkgrate`*:: +*`googlecloud.pubsub.subscription.num_undelivered_messages.value`*:: + -- -number of requests per second this node is sending (leader only). This value is undefined on single member clusters. +Number of unacknowledged messages (a.k.a. backlog messages) in a subscription. - -type: scaled_float +type: long -- -*`etcd.self.starttime`*:: +*`googlecloud.pubsub.subscription.oldest_unacked_message_age.value`*:: + -- -the time when this node was started - +Age (in seconds) of the oldest unacknowledged message (a.k.a. backlog message) in a subscription. -type: keyword +type: long -- -*`etcd.self.state`*:: +*`googlecloud.pubsub.subscription.pull_ack_message_operation_count.value`*:: + -- -either leader or follower +Cumulative count of acknowledge message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. +type: long -type: keyword +-- +*`googlecloud.pubsub.subscription.pull_ack_request_count.value`*:: ++ -- +Cumulative count of acknowledge requests, grouped by result. -[float] -=== store +type: long -The store statistics include information about the operations that this node has handled. +-- +*`googlecloud.pubsub.subscription.pull_message_operation_count.value`*:: ++ +-- +Cumulative count of pull message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. +type: long -*`etcd.store.gets.success`*:: +-- + +*`googlecloud.pubsub.subscription.pull_request_count.value`*:: + -- -type: integer +Cumulative count of pull requests, grouped by result. + +type: long -- -*`etcd.store.gets.fail`*:: +*`googlecloud.pubsub.subscription.push_request_count.value`*:: + -- -type: integer +Cumulative count of push attempts, grouped by result. Unlike pulls, the push server implementation does not batch user messages. So each request only contains one user message. The push server retries on errors, so a given user message can appear multiple times. + +type: long -- -*`etcd.store.sets.success`*:: +*`googlecloud.pubsub.subscription.push_request_latencies.value`*:: + -- -type: integer +Distribution of push request latencies (in microseconds), grouped by result. + +type: long -- -*`etcd.store.sets.fail`*:: +*`googlecloud.pubsub.subscription.sent_message_count.value`*:: + -- -type: integer +Cumulative count of messages sent by Cloud Pub/Sub to subscriber clients, grouped by delivery type. + +type: long -- -*`etcd.store.delete.success`*:: +*`googlecloud.pubsub.subscription.streaming_pull_ack_message_operation_count.value`*:: + -- -type: integer +Cumulative count of StreamingPull acknowledge message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. + +type: long -- -*`etcd.store.delete.fail`*:: +*`googlecloud.pubsub.subscription.streaming_pull_ack_request_count.value`*:: + -- -type: integer +Cumulative count of streaming pull requests with non-empty acknowledge ids, grouped by result. + +type: long -- -*`etcd.store.update.success`*:: +*`googlecloud.pubsub.subscription.streaming_pull_message_operation_count.value`*:: + -- -type: integer +Cumulative count of streaming pull message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count + +type: long -- -*`etcd.store.update.fail`*:: +*`googlecloud.pubsub.subscription.streaming_pull_response_count.value`*:: + -- -type: integer +Cumulative count of streaming pull responses, grouped by result. + +type: long -- -*`etcd.store.create.success`*:: +*`googlecloud.pubsub.subscription.dead_letter_message_count.value`*:: + -- -type: integer +Cumulative count of messages published to dead letter topic, grouped by result. + +type: long -- -*`etcd.store.create.fail`*:: +*`googlecloud.pubsub.subscription.mod_ack_deadline_message_count.value`*:: + -- -type: integer +Cumulative count of messages whose deadline was updated by ModifyAckDeadline requests, grouped by delivery type. + +type: long -- -*`etcd.store.compareandswap.success`*:: +*`googlecloud.pubsub.subscription.mod_ack_deadline_message_operation_count.value`*:: + -- -type: integer +Cumulative count of ModifyAckDeadline message operations, grouped by result. + +type: long -- -*`etcd.store.compareandswap.fail`*:: +*`googlecloud.pubsub.subscription.mod_ack_deadline_request_count.value`*:: + -- -type: integer +Cumulative count of ModifyAckDeadline requests, grouped by result. + +type: long -- -*`etcd.store.compareanddelete.success`*:: +*`googlecloud.pubsub.subscription.oldest_retained_acked_message_age.value`*:: + -- -type: integer +Age (in seconds) of the oldest acknowledged message retained in a subscription. + +type: long -- -*`etcd.store.compareanddelete.fail`*:: +*`googlecloud.pubsub.subscription.oldest_retained_acked_message_age_by_region.value`*:: + -- -type: integer +Age (in seconds) of the oldest acknowledged message retained in a subscription, broken down by Cloud region. + +type: long -- -*`etcd.store.expire.count`*:: +*`googlecloud.pubsub.subscription.oldest_unacked_message_age_by_region.value`*:: + -- -type: integer +Age (in seconds) of the oldest unacknowledged message in a subscription, broken down by Cloud region. + +type: long -- -*`etcd.store.watchers`*:: +*`googlecloud.pubsub.subscription.retained_acked_bytes.value`*:: + -- -type: integer +Total byte size of the acknowledged messages retained in a subscription. + +type: long -- -[[exported-fields-golang]] -== Golang fields +*`googlecloud.pubsub.subscription.retained_acked_bytes_by_region.value`*:: ++ +-- +Total byte size of the acknowledged messages retained in a subscription, broken down by Cloud region. -Golang module +type: long +-- +*`googlecloud.pubsub.subscription.seek_request_count.value`*:: ++ +-- +Cumulative count of seek attempts, grouped by result. -[float] -=== golang +type: long +-- +*`googlecloud.pubsub.subscription.streaming_pull_mod_ack_deadline_message_operation_count.value`*:: ++ +-- +Cumulative count of StreamingPull ModifyAckDeadline operations, grouped by result. +type: long -[float] -=== expvar +-- -expvar +*`googlecloud.pubsub.subscription.streaming_pull_mod_ack_deadline_request_count.value`*:: ++ +-- +Cumulative count of streaming pull requests with non-empty ModifyAckDeadline fields, grouped by result. +type: long +-- -*`golang.expvar.cmdline`*:: +*`googlecloud.pubsub.subscription.byte_cost.value`*:: + -- -The cmdline of this Go program start with. +Cumulative cost of operations, measured in bytes. This is used to measure quota utilization. - -type: keyword +type: long -- -[float] -=== heap - -The Go program heap information exposed by expvar. +*`googlecloud.pubsub.subscription.config_updates_count.value`*:: ++ +-- +Cumulative count of configuration changes for each subscription, grouped by operation type and result. +type: long +-- -*`golang.heap.cmdline`*:: +*`googlecloud.pubsub.subscription.unacked_bytes_by_region.value`*:: + -- -The cmdline of this Go program start with. - +Total byte size of the unacknowledged messages in a subscription, broken down by Cloud region. -type: keyword +type: long -- [float] -=== gc - -Garbage collector summary. - +=== topic +Topic related metrics -[float] -=== total_pause -Total GC pause duration over lifetime of process. +*`googlecloud.pubsub.topic.streaming_pull_response_count.value`*:: ++ +-- +Cumulative count of streaming pull responses, grouped by result. +type: long +-- -*`golang.heap.gc.total_pause.ns`*:: +*`googlecloud.pubsub.topic.send_message_operation_count.value`*:: + -- -Duration in Ns. - +Cumulative count of publish message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. type: long -- -*`golang.heap.gc.total_count`*:: +*`googlecloud.pubsub.topic.send_request_count.value`*:: + -- -Total number of GC was happened. - +Cumulative count of publish requests, grouped by result. type: long -- -*`golang.heap.gc.next_gc_limit`*:: +*`googlecloud.pubsub.topic.oldest_retained_acked_message_age_by_region.value`*:: + -- -Next collection will happen when HeapAlloc > this amount. - +Age (in seconds) of the oldest acknowledged message retained in a topic, broken down by Cloud region. type: long -format: bytes - -- -*`golang.heap.gc.cpu_fraction`*:: +*`googlecloud.pubsub.topic.oldest_unacked_message_age_by_region.value`*:: + -- -Fraction of CPU time used by GC. +Age (in seconds) of the oldest unacknowledged message in a topic, broken down by Cloud region. +type: long -type: float +-- +*`googlecloud.pubsub.topic.retained_acked_bytes_by_region.value`*:: ++ -- +Total byte size of the acknowledged messages retained in a topic, broken down by Cloud region. -[float] -=== pause +type: long -Last GC pause durations during the monitoring period. +-- +*`googlecloud.pubsub.topic.byte_cost.value`*:: ++ +-- +Cost of operations, measured in bytes. This is used to measure utilization for quotas. +type: long -*`golang.heap.gc.pause.count`*:: -+ -- -Count of GC pause duration during this collect period. +*`googlecloud.pubsub.topic.config_updates_count.value`*:: ++ +-- +Cumulative count of configuration changes, grouped by operation type and result. type: long -- -[float] -=== sum - -Total GC pause duration during this collect period. +*`googlecloud.pubsub.topic.message_sizes.value`*:: ++ +-- +Distribution of publish message sizes (in bytes) +type: long +-- -*`golang.heap.gc.pause.sum.ns`*:: +*`googlecloud.pubsub.topic.unacked_bytes_by_region.value`*:: + -- -Duration in Ns. - +Total byte size of the unacknowledged messages in a topic, broken down by Cloud region. type: long -- [float] -=== max - -Max GC pause duration during this collect period. +=== snapshot +Snapshot related metrics -*`golang.heap.gc.pause.max.ns`*:: +*`googlecloud.pubsub.snapshot.oldest_message_age.value`*:: + -- -Duration in Ns. - +Age (in seconds) of the oldest message retained in a snapshot. type: long -- -[float] -=== avg - -Average GC pause duration during this collect period. - - - -*`golang.heap.gc.pause.avg.ns`*:: +*`googlecloud.pubsub.snapshot.oldest_message_age_by_region.value`*:: + -- -Duration in Ns. - +Age (in seconds) of the oldest message retained in a snapshot, broken down by Cloud region. type: long -- -[float] -=== system - -Heap summary,which bytes was obtained from system. +*`googlecloud.pubsub.snapshot.backlog_bytes.value`*:: ++ +-- +Total byte size of the messages retained in a snapshot. +type: long +-- -*`golang.heap.system.total`*:: +*`googlecloud.pubsub.snapshot.backlog_bytes_by_region.value`*:: + -- -Total bytes obtained from system (sum of XxxSys below). - +Total byte size of the messages retained in a snapshot, broken down by Cloud region. type: long -format: bytes - -- -*`golang.heap.system.obtained`*:: +*`googlecloud.pubsub.snapshot.num_messages.value`*:: + -- -Via HeapSys, bytes obtained from system. heap_sys = heap_idle + heap_inuse. - +Number of messages retained in a snapshot. type: long -format: bytes - -- -*`golang.heap.system.stack`*:: +*`googlecloud.pubsub.snapshot.num_messages_by_region.value`*:: + -- -Bytes used by stack allocator, and these bytes was obtained from system. - +Number of messages retained in a snapshot, broken down by Cloud region. type: long -format: bytes - -- -*`golang.heap.system.released`*:: +*`googlecloud.pubsub.snapshot.config_updates_count.value`*:: + -- -Bytes released to the OS. - +Cumulative count of configuration changes, grouped by operation type and result. type: long -format: bytes - -- [float] -=== allocations +=== storage -Heap allocations summary. +Google Cloud Storage metrics -*`golang.heap.allocations.mallocs`*:: +*`googlecloud.storage.api.request_count.value`*:: + -- -Number of mallocs. - +Delta count of API calls, grouped by the API method name and response code. type: long -- -*`golang.heap.allocations.frees`*:: + +*`googlecloud.storage.authz.acl_based_object_access_count.value`*:: + -- -Number of frees. - +Delta count of requests that result in an object being granted access solely due to object ACLs. type: long -- -*`golang.heap.allocations.objects`*:: +*`googlecloud.storage.authz.acl_operations_count.value`*:: + -- -Total number of allocated objects. - +Usage of ACL operations broken down by type. type: long -- -*`golang.heap.allocations.total`*:: +*`googlecloud.storage.authz.object_specific_acl_mutation_count.value`*:: + -- -Bytes allocated (even if freed) throughout the lifetime. - +Delta count of changes made to object specific ACLs. type: long -format: bytes - -- -*`golang.heap.allocations.allocated`*:: + +*`googlecloud.storage.network.received_bytes_count.value`*:: + -- -Bytes allocated and not yet freed (same as Alloc above). - +Delta count of bytes received over the network, grouped by the API method name and response code. type: long -format: bytes - -- -*`golang.heap.allocations.idle`*:: +*`googlecloud.storage.network.sent_bytes_count.value`*:: + -- -Bytes in idle spans. - +Delta count of bytes sent over the network, grouped by the API method name and response code. type: long -format: bytes - -- -*`golang.heap.allocations.active`*:: + +*`googlecloud.storage.storage.object_count.value`*:: + -- -Bytes in non-idle span. - +Total number of objects per bucket, grouped by storage class. This value is measured once per day, and the value is repeated at each sampling interval throughout the day. type: long -format: bytes - -- -[[exported-fields-googlecloud]] -== Google Cloud Platform fields - -GCP module +*`googlecloud.storage.storage.total_byte_seconds.value`*:: ++ +-- +Delta count of bytes received over the network, grouped by the API method name and response code. +type: long +-- -*`googlecloud.labels`*:: +*`googlecloud.storage.storage.total_bytes.value`*:: + -- -type: object +Total size of all objects in the bucket, grouped by storage class. This value is measured once per day, and the value is repeated at each sampling interval throughout the day. + +type: long -- @@ -22322,6 +24622,16 @@ type: date -- +*`kubernetes.event.metadata.generate_name`*:: ++ +-- +Generate name of the event + + +type: keyword + +-- + *`kubernetes.event.metadata.name`*:: + -- @@ -24626,6 +26936,50 @@ type: long Domain name +type: keyword + +-- + +[float] +=== status + +status + + + +[float] +=== stat + +Memory stat + + + +*`kvm.status.stat.state`*:: ++ +-- +domain state + + +type: keyword + +-- + +*`kvm.status.id`*:: ++ +-- +Domain id + + +type: long + +-- + +*`kvm.status.name`*:: ++ +-- +Domain name + + type: keyword -- diff --git a/metricbeat/docs/images/metricbeat-azure-database-account-overview.png b/metricbeat/docs/images/metricbeat-azure-database-account-overview.png new file mode 100644 index 00000000000..040f4d9d8c2 Binary files /dev/null and b/metricbeat/docs/images/metricbeat-azure-database-account-overview.png differ diff --git a/metricbeat/docs/images/metricbeat-iis-application-pool-overview.png b/metricbeat/docs/images/metricbeat-iis-application-pool-overview.png new file mode 100644 index 00000000000..15c43a2190a Binary files /dev/null and b/metricbeat/docs/images/metricbeat-iis-application-pool-overview.png differ diff --git a/metricbeat/docs/images/metricbeat-iis-webserver-overview.png b/metricbeat/docs/images/metricbeat-iis-webserver-overview.png new file mode 100644 index 00000000000..b872c7cf527 Binary files /dev/null and b/metricbeat/docs/images/metricbeat-iis-webserver-overview.png differ diff --git a/metricbeat/docs/images/metricbeat-iis-webserver-process.png b/metricbeat/docs/images/metricbeat-iis-webserver-process.png new file mode 100644 index 00000000000..04449505c26 Binary files /dev/null and b/metricbeat/docs/images/metricbeat-iis-webserver-process.png differ diff --git a/metricbeat/docs/images/metricbeat-iis-website-overview.png b/metricbeat/docs/images/metricbeat-iis-website-overview.png new file mode 100644 index 00000000000..ee86a9be9d8 Binary files /dev/null and b/metricbeat/docs/images/metricbeat-iis-website-overview.png differ diff --git a/metricbeat/docs/kubernetes-default-indexers-matchers.asciidoc b/metricbeat/docs/kubernetes-default-indexers-matchers.asciidoc index 2e1e4dde705..c3bf974d53d 100644 --- a/metricbeat/docs/kubernetes-default-indexers-matchers.asciidoc +++ b/metricbeat/docs/kubernetes-default-indexers-matchers.asciidoc @@ -8,7 +8,7 @@ configuration: [source,yaml] ------------------------------------------------------------------------------- processors: -- add_kubernetes_metadata: - default_indexers.enabled: false - default_matchers.enabled: false + - add_kubernetes_metadata: + default_indexers.enabled: false + default_matchers.enabled: false ------------------------------------------------------------------------------- diff --git a/metricbeat/docs/metricbeat-filtering.asciidoc b/metricbeat/docs/metricbeat-filtering.asciidoc index c7ea4c353c5..096948238ea 100644 --- a/metricbeat/docs/metricbeat-filtering.asciidoc +++ b/metricbeat/docs/metricbeat-filtering.asciidoc @@ -13,8 +13,8 @@ dropping the `agent.name` and `agent.version` fields under `beat` from all docum [source, yaml] ---- processors: - - drop_fields: - fields: ['agent'] + - drop_fields: + fields: ['agent'] ---- include::{libbeat-dir}/processors-using.asciidoc[] diff --git a/metricbeat/docs/modules/googlecloud.asciidoc b/metricbeat/docs/modules/googlecloud.asciidoc index 19a741eb5d0..3dcf9b20db9 100644 --- a/metricbeat/docs/modules/googlecloud.asciidoc +++ b/metricbeat/docs/modules/googlecloud.asciidoc @@ -16,18 +16,88 @@ Note: extra GCP charges on Stackdriver Monitoring API requests will be generated == Module config and parameters This is a list of the possible module parameters you can tune: -* *zone*: A single string with the zone you want to monitor like "us-central1-a". If you need to fetch from multiple regions, you have to setup a different configuration for each (but you don't need a new instance of Metricbeat running) -* *region*: A single string with the region you want to monitor like "us-central1". This will enable monitoring for all zones under this region. +* *zone*: A single string with the zone you want to monitor like `us-central1-a`. +Or you can specific a partial zone name like `us-central1-` or `us-central1-*`, +which will monitor all zones start with `us-central1-`: `us-central1-a`, +`us-central1-b`, `us-central1-c` and `us-central1-f`. +Please see https://cloud.google.com/compute/docs/regions-zones#available[GCP zones] +for zones that are available in GCP. + +* *region*: A single string with the region you want to monitor like `us-central1`. +This will enable monitoring for all zones under this region. Or you can specific +a partial region name like `us-east` or `us-east*`, which will monitor all regions start with +`us-east`: `us-east1` and `us-east4`. If both region and zone are configured, +only region will be used. +Please see https://cloud.google.com/compute/docs/regions-zones#available[GCP regions] +for regions that are available in GCP. + * *project_id*: A single string with your GCP Project ID -* *credentials_file_path*: A single string pointing to the JSON file path reachable by Metricbeat that you have created using IAM. -* *exclude_labels*: (`true`/`false` default `false`) Do not extract extra labels and metadata information from Metricsets and fetch metrics onlly. At the moment, *labels and metadata extraction is only supported* in Compute Metricset. + +* *credentials_file_path*: A single string pointing to the JSON file path +reachable by Metricbeat that you have created using IAM. + +* *exclude_labels*: (`true`/`false` default `false`) Do not extract extra labels +and metadata information from metricsets and fetch metrics only. At the moment, +*labels and metadata extraction is only supported* in `compute` metricset. + +* *period*: A single time duration specified for this module collection frequency. + +[float] +== Example configuration +* `compute` metricset is enabled to collect metrics from `us-central1-a` zone +in `elastic-observability` project. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - compute + zone: "us-central1-a" + project_id: "elastic-observability" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s +---- + +* `compute` and `pubsub` metricsets are enabled to collect metrics from all zones +under `us-central1` region in `elastic-observability` project. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - compute + - pubsub + region: "us-central1" + project_id: "elastic-observability" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s +---- + +* `compute` metricset is enabled to collect metrics from all regions starts with +`us-west` in `elastic-observability` project, which includes all zones under +`us-west1`, `us-west2`, `us-west3` and `us-west4`. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - compute + - pubsub + region: "us-west" + project_id: "elastic-observability" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s +---- [float] == Authentication, authorization and permissions. Authentication and authorization in Google Cloud Platform can be achieved in many ways. For the current version of the Google Cloud Platform module for Metricbeat, the only supported method is using Service Account JSON files. A typical JSON with a private key looks like this: [float] -==== Example Credentials +=== Example Credentials [source,json] ---- { @@ -62,7 +132,9 @@ Google Cloud Platform offers the https://cloud.google.com/monitoring/api/metrics If you also want to *extract service labels* (by setting `exclude_labels` to false, which is the default state). You also make a new API check on the corresponding service. Service labels requires a new API call to extract those metrics. In the worst case the number of API calls will be doubled. In the best case, all metrics come from the same GCP entity and 100% of the required information is included in the first API call (which is cached for subsequent calls). -A recommended `period` value between fetches is between 5 and 10 minutes, depending on how granular you want your metrics. GCP restricts information for less than 5 minutes. +If `period` value is set to 5-minute and sample period of the metric type is 60-second, then this module will collect data from this metric type once every 5 minutes with aggregation. +GCP monitoring data has a up to 240 seconds latency, which means latest monitoring data will be up to 4 minutes old. Please see https://cloud.google.com/monitoring/api/v3/latency-n-retention[Latency of GCP Monitoring Metric Data] for more details. +In googlecloud module, metrics are collected based on this ingest delay, which is also obtained from ListMetricDescriptors API. [float] === Rough estimation of the number of API Calls @@ -101,13 +173,21 @@ metricbeat.modules: - module: googlecloud metricsets: - compute + region: "us-central1" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 300s + +- module: googlecloud + metricsets: - pubsub - loadbalancing zone: "us-central1-a" project_id: "your project id" credentials_file_path: "your JSON credentials file path" exclude_labels: false - period: 300s + period: 60s - module: googlecloud metricsets: @@ -117,6 +197,15 @@ metricbeat.modules: credentials_file_path: "your JSON credentials file path" exclude_labels: false period: 300s + +- module: googlecloud + metricsets: + - compute + region: "us-" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s ---- [float] @@ -130,6 +219,8 @@ The following metricsets are available: * <> +* <> + * <> include::googlecloud/compute.asciidoc[] @@ -138,5 +229,7 @@ include::googlecloud/loadbalancing.asciidoc[] include::googlecloud/pubsub.asciidoc[] +include::googlecloud/stackdriver.asciidoc[] + include::googlecloud/storage.asciidoc[] diff --git a/metricbeat/docs/modules/googlecloud/stackdriver.asciidoc b/metricbeat/docs/modules/googlecloud/stackdriver.asciidoc new file mode 100644 index 00000000000..16609f7b01e --- /dev/null +++ b/metricbeat/docs/modules/googlecloud/stackdriver.asciidoc @@ -0,0 +1,23 @@ +//// +This file is generated! See scripts/mage/docs_collector.go +//// + +[[metricbeat-metricset-googlecloud-stackdriver]] +=== Google Cloud Platform stackdriver metricset + +beta[] + +include::../../../../x-pack/metricbeat/module/googlecloud/stackdriver/_meta/docs.asciidoc[] + + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../../x-pack/metricbeat/module/googlecloud/stackdriver/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules/kvm.asciidoc b/metricbeat/docs/modules/kvm.asciidoc index f8373184ba1..bc29173f280 100644 --- a/metricbeat/docs/modules/kvm.asciidoc +++ b/metricbeat/docs/modules/kvm.asciidoc @@ -20,7 +20,7 @@ in <>. Here is an example configuration: ---- metricbeat.modules: - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] @@ -39,5 +39,9 @@ The following metricsets are available: * <> +* <> + include::kvm/dommemstat.asciidoc[] +include::kvm/status.asciidoc[] + diff --git a/metricbeat/docs/modules/kvm/status.asciidoc b/metricbeat/docs/modules/kvm/status.asciidoc new file mode 100644 index 00000000000..5fea3653349 --- /dev/null +++ b/metricbeat/docs/modules/kvm/status.asciidoc @@ -0,0 +1,24 @@ +//// +This file is generated! See scripts/mage/docs_collector.go +//// + +[[metricbeat-metricset-kvm-status]] +=== kvm status metricset + +beta[] + +include::../../../module/kvm/status/_meta/docs.asciidoc[] + +This is a default metricset. If the host module is unconfigured, this metricset is enabled by default. + +==== Fields + +For a description of each field in the metricset, see the +<> section. + +Here is an example document generated by this metricset: + +[source,json] +---- +include::../../../module/kvm/status/_meta/data.json[] +---- diff --git a/metricbeat/docs/modules/mssql.asciidoc b/metricbeat/docs/modules/mssql.asciidoc index 15ad0f80113..a8a5aa2ba28 100644 --- a/metricbeat/docs/modules/mssql.asciidoc +++ b/metricbeat/docs/modules/mssql.asciidoc @@ -8,7 +8,7 @@ This file is generated! See scripts/mage/docs_collector.go beta[] -This is the https://www.microsoft.com/en-us/sql-server/sql-server-2017[Microsoft SQL 2017] Metricbeat module. It is still in beta and under active development to add new Metricsets and introduce enhancements. +This is the https://www.microsoft.com/en-us/sql-server/sql-server-2017[Microsoft SQL 2017] Metricbeat module. It is still under active development to add new Metricsets and introduce enhancements. [float] === Compatibility @@ -33,7 +33,7 @@ The following Metricsets are already included: [float] === Module-specific configuration notes -When configuring the `hosts` option, you can specify native user credentials +When configuring the `hosts` option, you can specify native user credentials as part of the host string with the following format: ---- @@ -57,6 +57,7 @@ metricbeat.modules: Store sensitive values like passwords in the <>. + [float] === Example configuration diff --git a/metricbeat/docs/modules/mssql/performance.asciidoc b/metricbeat/docs/modules/mssql/performance.asciidoc index 214ae0455fa..b21411b5a60 100644 --- a/metricbeat/docs/modules/mssql/performance.asciidoc +++ b/metricbeat/docs/modules/mssql/performance.asciidoc @@ -5,8 +5,6 @@ This file is generated! See scripts/mage/docs_collector.go [[metricbeat-metricset-mssql-performance]] === MSSQL performance metricset -beta[] - include::../../../../x-pack/metricbeat/module/mssql/performance/_meta/docs.asciidoc[] This is a default metricset. If the host module is unconfigured, this metricset is enabled by default. diff --git a/metricbeat/docs/modules/mssql/transaction_log.asciidoc b/metricbeat/docs/modules/mssql/transaction_log.asciidoc index cf0bb4f35eb..63bf00583c4 100644 --- a/metricbeat/docs/modules/mssql/transaction_log.asciidoc +++ b/metricbeat/docs/modules/mssql/transaction_log.asciidoc @@ -5,8 +5,6 @@ This file is generated! See scripts/mage/docs_collector.go [[metricbeat-metricset-mssql-transaction_log]] === MSSQL transaction_log metricset -beta[] - include::../../../../x-pack/metricbeat/module/mssql/transaction_log/_meta/docs.asciidoc[] This is a default metricset. If the host module is unconfigured, this metricset is enabled by default. diff --git a/metricbeat/docs/modules/oracle.asciidoc b/metricbeat/docs/modules/oracle.asciidoc index d56abf8648b..2492adcaaaf 100644 --- a/metricbeat/docs/modules/oracle.asciidoc +++ b/metricbeat/docs/modules/oracle.asciidoc @@ -57,7 +57,7 @@ metricbeat.modules: metricsets: ["tablespace", "performance"] enabled: true period: 10s - hosts: ["oracle://user:pass@localhost:1521/ORCLPDB1.localdomain?sysdba=1"] + hosts: ["user:pass@0.0.0.0:1521/ORCLPDB1.localdomain"] # username: "" # password: "" diff --git a/metricbeat/docs/modules/windows/perfmon.asciidoc b/metricbeat/docs/modules/windows/perfmon.asciidoc index f3f53901205..20df688c5eb 100644 --- a/metricbeat/docs/modules/windows/perfmon.asciidoc +++ b/metricbeat/docs/modules/windows/perfmon.asciidoc @@ -5,8 +5,6 @@ This file is generated! See scripts/mage/docs_collector.go [[metricbeat-metricset-windows-perfmon]] === Windows perfmon metricset -beta[] - include::../../../module/windows/perfmon/_meta/docs.asciidoc[] diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index b324627d173..73af89aca05 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -109,9 +109,10 @@ This file is generated! See scripts/mage/docs_collector.go .2+| .2+| |<> |<> |<> beta[] |image:./images/icon-yes.png[Prebuilt dashboards are available] | -.4+| .4+| |<> beta[] +.5+| .5+| |<> beta[] |<> beta[] |<> beta[] +|<> beta[] |<> beta[] |<> |image:./images/icon-no.png[No prebuilt dashboards] | .1+| .1+| |<> @@ -123,7 +124,7 @@ This file is generated! See scripts/mage/docs_collector.go |<> |<> beta[] |image:./images/icon-yes.png[Prebuilt dashboards are available] | .1+| .1+| |<> beta[] -|<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | +|<> beta[] |image:./images/icon-yes.png[Prebuilt dashboards are available] | .3+| .3+| |<> beta[] |<> beta[] |<> beta[] @@ -167,7 +168,8 @@ This file is generated! See scripts/mage/docs_collector.go |<> |<> |<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | -.1+| .1+| |<> beta[] +.2+| .2+| |<> beta[] +|<> beta[] |<> |image:./images/icon-no.png[No prebuilt dashboards] | .2+| .2+| |<> |<> @@ -180,8 +182,8 @@ This file is generated! See scripts/mage/docs_collector.go |<> |<> |<> beta[] |image:./images/icon-yes.png[Prebuilt dashboards are available] | -.2+| .2+| |<> beta[] -|<> beta[] +.2+| .2+| |<> +|<> |<> |image:./images/icon-no.png[No prebuilt dashboards] | .1+| .1+| |<> |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | @@ -264,7 +266,7 @@ This file is generated! See scripts/mage/docs_collector.go |<> |<> |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | -.2+| .2+| |<> beta[] +.2+| .2+| |<> |<> |<> |image:./images/icon-yes.png[Prebuilt dashboards are available] | .3+| .3+| |<> diff --git a/metricbeat/docs/running-on-cloudfoundry.asciidoc b/metricbeat/docs/running-on-cloudfoundry.asciidoc index e6c25d02587..2988e4d3a8b 100644 --- a/metricbeat/docs/running-on-cloudfoundry.asciidoc +++ b/metricbeat/docs/running-on-cloudfoundry.asciidoc @@ -1,5 +1,5 @@ [[running-on-cloudfoundry]] -=== Running {beatname_uc} on Cloud Foundry +=== Run {beatname_uc} on Cloud Foundry You can use {beatname_uc} on Cloud Foundry to retrieve and ship metrics. @@ -14,18 +14,19 @@ endif::[] [float] ==== Cloud Foundry credentials -{beatname_uc} needs credentials created with UAA so it can connect to loggregator to receive the logs. The uaac +{beatname_uc} needs credentials created with UAA so it can connect to loggregator to receive the logs. The `uaac` command will create the required credentials for connecting to loggregator. -["source", "sh"] +["source","sh",subs="attributes"] ------------------------------------------------ uaac client add {beatname_lc} --name {beatname_lc} --secret changeme --authorized_grant_types client_credentials,refresh_token --authorities doppler.firehose,cloud_controller.admin_read_only ------------------------------------------------ [WARNING] ======================================= -*Use a unique secret:* The uaac command above is just an example and the secret should be changed and the -`{beatname_lc}.yml` should be updated with your choosen secret. +*Use a unique secret:* The `uaac` command shown here is an example. Remember to +replace `changeme` with your secret, and update the +{beatname_lc}.yml+ file to +use your chosen secret. ======================================= diff --git a/metricbeat/docs/running-on-kubernetes.asciidoc b/metricbeat/docs/running-on-kubernetes.asciidoc index dfa6cbb25d4..7267c0f9872 100644 --- a/metricbeat/docs/running-on-kubernetes.asciidoc +++ b/metricbeat/docs/running-on-kubernetes.asciidoc @@ -1,5 +1,5 @@ [[running-on-kubernetes]] -=== Running Metricbeat on Kubernetes +=== Run Metricbeat on Kubernetes You can use {beatname_uc} <> on Kubernetes to retrieve cluster metrics. @@ -85,6 +85,15 @@ spec: If you are using Red Hat OpenShift, you need to specify additional settings in the manifest file and enable the container to run as privileged. +. Modify the `DaemonSet` container spec in the manifest file: ++ +[source,yaml] +----- + securityContext: + runAsUser: 0 + privileged: true +----- + . In the manifest file, edit the `metricbeat-daemonset-modules` ConfigMap, and specify the following settings under `kubernetes.yml` in the `data` section: + @@ -103,7 +112,26 @@ specify the following settings under `kubernetes.yml` in the `data` section: hosts: ["https://${NODE_NAME}:10250"] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token ssl.certificate_authorities: - - /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt + - /path/to/kubelet-service-ca.crt +----- +NOTE: `kubelet-service-ca.crt` can be any CA bundle that contains the issuer of the certificate used in the Kubelet API. +According to each specific installation of Openshift this can be found either in `secrets` or in `configmaps`. +In some installations it can be available as part of the service account secret, in +`/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt`. +In case of using Openshift installer[https://github.com/openshift/installer/blob/master/docs/user/gcp/install.md] +for GCP then the following `configmap` can be mounted in Metricbeat Pod and use `ca-bundle.crt` +in `ssl.certificate_authorities`: ++ +[source,shell] +----- +Name: kubelet-serving-ca +Namespace: openshift-kube-apiserver +Labels: +Annotations: + +Data +==== +ca-bundle.crt: ----- . Under the `metricbeat` ClusterRole, add the following resources: diff --git a/metricbeat/include/list_common.go b/metricbeat/include/list_common.go index f15d6a4be16..ced01fa0d57 100644 --- a/metricbeat/include/list_common.go +++ b/metricbeat/include/list_common.go @@ -92,6 +92,7 @@ import ( _ "github.com/elastic/beats/v7/metricbeat/module/kibana/status" _ "github.com/elastic/beats/v7/metricbeat/module/kvm" _ "github.com/elastic/beats/v7/metricbeat/module/kvm/dommemstat" + _ "github.com/elastic/beats/v7/metricbeat/module/kvm/status" _ "github.com/elastic/beats/v7/metricbeat/module/logstash" _ "github.com/elastic/beats/v7/metricbeat/module/logstash/node" _ "github.com/elastic/beats/v7/metricbeat/module/logstash/node_stats" diff --git a/metricbeat/magefile.go b/metricbeat/magefile.go index 753e5e5a518..6e78e1559b9 100644 --- a/metricbeat/magefile.go +++ b/metricbeat/magefile.go @@ -31,6 +31,9 @@ import ( devtools "github.com/elastic/beats/v7/dev-tools/mage" metricbeat "github.com/elastic/beats/v7/metricbeat/scripts/mage" + // register kubernetes runner + _ "github.com/elastic/beats/v7/dev-tools/mage/kubernetes" + // mage:import "github.com/elastic/beats/v7/dev-tools/mage/target/build" // mage:import @@ -46,14 +49,14 @@ import ( // mage:import "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" // mage:import - "github.com/elastic/beats/v7/dev-tools/mage/target/update" - // mage:import _ "github.com/elastic/beats/v7/dev-tools/mage/target/compose" + // mage:import + _ "github.com/elastic/beats/v7/metricbeat/scripts/mage/target/metricset" ) func init() { - common.RegisterCheckDeps(update.Update) - test.RegisterDeps(GoIntegTest, PythonIntegTest) + common.RegisterCheckDeps(Update) + test.RegisterDeps(IntegTest) unittest.RegisterGoTestDeps(Fields) unittest.RegisterPythonTestDeps(Fields) @@ -75,8 +78,9 @@ func Package() { devtools.UseElasticBeatOSSPackaging() metricbeat.CustomizePackaging() + devtools.PackageKibanaDashboardsFromBuildDir() - mg.Deps(update.Update, metricbeat.PrepareModulePackagingOSS) + mg.Deps(Update) mg.Deps(build.CrossBuild, build.CrossBuildGoDaemon) mg.SerialDeps(devtools.Package, TestPackages) } @@ -102,12 +106,6 @@ func Config() { mg.Deps(configYML, metricbeat.GenerateDirModulesD) } -// Imports generates an include/list_{suffix}.go file containing -// a import statement for each module and dataset. -func Imports() error { - return metricbeat.GenerateOSSMetricbeatModuleIncludeListGo() -} - func configYML() error { return devtools.Config(devtools.AllConfigTypes, metricbeat.OSSConfigFileParams(), ".") } @@ -133,11 +131,27 @@ func MockedTests(ctx context.Context) error { return devtools.GoTest(ctx, params) } -// Fields generates a fields.yml for the Beat. -func Fields() error { +// Fields generates a fields.yml and fields.go for each module. +func Fields() { + mg.Deps(fieldsYML, moduleFieldsGo) +} + +func fieldsYML() error { return devtools.GenerateFieldsYAML("module") } +func moduleFieldsGo() error { + return devtools.GenerateModuleFieldsGo("module") +} + +// Update is an alias for running fields, dashboards, config. +func Update() { + mg.SerialDeps( + Fields, Dashboards, Config, CollectAll, + metricbeat.PrepareModulePackagingOSS, + metricbeat.GenerateOSSMetricbeatModuleIncludeListGo) +} + // FieldsDocs generates docs/fields.asciidoc containing all fields // (including x-pack). func FieldsDocs() error { @@ -168,7 +182,9 @@ func IntegTest() { // Use TEST_TAGS=tag1,tag2 to add additional build tags. // Use MODULE=module to run only tests for `module`. func GoIntegTest(ctx context.Context) error { - mg.Deps(Fields) + if !devtools.IsInIntegTestEnv() { + mg.SerialDeps(Fields, Dashboards) + } return devtools.GoTestIntegrationForModule(ctx) } @@ -181,8 +197,12 @@ func PythonIntegTest(ctx context.Context) error { if !devtools.IsInIntegTestEnv() { mg.SerialDeps(Fields, Dashboards) } - return devtools.RunIntegTest("pythonIntegTest", func() error { + runner, err := devtools.NewDockerIntegrationRunner(devtools.ListMatchingEnvVars("NOSE_")...) + if err != nil { + return err + } + return runner.Test("pythonIntegTest", func() error { mg.Deps(devtools.BuildSystemTestBinary) return devtools.PythonNoseTest(devtools.DefaultPythonTestIntegrationArgs()) - }, devtools.ListMatchingEnvVars("NOSE_")...) + }) } diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 6de1e04436a..ee6266559f0 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -53,6 +53,7 @@ metricbeat.max_start_delay: 10s #timeseries.enabled: false + #========================== Modules configuration ============================= metricbeat.modules: @@ -556,7 +557,7 @@ metricbeat.modules: #--------------------------------- Kvm Module --------------------------------- - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] @@ -871,7 +872,8 @@ metricbeat.modules: -#================================ General ====================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -979,7 +981,7 @@ metricbeat.modules: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -996,153 +998,153 @@ metricbeat.modules: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Metricbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -1155,11 +1157,11 @@ metricbeat.modules: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -1279,7 +1281,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -1393,7 +1416,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -1547,6 +1570,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -1569,7 +1595,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -1687,7 +1713,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -1721,7 +1747,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -1734,7 +1760,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Metricbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -1760,11 +1786,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -1808,8 +1836,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1863,7 +1890,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1877,13 +1904,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'metricbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "metricbeat" +#setup.ilm.rollover_alias: 'metricbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'metricbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1898,7 +1925,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1953,9 +1980,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -2022,8 +2048,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Metricbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -2133,6 +2158,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -2144,7 +2190,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -2168,12 +2215,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/metricbeat/metricbeat.yml b/metricbeat/metricbeat.yml index 5bd19f3030c..96975ee6027 100644 --- a/metricbeat/metricbeat.yml +++ b/metricbeat/metricbeat.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/metricbeat/index.html -#========================== Modules configuration ============================ +# =========================== Modules configuration ============================ metricbeat.config.modules: # Glob pattern for configuration loading @@ -19,14 +19,15 @@ metricbeat.config.modules: # Period on which files under path should be checked for changes #reload.period: 10s -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 index.codec: best_compression #_source.enabled: false -#================================ General ===================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -41,8 +42,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -54,7 +54,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -71,7 +71,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Metricbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -84,11 +84,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -101,7 +101,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -116,7 +116,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -126,7 +126,8 @@ processors: - add_docker_metadata: ~ - add_kubernetes_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -137,8 +138,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# metricbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Metricbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -159,7 +160,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/metricbeat/module/aerospike/fields.go b/metricbeat/module/aerospike/fields.go index 4e03f23d432..0a063636e18 100644 --- a/metricbeat/module/aerospike/fields.go +++ b/metricbeat/module/aerospike/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetAerospike returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/aerospike. +// This is the base64 encoded gzipped contents of module/aerospike. func AssetAerospike() string { return "eJzUmMGO4zYMhu95CmIve2nyADkUWLSXHnZRFL0VxYCR6FgdWTQkalIDffhCdjzj2HKSycxuPDrkYEvk/8mkRGYNj9RsAclzqM0jrQDEiKUtfPrSP/u0AtAUlDe1GHZb+HkFAPD8HirW0aalnixhoC3scQVQGLI6bNvJa3BY0amjNKSp03TPsT4+yXg6NTU0l35DjYqe3+RMzprtRs7IKUk/xjKGUpQ15OTk1ZyWC3rS+KW1BkFQwmb0NqdiqESTJaHJ63NqrlA0UNU5APHoAqq0IOSVnlM7VEzes8/O6EVbdvuZCVfoTuNbrHbkgYvjl8pSSIkCBRpLGg5GSkDXicuRDcKQ5aHg6PRiGDyFaIU0GAeY9EGr7zxGiEpRCD8M4uiviPYMz3nJYiriOE677yf50r4nPRo4ylR2L9kT5uLkvZIzmf/oqTlm6FRcCIW7JeFE7SQDP0z+TYJnkdmX3/ArUu/gzXe9GFv7Hz35MhA3Xov3j+cpyyIDem7LZ0L6pdB7MmoczzeXnL+a8AhB2OOeurB9Zd2JT2gs7ixtapXbwk5ZUGhJPxSWMTepYF+hbKEmr6b19BUYaXwlDNFT2kWCyjhTxQoUOzH7yDGAblFTyQ+oPIcAaG37NBwLpr4lmD9LCk/3B/29W5y+GBcdlsIalZGmFZi8gJQmXEMkLGg3u0Yol7Jng7+HmVt8BcqfyXtn4Jnl+ImsZYXpKhUewUB7+KcnrM+QxUB6gWBJFuya65l6nvJQPew8oSppXO10NDtmS+hedwD8VoD4SD8NOvsSA/SO4HNp9uX6gEJ+/VfC+K+iin3z97pW8vliqPXiu0XvdWp9ba2dnluvbZgXmMrdJl1M5tfEv0bBuyXBl4qjkwEZKxVr08V/UvZGOOM0/btMunQHtfLeiBgWzBhIsdPom46UwhtZF3UVHXlvPq3Tb/a4e6TmwH58gl8Q+O3Z88TuSyesaVNyyP8beZtX1gQTkycO3xkzOZwl5N0/pCYF6s2XyB+k2OuZZu3S7VFhEMp1aWfD9IowfGkT/FHfMNbgUBpVAvpUw4p5oqOQTIdzUuD9EKHGjdPk5TyYzZMgXD+0rVD+w76lqBnLMQFU9J6c2Kb9eyYVmQfj9l0vFjar/wMAAP//lvJazw==" } diff --git a/metricbeat/module/apache/fields.go b/metricbeat/module/apache/fields.go index 619e56f1a83..332b5f2c1a0 100644 --- a/metricbeat/module/apache/fields.go +++ b/metricbeat/module/apache/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetApache returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/apache. +// This is the base64 encoded gzipped contents of module/apache. func AssetApache() string { return "eJzMl8GO2zYQhu9+ioHOjZE95OJDgTQB2qKLNtjdoIeiUGhqJBOmOSyHWsNvX5CUVa1XUmzXDKLTwlr9/zfD4Qz5BrZ4WIGwQm5wAeCV17iC4n38oVgAVMjSKesVmRX8uAAASC/hl6enTx+B0T2jgx16pySjZ5CkNUqPFdSOduA3ePxij+vu/5cLAN6Q86UkU6tmBbXQHAgcahSMK2hE+B/0XpmGV/BXwayLH6DYeG+LvxcAtUJd8SoivQEjdjgIJDz+YIOOo9Z2v4wEE54v6bMvIMl4oQxH6C4k8BvhYY8OgaUT9hhXimnZiQxhhkDshW+5/3kMKjwvoz4+E7gROQlfhjxYivQ5WNH0IYyFMQxlQ+zDXy9eHgPa4mFPrjp5NxPAoJCOwstRW09e6FJIiczIo+aaTHOZ81MQBdPu1uiAakjy4PCfFtnzHMl2ffDZOLZKU9RP+6QaBzlilhZdyShHYVgKjVVZaxL+MqiHTh4sOmCUZCY4ImkmiJ9iFs4n6HKSjaLTH8fYk9ui4+W65cONKuP3viaCaO8wa68qPb47/499EJ23b61XE23htM+d4fw5qsUGdeI33p6GJGm4lKNAs9k4gys8j2nYJX1QpqvNV5xfyc3NUcbXRdr2Vovy4dPn61ZEkzgdCnDOvjwzC4HrnkQ1swCMLjNAsIiBTlPwgT3uMnI8RoOw5NMQcqN05dCU3yAnVPd2MT9nUGXP0SlXMpzYO2QMyqA2Puqv2UO95HVbKR4/MnSSdAYZBDy9VoIPRi73ToVDeQaU90F/gAKd1deItoi2FFo95+i0CSpYYAXR5JJkSU2cNVmaGKtpoplGfHUlh44L4hmdaPDiMi7uimv3eHitTFPWQnpyK7h7+/a61A0DgJpcvBlpwR52yrQepxe1ePc907/r+Geqsrj7riO4mwihH6SSHK5JvLpqXl3Mj73i8fp88anTCxe6VPnKGm5x0OvUobXTq+pQVIFg/BZ0C4yH5DBxRX6RDjQdi9Wnt6GbJCTpw0PQn8YIDTvXSPgN0aZRMO1fGS410TZLUXw0DPdRfGYhutFT/jcbMpB8SCbnjURNTZNnGN5PKB+dGyck1q3Wh7JWRvEmD8bPvQ30NtPpCFfrUmoUJkuR/Bpu7p38zKKQDYdvTTm6xh82nLM1zfWLvYhnvLIml7dU/0xGceCcVa5ZT9zLxb8BAAD//2GFvEU=" } diff --git a/metricbeat/module/beat/fields.go b/metricbeat/module/beat/fields.go index 33bbffd7dc0..58ff1927451 100644 --- a/metricbeat/module/beat/fields.go +++ b/metricbeat/module/beat/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetBeat returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/beat. +// This is the base64 encoded gzipped contents of module/beat. func AssetBeat() string { return "eJzcl0+L2zwQxu/5FIPP7+YD+PAWSlvYSwtloYdSimxPvGJljSuNdvG3L5LXiWPJdtJmE6hOiRU/z0/zR1Lu4Am7HAoUvAFgyQpzyN6j4GwDUKEtjWxZks7h/w0AgJ+ChiqncANgUKGwmEMtNgAWmaWubQ7fM2tV9h9kj8xt9sPPPZLhnyXpnaxz2All/fs7iaqyeVC+Ay0a3LP4wV3rtQ259vVJguhYZawkq/2jQesJuxcy4+dJxX6Etd5/2EbCXusC0v69WNyy4Fh9HIRTtKcqx5kaxjRyY45GaFFjg5q3qEWhsDr62QBWECkUejK3gOfHvYUSNRuhRjbwavMujRNqbluS05wEkZqxRnMeyGfXFGiAdq/6FlJrHRjIcet4678kEeISOAVBNOgBenFwFisoupDFJMQvhw7fiCFoQ4FS12mQcZHaixSp/asidS3LBreNTcZCka7PC0TA6kWTfsbpYFiTIcdS46WMD4V4kPZu2qdC6vlyULIopnNz+TiB41MINpTUNKSBCYRSwXy60FRWIOqVaHoJ7QQ8P770jTItnjUwmN/AY7x0F50B6cdD1446exEIn1EnVwOrETsD6KN3gbCFoplzW4rfGFmUT9GREFMnOuEPwOGoQ/pgeQBNLwqreoHjQMvyeS7nb4vbWy+neHT9EVw+RtvKdUgD4irBQFoZatvblsAawh7VtUqWgm8ZV3ugWAfeCRlfuK4Ku0Kw30mJGqG7m5Aykb8+dqe2FhMLdUXSB+8HehLZxXPAoJiL+cVOga8oqtnTE844AYruVg0VnJeCNTpZjaHZs+5tKT3fkv+A+GIkL19HLpD1b97kn0i7Dxfj9B9nzHnDzIeMTgGWPvwOAAD//2j1+Zk=" } diff --git a/metricbeat/module/ceph/fields.go b/metricbeat/module/ceph/fields.go index f401f784976..a2623b56a06 100644 --- a/metricbeat/module/ceph/fields.go +++ b/metricbeat/module/ceph/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetCeph returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/ceph. +// This is the base64 encoded gzipped contents of module/ceph. func AssetCeph() string { return "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" } diff --git a/metricbeat/module/consul/fields.go b/metricbeat/module/consul/fields.go index 3e396c8a0b3..9890d32f00e 100644 --- a/metricbeat/module/consul/fields.go +++ b/metricbeat/module/consul/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetConsul returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/consul. +// This is the base64 encoded gzipped contents of module/consul. func AssetConsul() string { return "eJzcVsFu2zAMvecriN7TD8hhwLbDdlkLdL0XtMzY2mTRIKlu/vtBdtzYiZ2m2IAN0yWBJD6+90gT2sJ36nbgOGoKGwDzFmgHN8PGzQagJHXiW/Mcd/BuAwDwsT+EhssUaAMgFAiVdlCQ4QZg7ymUuuvvbiFiQ5MMeVnX0g4q4dQedqYh0zCsKNrL7lJkXqcExrXAfVzvMzB8IRPvlAz2ZK4mhWbYAR/3LA3mUNgLN4CjbB/VMDoCSTH6WAHqgDbBP5Uzk5SMWx/YZqdr0tbQpog1YbC6OzsfMQvmQBgXzmcG3T+TYAgHOOA9WE0Q2GEAJXkmAReSGsmiLknRfENXq5rlfhiCcyXRqBzL8EYjtDuNuEzhEuYUt+iMlpCP6IFjtXJhpvMuNQVJ9rbHzH8aalg64MLQRyqHbsvO33+9XVXaYAjsnhyneNpHr5KaEfpM2AIX38iZQg+a/b/Uau3T4fpv5b0/TQkce9U5A2AswSsgVBRzU44mtUKqSQh8LL1DY7mFx9orNNhBkUTtYF/uJePht0gGWnMKJQhZkphPENQIyw7U0AieMSRat7ti4WQ+LnbB9aIfDhPjCHeuNDCWf01nX41//BP60H84x74pur5vDtO5FXakeqGUKAVW9OQ4BHLG8ma5MzafBjh4gVsZXdf6JCn+GZvOiRkbBqCf5FK+spxm5NFi0tNZfp09Z0we+yat0fo6VWe8atQhXdlfwHYN9ZJ/U+4uidDiXLxewTX5pjlXqzZPeaF643qlir1TwwfvI0SMrOQ4luv5R4p9/f8HU+6Oood3ZTOMATVut1bT9gdLKE87Lb/kevMU1Of322FiqKEYlbebXwEAAP//P1vjFg==" } diff --git a/metricbeat/module/couchbase/fields.go b/metricbeat/module/couchbase/fields.go index 5168ad70677..8ddf5d937fd 100644 --- a/metricbeat/module/couchbase/fields.go +++ b/metricbeat/module/couchbase/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetCouchbase returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/couchbase. +// This is the base64 encoded gzipped contents of module/couchbase. func AssetCouchbase() string { return "eJzMmVFv28YPwN/zKYgAfyAF/tXe/TCg6DB0DymKLnsaBpW+o61bJJ12pOK6n37gnWTLruXYiWpPD3mwFPLHI4/k8d7CI61nYHxrijky3QCIk5JmcPu+/+32BsASm+Aacb6ewc83AAD3JMEZBuPLkoyQhUXwFWz+DZjCEwXObgC48EFy4+uFW85ggWXUFKgkZJrBEvUbEnH1kmfw5y1zeft/uC1Emtu/bgAWjkrLs6j3LdRY0S6zPrJuVFTwbdP9cgBany+b//wCxteCrmaQgqDqLJICBVYUCNgEbL6zLOtEDamGZPPWPJJsfj6EdgRPn+0iJlk9Wjb4anf1+mefacilf3de9GSPtF75YPfeHeHT5yNWBH4RVy5RZge1qorptD6sm5O0WhTMWiabzddCvCdn4UOFMoND7xJc6evleWS/u2+RrGUKUT2snBSu7jC5p+aGjFs4ssCCQinaMBAEYmepFnA1fH53P2KX48dsQWKKycA/ttWcguKpcOiEH1F/0WV9V/m2lg2d6oa7qODNYcSKKh/W14JM2hPmfD0I0+PQ/7ReMAtYXQH587v7F/K2TFljZIS2oWCo3n+beNlgSTZflB73P3iG+1OSikvaYb9b+ABoxD0R+PnfZITfAC41t0u0KhWfNqidyUDWDXv3vxELfcN5QyFnMpPvM99QQP2MdZGAyfjaHsZwQlVu1FWTU6hoBmT2xqHWb81XBxPrpuSWLQuFaSpbJ2ya0lZYmy0C0cW2z6+BCAoMFmzQmOMGDWnmjrHWmXZ0Cyly2kbiBcuLkX/YQkf1ENWrqvPYL0v9ECG/W3B8QlfivCQQfx5+rA9PWLaXi5kP+/TDrHse+Hydxw7nP4Eem51zQr/Cr3nKMZOmtnv82uf2KLY/UhwrYa629DXveoZqPhVJ6gLS/uooNjvsN1UZT0fOENzd60odLbMXpds48BiXNirD3HXZfaRF/8Wpax9da3zt7TXod7Z/G4L2/opy1l7a2nOFjDZiywu4L+8GrF7mhlFrrlEQ1QE7NfDktb9StLwoTq5S8vZpT6pym1GLtzRNmxwjcZIe2VQ2X9L054hlrLdVhbXlw4rVmMx6w5me4XM9eF1rfrDbvcRlBiU7HoRDC1Dwshb8ooHH3XRpQLx/csMUKycYwg2Kw/IZW147CEvH8LRvtER3WuHJ0eqk9d5gHg2aHxIYZ7PGD69BmgLiXM6LOv50xKbNW3Gl+xanI3lAOTRgeu0I6aEgeP/pDxioAlU1Pgzq2oI8jkxS1Zw8jfatR9JxAkbsNybtJuoTZkOxSTtIR00+X+ZpfLw/1Z92Ng0NBc20ZIFdbSjN1eN1E6yQgQWD0MhEbUmSF06mn55rHVTBI0d3zzLtBYyGcS+1v1wY905lbH/MxbL0Rr16nfm4QQ2QflK+gXlmZLDFDxRdfZj+BxD2+k66dhgdQk52taAaNkfe5yt+xzV+OHldyui9uD+HOxls9J7mldONYev+PIxvps8Hu1P+Ll/5+vt75F0UXmHzw9ylwlMveeK+izST+2gb1Alo29EchWkbcRVl013JPLiKwLbB1UtYFc5sS1ysI67eOhHumMwI19M8D9SUzmCuJTrV58njKUr9yXrTVlQLDy+Oo27Obv4NAAD//w/9DiA=" } diff --git a/metricbeat/module/couchdb/fields.go b/metricbeat/module/couchdb/fields.go index a6fc61d4264..5c0e6f2d20b 100644 --- a/metricbeat/module/couchdb/fields.go +++ b/metricbeat/module/couchdb/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetCouchdb returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/couchdb. +// This is the base64 encoded gzipped contents of module/couchdb. func AssetCouchdb() string { return "eJy8mEtv6jgUx/d8iiP2lcJrURaVWkrb0fSBplSjWUXGPhCriZ2xnSLup79yIJAUaK5b2Vkm9vn/ch5+nAt4x80YqCxowhYdAMNNimPoTuyb25tuB4ChpornhksxhqsOAFTjIZOsSLEDoDBFonEMK9IBWHJMmR6XQy9AkAzrEvYxm9wOVrLId28aKpPKPhrFqd4NqZutm9aoPlDtX5+yfqRwVfsAMJHCEC407H57ZxK0IUbXRjZ/s3o+c9XZEmNy1vhyDq8F0T4P8/msZOLaHNzyFUYd5YPjOlZI2OeJB6ZUitWJjy1Y5fNcZAtUIJelDjR1zjItivQ9Vvh/gdr4wDpQWSU4UjoLRlOOwuiKjYtVTBMiVuiXcicLS6mASmG4KGSh4Uj7LLfBLJeKqE3sNdwH4r2gU9yDhLwsl69D3ijTKthxhiaRR377WdnubMNp2221O3mZ/effV1bFoUYepte3/qGsigPU7OV17h/KqjhA3U4fp/Opf6ytjgPY/TSAs+6nLr6avYWI31sbUnNlsJtuoWMq2dEG8PPdvNBQGv7+1t7tR1HXv9f6UQQvf4NCnUuh/2Q76vajXhCwHkwUEoPMka4fhK4P15Ri7oo3COK8QdSDJ/mBDGaoMiJQmHTjyDkMwjmEZ2ngSTK+5I6uHAYpkGEUwQ1h8M9us3cjDBHsYdSDN0EKk0jFfzk7cRAEcQB3Ui04Yygc+bzk4THgNhHvZCFcHTgK4sARPJXHzBLzOk3l2jnSl0FAL+3Ve5ly6lgrvRDr9rDXh5lCKgXjdhrcEZ46OnIUZN0ZRRH8JQwqQVJ43fYupkpJ1cZ6ujvTpPzG0abqD337QMOIIQuiMV4rbjzfuQ3PUAPZa8KaaNheuFl7hGWOIq6m+gW1UnAsdRbNrvIxJTTBOOPaN51VQ2E4JWWtlLrwSfcsanXztrHwQPmIYmWSknJ/EedCc4b7jt+am0QW9nRBE/4vLtqZ9zkaoMlSpaikRYbClClqZWGpZFbLXaekSLjnBszJlGiofl1WUsdLnnrOW6uwnyHVoQecEF1idH4HAAD//3gQco4=" } diff --git a/metricbeat/module/docker/fields.go b/metricbeat/module/docker/fields.go index 89cd5fc99a9..eafb7a969e9 100644 --- a/metricbeat/module/docker/fields.go +++ b/metricbeat/module/docker/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetDocker returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/docker. +// This is the base64 encoded gzipped contents of module/docker. func AssetDocker() string { return "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" } diff --git a/metricbeat/module/dropwizard/fields.go b/metricbeat/module/dropwizard/fields.go index 3143c53ee41..5325527e89e 100644 --- a/metricbeat/module/dropwizard/fields.go +++ b/metricbeat/module/dropwizard/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetDropwizard returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/dropwizard. +// This is the base64 encoded gzipped contents of module/dropwizard. func AssetDropwizard() string { return "eJxsjk2KAjEQhfc5xSPr6TlAFrOaG7gUkZBUuoPpJFSVSHt6aRX/sJaveO/7BhxocYjc+imfPUcDaNZCDvb/EVoDRJLAuWtu1eHPAMBGvQpCK4WCUkTiNuPZ+jUAUyEv5DB6Awip5jqKw9aKFPsDO6l2u1t/U2Pdh1ZTHh2SL0IGSJlKFHfFDah+pg/Z9XTpK4Hbsd+TL7LvW7e9V7tLAAAA///zhlJc" } diff --git a/metricbeat/module/elasticsearch/elasticsearch.go b/metricbeat/module/elasticsearch/elasticsearch.go index c2264f9d6a8..46825ee0084 100644 --- a/metricbeat/module/elasticsearch/elasticsearch.go +++ b/metricbeat/module/elasticsearch/elasticsearch.go @@ -28,6 +28,8 @@ import ( "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" + s "github.com/elastic/beats/v7/libbeat/common/schema" + c "github.com/elastic/beats/v7/libbeat/common/schema/mapstriface" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/metricbeat/helper" "github.com/elastic/beats/v7/metricbeat/helper/elastic" @@ -63,6 +65,9 @@ var CCRStatsAPIAvailableVersion = common.MustNewVersion("6.5.0") // EnrichStatsAPIAvailableVersion is the version of Elasticsearch since when the Enrich stats API is available. var EnrichStatsAPIAvailableVersion = common.MustNewVersion("7.5.0") +// BulkStatsAvailableVersion is the version since when bulk indexing stats are available +var BulkStatsAvailableVersion = common.MustNewVersion("7.8.0") + // Global clusterIdCache. Assumption is that the same node id never can belong to a different cluster id. var clusterIDCache = map[string]string{} @@ -107,6 +112,14 @@ type licenseWrapper struct { License License `json:"license"` } +var BulkStatsDict = c.Dict("bulk", s.Schema{ + "total_operations": c.Int("total_operations"), + "total_time_in_millis": c.Int("total_time_in_millis"), + "total_size_in_bytes": c.Int("total_size_in_bytes"), + "avg_time_in_millis": c.Int("avg_time_in_millis"), + "avg_size_in_bytes": c.Int("avg_size_in_bytes"), +}, c.DictOptional) + // GetClusterID fetches cluster id for given nodeID. func GetClusterID(http *helper.HTTP, uri string, nodeID string) (string, error) { // Check if cluster id already cached. If yes, return it. diff --git a/metricbeat/module/elasticsearch/fields.go b/metricbeat/module/elasticsearch/fields.go index b08f0c8122c..aadc37a05c3 100644 --- a/metricbeat/module/elasticsearch/fields.go +++ b/metricbeat/module/elasticsearch/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetElasticsearch returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/elasticsearch. +// This is the base64 encoded gzipped contents of module/elasticsearch. func AssetElasticsearch() string { return "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" } diff --git a/metricbeat/module/elasticsearch/index/data_xpack.go b/metricbeat/module/elasticsearch/index/data_xpack.go index 35e9119fdf7..6c73b4ee2e1 100644 --- a/metricbeat/module/elasticsearch/index/data_xpack.go +++ b/metricbeat/module/elasticsearch/index/data_xpack.go @@ -65,6 +65,7 @@ type indexStats struct { IndexTimeInMillis int `json:"index_time_in_millis"` ThrottleTimeInMillis int `json:"throttle_time_in_millis"` } `json:"indexing"` + Bulk bulkStats `json:"bulk"` Merges struct { TotalSizeInBytes int `json:"total_size_in_bytes"` } `json:"merges"` @@ -120,6 +121,14 @@ type shardStats struct { Relocating int `json:"relocating"` } +type bulkStats struct { + TotalOperations int `json:"total_operations"` + TotalTimeInMillis int `json:"total_time_in_millis"` + TotalSizeInBytes int `json:"total_size_in_bytes"` + AvgTimeInMillis int `json:"throttle_time_in_millis"` + AvgSizeInBytes int `json:"avg_size_in_bytes"` +} + func eventsMappingXPack(r mb.ReporterV2, m *MetricSet, info elasticsearch.Info, content []byte) error { clusterStateMetrics := []string{"metadata", "routing_table"} clusterState, err := elasticsearch.GetClusterState(m.HTTP, m.HTTP.GetURI(), clusterStateMetrics) diff --git a/metricbeat/module/elasticsearch/index/index.go b/metricbeat/module/elasticsearch/index/index.go index cd2dc3ffca0..3454b0d6554 100644 --- a/metricbeat/module/elasticsearch/index/index.go +++ b/metricbeat/module/elasticsearch/index/index.go @@ -18,8 +18,12 @@ package index import ( + "net/url" + "strings" + "github.com/pkg/errors" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/elasticsearch" ) @@ -67,14 +71,22 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { return nil } - content, err := m.HTTP.FetchContent() + info, err := elasticsearch.GetInfo(m.HTTP, m.HostData().SanitizedURI) if err != nil { + return errors.Wrap(err, "failed to get info from Elasticsearch") + } + + if err := m.updateServicePath(*info.Version.Number); err != nil { + if m.XPack { + m.Logger().Error(err) + return nil + } return err } - info, err := elasticsearch.GetInfo(m.HTTP, m.HostData().SanitizedURI) + content, err := m.HTTP.FetchContent() if err != nil { - return errors.Wrap(err, "failed to get info from Elasticsearch") + return err } if m.XPack { @@ -92,3 +104,35 @@ func (m *MetricSet) Fetch(r mb.ReporterV2) error { return nil } + +func (m *MetricSet) updateServicePath(esVersion common.Version) error { + p, err := getServicePath(esVersion) + if err != nil { + return err + } + + m.SetServiceURI(p) + return nil + +} + +func getServicePath(esVersion common.Version) (string, error) { + currPath := statsPath + if esVersion.LessThan(elasticsearch.BulkStatsAvailableVersion) { + // Can't request bulk stats so don't change service URI + return currPath, nil + } + + u, err := url.Parse(currPath) + if err != nil { + return "", err + } + + if strings.HasSuffix(u.Path, ",bulk") { + // Bulk stats already being requested so don't change service URI + return currPath, nil + } + + u.Path += ",bulk" + return u.String(), nil +} diff --git a/metricbeat/module/elasticsearch/index/index_test.go b/metricbeat/module/elasticsearch/index/index_test.go new file mode 100644 index 00000000000..8c4106e9944 --- /dev/null +++ b/metricbeat/module/elasticsearch/index/index_test.go @@ -0,0 +1,70 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package index + +import ( + "strings" + "testing" + "testing/quick" + + "github.com/elastic/beats/v7/libbeat/common" + + "github.com/stretchr/testify/require" +) + +func TestGetServiceURI(t *testing.T) { + tests := map[string]struct { + esVersion *common.Version + expectedPath string + }{ + "bulk_stats_unavailable": { + esVersion: common.MustNewVersion("7.7.0"), + expectedPath: statsPath, + }, + "bulk_stats_available": { + esVersion: common.MustNewVersion("7.8.0"), + expectedPath: strings.Replace(statsPath, statsMetrics, statsMetrics+",bulk", 1), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + newURI, err := getServicePath(*test.esVersion) + require.NoError(t, err) + require.Equal(t, test.expectedPath, newURI) + }) + } +} + +func TestGetServiceURIMultipleCalls(t *testing.T) { + err := quick.Check(func(r uint) bool { + numCalls := 2 + (r % 10) // between 2 and 11 + + var uri string + var err error + for i := uint(0); i < numCalls; i++ { + uri, err = getServicePath(*common.MustNewVersion("7.8.0")) + if err != nil { + return false + } + } + + return err == nil && uri == strings.Replace(statsPath, statsMetrics, statsMetrics+",bulk", 1) + }, nil) + require.NoError(t, err) +} diff --git a/metricbeat/module/elasticsearch/index_summary/data_xpack.go b/metricbeat/module/elasticsearch/index_summary/data_xpack.go index d1e00ea64b8..4e35744133d 100644 --- a/metricbeat/module/elasticsearch/index_summary/data_xpack.go +++ b/metricbeat/module/elasticsearch/index_summary/data_xpack.go @@ -51,6 +51,7 @@ var ( "is_throttled": c.Bool("is_throttled"), "throttle_time_in_millis": c.Int("throttle_time_in_millis"), }), + "bulk": elasticsearch.BulkStatsDict, "search": c.Dict("search", s.Schema{ "query_total": c.Int("query_total"), "query_time_in_millis": c.Int("query_time_in_millis"), diff --git a/metricbeat/module/elasticsearch/node_stats/data_xpack.go b/metricbeat/module/elasticsearch/node_stats/data_xpack.go index f7f612b11ee..53340103176 100644 --- a/metricbeat/module/elasticsearch/node_stats/data_xpack.go +++ b/metricbeat/module/elasticsearch/node_stats/data_xpack.go @@ -49,6 +49,7 @@ var ( "index_time_in_millis": c.Int("index_time_in_millis"), "throttle_time_in_millis": c.Int("throttle_time_in_millis"), }), + "bulk": elasticsearch.BulkStatsDict, "search": c.Dict("search", s.Schema{ "query_total": c.Int("query_total"), "query_time_in_millis": c.Int("query_time_in_millis"), diff --git a/metricbeat/module/envoyproxy/fields.go b/metricbeat/module/envoyproxy/fields.go index 06afeacdf12..ab451a5ef5e 100644 --- a/metricbeat/module/envoyproxy/fields.go +++ b/metricbeat/module/envoyproxy/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetEnvoyproxy returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/envoyproxy. +// This is the base64 encoded gzipped contents of module/envoyproxy. func AssetEnvoyproxy() string { return "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" } diff --git a/metricbeat/module/etcd/fields.go b/metricbeat/module/etcd/fields.go index b2d735b9908..2873b9f1681 100644 --- a/metricbeat/module/etcd/fields.go +++ b/metricbeat/module/etcd/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetEtcd returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/etcd. +// This is the base64 encoded gzipped contents of module/etcd. func AssetEtcd() string { return "eJzMmU9v28gOwO/+FEQur+/h1e/wbjkssJsWiwKbomiL9rBYqPQMZc1mNKMOKbvup1+M/jiy9SdO5LjVoQ0sDfkbkuKQ1Eu4o901kCi9ABAjlq7h6rUofbUA0MQqmEKMd9fwywIAqifh1uvS0gIgkCVkuoY1LgCYRIxb8zX8ecVsr/4LV5lIcfXXAiA1ZDVfVzJegsOc9lrjJbsiSgm+LJpfBnTH60tc9AWUd4LGMbCgGBajGCRDgS0FgkCoIQ0+h9f3KroEXQosTLKhwMa7/b0W6I52Wx905/cRrHhFXfDruzfQSIPUB8hJQoQL8X/aoF0segSWUFPoKe9a4wHVN605KvfU8jqmWXYePnRZex0bp4uXemv9lgIvlS+d8MFDY7APAMfrY0bgynxFAXwKKRpLGtBp4FIpYk5LC+8xFXj/7gYCfS2J5WArY+BT8MtGeG9BuxHjhNYH3hjczhTj6TBx03NJGsONU/T/GGayKOTU7lzuhUYeiAdClUFBFMA4kIxA2ZKlt7fTndnIXuKGAq5p1Ias0JJOUutRHiFWlSGQ66+YKTbHbyYv87OLNW5S7FggHUvcC2RBpzHoV7QxKIeJ8UTi0dwGE8kVjqPqzauYGlBJifZYVC+NMoXNvDT6oZKwz9nVCRKjtcrsn/4P/2vvkNOFNwcBss+rK5JTM2uGnEwYaLWT48h+4J37nJFkFADbM4C+GRYee+smPJaoDN2amkw1iGe9Wz8O7+0+2zd4jRJgIgcop0MWwRee0XKifJ4bEdLPRKq8Y3Jc8r1OqHWSPpWxIKeNW1+QsNF4KmB9hlyQr1Y4jbcOhUpYMDyfc5mcwHr61OwhZej081krkCKzIf0QVoukDd/NyXmvDN9dLuPlG6USvUrEC9qEzXdaxiw3XFMOmDH1IUepU+PxogdM/MF8p8rn4gNp0CgYU87tp5ubaadv0SYp75xKdBmq43DpeLkq1R3J8j+D5H71N6nj2Kh/TJ4aIn805VTsKrbBxE4LMIudjvVrjlXWQSg8YivnDOTPwQh1ufrCH0HGvarmbFxtddpVMUi2QnVHTjcHzc8XAw3f/iw9KRAmNnXOaPitZavUnBwME3Tni4hjtsmA2Ocvyn3Yzcm3t5WEy2XctU9yyllQOEFrvbpYwm12WilFIV3LAOSYhW8p/xCZ4Hc/bm1HsvVh1vH2thZxOXsra8hJUhcv5ORi5q7qhaqgqe1cHbHTWaDL2tYdl+XdVztDzPeNnU3nxMDhdCxKO9tszBy3rye3tj2DxKAsnflaEhhNTkxqKFSZPt7JKRaIp3RuxqV+Oa/t7jvL6PjSVu1ZPRppO7gjrhGcqooXkx+3szOIIkuUCNuMYndrGJzXBFushsKh256NUJXFeZEwj8dbZagIFgkbK2XIsIo97tgMo0sY/z2nnQw3XvoXH8oe1B5IbZZYxAayaT4mKoLhydIDRPcz31rNvsnpeDEarAheEfOIHyvOFTq9NVqygL1RyYMjtZMx69xUUAAm5Z3ucBpuUlisxF60gzTwzu7+PY5d3K2fFXhv0KczD8IzOf28sTG0ieq7zmFo8PFw9oDwZ4gKrgcw8KLJAJV1l/AxPrNBW1YPlU5Tahxp8A7YuLVtE307BTv63nGwzR8eReff4/Bmf+zhMcY0Yvkn8ZCpxratHcN+JN+vhcQHmlMMfcyoFtL9dGqcsmV0qqsLOuMd4MqX9UTWF1Q3YIPvYjsOm1VHrWn009x4NjlYPPApbXjlyAs1Qz/P16/JktCTCZrl8xjKQuMMhmb5PAYVaA5Ds3wmg88LDIRO8xaLp7McijkX08xI6Ql6PNegXPpWmECPKAcGpWxRVEZhelv/BAAA//+7EUCM" } diff --git a/metricbeat/module/golang/fields.go b/metricbeat/module/golang/fields.go index 3a81eac5023..0a3fab7f077 100644 --- a/metricbeat/module/golang/fields.go +++ b/metricbeat/module/golang/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetGolang returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/golang. +// This is the base64 encoded gzipped contents of module/golang. func AssetGolang() string { return "eJzkV9tu4zYQffdXHPgpiyb+AANdYOui7kObFkhbFCgKYyyNJDa8CCRlW39fkJIcx5G0cjaBe9GTTQkz55y5cOYOj1wvkRtJOp8BXnjJS8zX8WA+A1J2iRWlF0Yv8XEGAM1LKJNWkmeAK4z1m8ToTORLZCRdOLUsmRwvkVP4hr0XOndL/DF3Ts5vMS+8L+d/zoBMsEzdMtq+gybFJ4jC4+syHFlTle1JD6rndk5t8aHckT0e99kbtNk8Lyw8J9c95wBOQSQqlULzs3cdkkeu98amZ+9G8ITnl4I7ozAZfCEc1galNbklBefJeuyFLxYvBCmYyi+RI7g+cRXMQejMWEXh8yCXcZxiW7fKLf710vUCy5NeTOdKTkC0JrulnJEYKTnxxsJVSpGtF2ef9ul0CskbT3JTUuXO9RrHNwFjI12wj/UK0QXSyjYhNzu2kCJjL1QUtbQmYefO8Y9xOOWhXb//loI0x+bwGhYAvu2QC437HpjPBU1Mpf2goANopuupK7VlG2Rbr7Anh4LKkjWnw7g0H/wmTzZSKHExsqZUl9jWnvuEnoL8ng++y9eg415I2eLGvmCN75nKT1KaBB+bEiMVVBymlJTVJrMUzQ0yyqShPr5TIH/XGg9Cr37+FTFXq7ZTrVfDyN6vnn4g51+Wkwu/hM7hC4YyWngT/5ZshelJimk1NZTEeMOywio4aTP5rEccOQnXJc4IoyfcrlKjqIf0vwT2UF+7FPN4JDClw2FKOC7hNqHbnQJTdHh3uX+kw39X7IvUpt2Q17dT+9OObZgu/n+K945trnaez1vKq0e3cM1109rtvhBJ0Vyr8R43W09Cc4rMGtU6ftVId4UbvmmJDZc+HrhxlQqd/vfD4aF22LI0+w/Dl2hn4wpUfhMU4/RQu9sRRou4ymxc7fB181OkkvFV+1tXjofpOU/J4xW4fRPpdFNMRAEKYxd5Y29BOg1ThOMLs/KJWLuwXSNuDbcOALyJA9FPD58p8JZ+GKTetMpP7L52P1PRRp8cX7pK3B93iNbHcEQzy70ReTsI0cNYK/iLE/8eEM73qTZknHY+P7PpXS3Jn4De8I41RCNi+gG+sKbKC1P5mP3dkj1M5GjqH0AmtB9tPGr2DR/cOFIMcmh2Q9qaHY/cGqEDX42H0NE/XEl6JHPCVrm7Kkpt9N0R6WL2dwAAAP//qwVEDQ==" } diff --git a/metricbeat/module/graphite/fields.go b/metricbeat/module/graphite/fields.go index a4845943868..a8deeafd0d3 100644 --- a/metricbeat/module/graphite/fields.go +++ b/metricbeat/module/graphite/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetGraphite returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/graphite. +// This is the base64 encoded gzipped contents of module/graphite. func AssetGraphite() string { return "eJx8j80OwiAcw+88RbP7XoCDN+PJhyBSJxkbBJi6tzf7zJjTHvunv5YSNXuJKij/MIkCSCZZShSX2SoEoBlvwfhkXCtxEgDWBK5Od3YIBlqqSIlKCeBuaHWU49sSrWqYtQxKvR9N1/nZOejJSVtaZHgyrPYR7ydz0hch/8Ki/YDtCL5V4y2z27KkZv9yQe9uf/YMOk/AqVR8AgAA///94G6N" } diff --git a/metricbeat/module/haproxy/fields.go b/metricbeat/module/haproxy/fields.go index 531515409dc..f5cd629e773 100644 --- a/metricbeat/module/haproxy/fields.go +++ b/metricbeat/module/haproxy/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetHaproxy returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/haproxy. +// This is the base64 encoded gzipped contents of module/haproxy. func AssetHaproxy() string { return "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" } diff --git a/metricbeat/module/http/fields.go b/metricbeat/module/http/fields.go index d8c94f37f07..ce8ab7e8223 100644 --- a/metricbeat/module/http/fields.go +++ b/metricbeat/module/http/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetHttp returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/http. +// This is the base64 encoded gzipped contents of module/http. func AssetHttp() string { return "eJzMlDFv6jAUhff8iqPMD+kNTBne/KanN7BVHdz4AIbETn1vaPn3lYEgkhokSofe0Tbf+Y6MM8OW+wpr1a4A1GnDCuXfxeJ/WQCWUkfXqQu+wp8CANIW2mD7hgUQ2dAIK6xMAQhVnV9JhadSpCl/oUzg8rkAlo6NlerAmMGblufUNLrvEiWGfljJZI8pl6TI156i5/Uc8Cp0mEO1EwnOL0NsTTp5cWyaP2pDYxllAj16hJcNa51s3ZQBFmselU5gCL1mmksXvPBbqh9RP7F7ZE23o82m18EyG73l/i1E+8VsUaO95Oh8N22XXsr89zxr1K2jkXuczsR/QbEMvX9QeiIweG1kdKV3/1PS79FSo6uFl3c6/hAMc+25CuOO8RGRT4TbAh8BAAD//zeEPwk=" } diff --git a/metricbeat/module/jolokia/fields.go b/metricbeat/module/jolokia/fields.go index 241a0320e41..574411f4107 100644 --- a/metricbeat/module/jolokia/fields.go +++ b/metricbeat/module/jolokia/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetJolokia returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/jolokia. +// This is the base64 encoded gzipped contents of module/jolokia. func AssetJolokia() string { return "eJx8kFFOwzAQRP99ipG/6QX8wQG4AkLISjbptrbX8m5RensUkoARqPM5uzN6mhOudA+4SJIrRwcYW6IA/7I53gEj6dC4GksJeHYAsF+RZbwlcoCepdn7IGXiOWCKSVe3UaKoFDCv1UpmXGYNePWqyT/Bn82qf3PAxJRGDV/lJ5SYqYdaZfe6FjW51d35h2vTHsQgxSIXRSZrPChoqaI04oPj91Ocqdge7ik2km2dvHTtf0EewvyMdcnLQULWffxe6dDB8hkAAP//TGJ6CQ==" } diff --git a/metricbeat/module/kafka/fields.go b/metricbeat/module/kafka/fields.go index 7920163da93..72ee2cdc60c 100644 --- a/metricbeat/module/kafka/fields.go +++ b/metricbeat/module/kafka/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetKafka returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/kafka. +// This is the base64 encoded gzipped contents of module/kafka. func AssetKafka() string { return "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" } diff --git a/metricbeat/module/kibana/fields.go b/metricbeat/module/kibana/fields.go index eeddf8d856a..fed14792f3b 100644 --- a/metricbeat/module/kibana/fields.go +++ b/metricbeat/module/kibana/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetKibana returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/kibana. +// This is the base64 encoded gzipped contents of module/kibana. func AssetKibana() string { return "eJzMmM1u4zYQx+9+ioEve0n0AD4UKNoCLYoExbZBD0VhjMWxxYYiVc7Iifv0BSnJkWXKllsLuzoEiT7+85vhfJB5hFc6rOBVb9DiAkC0GFrB8ud4Y7kAUMS515VoZ1fwzQIAoHkIpVO1oQUAF87LOnd2q3cr2KLhcNeTIWRawS4IM4lou+MV/LFkNssHWBYi1fLPBcBWk1G8itqPYLGkHlG45FAFHe/qqr2ToDrV6WuxoPDxbkpuVLK5WoejDqBV4Gv7KLokKEm8zjnrvX7qd3cN2fp8da3VyYMOEY1GHjypUIoVMPm9zik7+7DUO4+ND+JrGjy94GPPT21Z0OYELy8/fZ8kDj+TxK90eHN+CHWj2TP1zqy2it7vZ/cZSwK3be1/YtBWyFs0CUMdQuFYsnndDyZGQyAeLVeh3lApTzxMj6mJk/76HtkTLJD/xEc/YsUE5KRDe/KsnZ3sxjSItOppCNLvXArBsaFYrLhwkoTeOGcIh7JXsH8vSAryIAV1LmxqbRRoBjzaa+6lkQSlTifDPZIyLCehkSJlqGPInc1r78nGYWApD+JpJuPs7sZircsN+VCuudFkBXomoERFIC6Gr8m/DJ6dEEiBAhvv3pg8Q44WmKyCsjaiK0PAOvyKllzNJ4riwNPfNbH0Xo7KgMwU5oCAszk9xPSWgg5R3tNjzQTEghujuSDVl82Scau8y8cqeTilJkTql0aum02D56lB9IFC+7B6xrlqrcjgISuH33dgnKMhtd4ah8M6uEoI8EOwA8EORDugLZTaGM2UO6uGRju8kkrnD1lBOIzJpXhN4OliFpRHAjcWul5vdoIm2xyEzr+9kPhR2vkSZQVjH191AOC3YL1xAI1xOQqpkMZtdoUIp9WP2xAm9cXofwzcgQA2h+nIrP+htdGlli8G/oTvGTijgCvMKRKdxv/ZKcr+4s6phwkLUYW9Zar0rrgzAfclaoc22gvyaOV1RG0rvFeHAvjc9tbc1VZu6lQfUEpz21rnCNTHvGmd52aYvJGnnmkaztVBO5iBrCl0e8Y3snJcOcu0Dst+r/X73IpCEL1t0HyQ4X43T4p/uyePOzq6HimvDJjeiMH3ebCe8F2XdTkRa3Rfd/Ph9deo0C7H/zqnzn/qS++PvtrzcQBLI7dni6yp0nkON20LyOY65MS8yVyoJmOy8Of9V79Vj8ZGVj/dXf5z73pq5M7rASb0rEknHLhDs/iusdIzOH6MgPNBPY6U3h9PYupmdlgrzTL4t1d3jYUQJk9uuBa/ibyQmJX9wX05ojBhks9JegL3bwAAAP//xlKE7A==" } diff --git a/metricbeat/module/kubernetes/_meta/Dockerfile b/metricbeat/module/kubernetes/_meta/Dockerfile deleted file mode 100644 index b3dac95e01f..00000000000 --- a/metricbeat/module/kubernetes/_meta/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM exekias/localkube-image -RUN apt-get update && apt-get install -y curl && apt-get clean -HEALTHCHECK --interval=1s --retries=300 CMD curl -f localhost:10255/stats/summary | grep kube-addon-manager -CMD exec /localkube start \ - --apiserver-insecure-address=0.0.0.0 \ - --apiserver-insecure-port=8080 \ - --logtostderr=true \ - --containerized diff --git a/metricbeat/module/kubernetes/_meta/Dockerfile.kube-state b/metricbeat/module/kubernetes/_meta/Dockerfile.kube-state deleted file mode 100644 index b064dc3065e..00000000000 --- a/metricbeat/module/kubernetes/_meta/Dockerfile.kube-state +++ /dev/null @@ -1,8 +0,0 @@ -FROM gcr.io/google_containers/kube-state-metrics:v0.5.0 - -ADD kubeconfig / - -HEALTHCHECK --interval=1s --retries=90 CMD curl -f http://localhost:8080/metrics - -ENTRYPOINT ["/kube-state-metrics"] -CMD ["--port=8080", "--in-cluster=false", "--apiserver=http://172.17.0.1:8080", "--kubeconfig=/kubeconfig"] diff --git a/metricbeat/module/kubernetes/_meta/README.md b/metricbeat/module/kubernetes/_meta/README.md new file mode 100644 index 00000000000..903e9010018 --- /dev/null +++ b/metricbeat/module/kubernetes/_meta/README.md @@ -0,0 +1,23 @@ +# Running integration tests. + +Running the integration tests for the kubernetes module has the requirement of: + +* docker +* kind +* kubectl + +Once those tools are installed its as simple as: + +``` +MODULE="kubernetes" mage goIntegTest +``` + +The integration tester will use the default context from the kubectl configuration defined +in the `KUBECONFIG` environment variable. There is no requirement that the kubernetes even +be local to your development machine, it just needs to be accessible. + +If no `KUBECONFIG` is set and `kind` is installed then the runner will use `kind` to create +a local cluster inside of your local docker to perform the intergation tests inside. The +`kind` cluster will be created and destroy before and after the test. If you would like to +keep the `kind` cluster running after the test has finished you can set `KIND_SKIP_DELETE=1` +inside of your environment. diff --git a/metricbeat/module/kubernetes/_meta/kubeconfig b/metricbeat/module/kubernetes/_meta/kubeconfig deleted file mode 100644 index cad24101463..00000000000 --- a/metricbeat/module/kubernetes/_meta/kubeconfig +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: v1 -clusters: -- cluster: - server: http://172.17.0.1:8080 - name: kubernetes -contexts: -- context: - cluster: kubernetes - user: kubernetes - name: kubernetes -current-context: kubernetes -kind: Config -preferences: {} -users: -- name: kubernetes - user: - client-certificate: - client-key: diff --git a/metricbeat/module/kubernetes/apiserver/apiserver_integration_test.go b/metricbeat/module/kubernetes/apiserver/apiserver_integration_test.go new file mode 100644 index 00000000000..807b2c0760e --- /dev/null +++ b/metricbeat/module/kubernetes/apiserver/apiserver_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package apiserver + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetAPIServerConfig(t, "apiserver") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/container/container_integration_test.go b/metricbeat/module/kubernetes/container/container_integration_test.go new file mode 100644 index 00000000000..18df4cdd2d1 --- /dev/null +++ b/metricbeat/module/kubernetes/container/container_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package container + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeletConfig(t, "container") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/controllermanager/controllermanager_integration_test.go b/metricbeat/module/kubernetes/controllermanager/controllermanager_integration_test.go new file mode 100644 index 00000000000..f07bb7b1071 --- /dev/null +++ b/metricbeat/module/kubernetes/controllermanager/controllermanager_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package controllermanager + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetAPIServerConfig(t, "controllermanager") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/docker-compose.yml b/metricbeat/module/kubernetes/docker-compose.yml deleted file mode 100644 index 083e326f325..00000000000 --- a/metricbeat/module/kubernetes/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '2.3' - -services: - #kubernetes: - # build: ./module/kubernetes/_meta - # network_mode: host - # pid: host - # privileged: true - # volumes: - # - /:/rootfs:ro - # - /sys:/sys - # - /var/lib/docker:/var/lib/docker - # - /var/run:/var/run - # ports: - # - 10255 - - #kubestate: - # build: - # context: ./_meta - # dockerfile: Dockerfile.kube-state - # depends_on: - # - kubernetes - # ports: - # - 18080 diff --git a/metricbeat/module/kubernetes/event/_meta/fields.yml b/metricbeat/module/kubernetes/event/_meta/fields.yml index 023e81d2d11..bf66a8aec45 100644 --- a/metricbeat/module/kubernetes/event/_meta/fields.yml +++ b/metricbeat/module/kubernetes/event/_meta/fields.yml @@ -58,6 +58,10 @@ type: date description: > Timestamp of creation of the given event + - name: generate_name + type: keyword + description: > + Generate name of the event - name: name type: keyword description: > diff --git a/metricbeat/module/kubernetes/fields.go b/metricbeat/module/kubernetes/fields.go index 06292b72a57..02fae8082af 100644 --- a/metricbeat/module/kubernetes/fields.go +++ b/metricbeat/module/kubernetes/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetKubernetes returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/kubernetes. +// This is the base64 encoded gzipped contents of module/kubernetes. func AssetKubernetes() string { - return "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" + return "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" } diff --git a/metricbeat/module/kubernetes/kubernetes.yml b/metricbeat/module/kubernetes/kubernetes.yml new file mode 100644 index 00000000000..d87cb4f5f9b --- /dev/null +++ b/metricbeat/module/kubernetes/kubernetes.yml @@ -0,0 +1,135 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kube-state-metrics +rules: +- apiGroups: ["*"] + resources: ["*"] + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kube-state-metrics +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kube-state-metrics +subjects: +- kind: ServiceAccount + name: kube-state-metrics + namespace: default +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kube-state-metrics +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kube-state-metrics + labels: + app: kube-state-metrics +spec: + replicas: 1 + selector: + matchLabels: + app: kube-state-metrics + template: + metadata: + labels: + app: kube-state-metrics + spec: + containers: + - name: kube-state-metrics + image: quay.io/coreos/kube-state-metrics:v1.8.0 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + timeoutSeconds: 5 + ports: + - containerPort: 8080 + name: http-metrics + - containerPort: 8081 + name: telemetry + readinessProbe: + httpGet: + path: / + port: 8081 + initialDelaySeconds: 5 + timeoutSeconds: 5 + serviceAccountName: kube-state-metrics +--- +apiVersion: v1 +kind: Service +metadata: + name: kube-state-metrics +spec: + type: ClusterIP + selector: + app: kube-state-metrics + ports: + - port: 8080 + targetPort: 8080 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: basic-sts + labels: + app: basic-sts +spec: + serviceName: basic-sts + replicas: 1 + selector: + matchLabels: + app: basic-sts + template: + metadata: + labels: + app: basic-sts + spec: + containers: + - name: sh + image: alpine:3 + command: ["sh", "-c", "sleep infinity"] + volumeMounts: + - name: mnt + mountPath: /mnt + volumeClaimTemplates: + - metadata: + name: mnt + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Mi +--- +apiVersion: batch/v1beta1 +kind: CronJob +metadata: + name: basic-cronjob +spec: + schedule: "* * * * *" + jobTemplate: + spec: + template: + metadata: + name: basic-job + spec: + containers: + - name: hello + image: alpine:3 + command: ["sh", "-c", "echo Hello!"] + restartPolicy: Never +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: object-counts +spec: + hard: + configmaps: "99" diff --git a/metricbeat/module/kubernetes/node/node_integration_test.go b/metricbeat/module/kubernetes/node/node_integration_test.go new file mode 100644 index 00000000000..7d1c73bc20b --- /dev/null +++ b/metricbeat/module/kubernetes/node/node_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package node + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeletConfig(t, "node") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/pod/pod_integration_test.go b/metricbeat/module/kubernetes/pod/pod_integration_test.go new file mode 100644 index 00000000000..9202891f84d --- /dev/null +++ b/metricbeat/module/kubernetes/pod/pod_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package pod + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeletConfig(t, "pod") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/proxy/proxy_integration_test.go b/metricbeat/module/kubernetes/proxy/proxy_integration_test.go new file mode 100644 index 00000000000..d596fb545f8 --- /dev/null +++ b/metricbeat/module/kubernetes/proxy/proxy_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package proxy + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeProxyConfig(t, "proxy") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/scheduler/scheduler_integration_test.go b/metricbeat/module/kubernetes/scheduler/scheduler_integration_test.go new file mode 100644 index 00000000000..90d986404dc --- /dev/null +++ b/metricbeat/module/kubernetes/scheduler/scheduler_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package scheduler + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetSchedulerConfig(t, "scheduler") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_container/state_container_integration_test.go b/metricbeat/module/kubernetes/state_container/state_container_integration_test.go new file mode 100644 index 00000000000..6d7a7978250 --- /dev/null +++ b/metricbeat/module/kubernetes/state_container/state_container_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_container + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_container") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_cronjob/state_cronjob_integration_test.go b/metricbeat/module/kubernetes/state_cronjob/state_cronjob_integration_test.go new file mode 100644 index 00000000000..3a0f4ff3659 --- /dev/null +++ b/metricbeat/module/kubernetes/state_cronjob/state_cronjob_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_cronjob + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_cronjob") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_deployment/state_deployment_integration_test.go b/metricbeat/module/kubernetes/state_deployment/state_deployment_integration_test.go new file mode 100644 index 00000000000..17a38d56ecd --- /dev/null +++ b/metricbeat/module/kubernetes/state_deployment/state_deployment_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_deployment + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_deployment") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_node/state_node_integration_test.go b/metricbeat/module/kubernetes/state_node/state_node_integration_test.go new file mode 100644 index 00000000000..13b9af0f929 --- /dev/null +++ b/metricbeat/module/kubernetes/state_node/state_node_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_node + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_node") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_persistentvolume/state_persistentvolume_integration_test.go b/metricbeat/module/kubernetes/state_persistentvolume/state_persistentvolume_integration_test.go new file mode 100644 index 00000000000..7840febd337 --- /dev/null +++ b/metricbeat/module/kubernetes/state_persistentvolume/state_persistentvolume_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_persistentvolume + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_persistentvolume") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_persistentvolumeclaim/state_persistentvolumeclaim_integration_test.go b/metricbeat/module/kubernetes/state_persistentvolumeclaim/state_persistentvolumeclaim_integration_test.go new file mode 100644 index 00000000000..24529123d5d --- /dev/null +++ b/metricbeat/module/kubernetes/state_persistentvolumeclaim/state_persistentvolumeclaim_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_persistentvolumeclaim + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_persistentvolumeclaim") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_pod/state_pod_integration_test.go b/metricbeat/module/kubernetes/state_pod/state_pod_integration_test.go new file mode 100644 index 00000000000..c269092f067 --- /dev/null +++ b/metricbeat/module/kubernetes/state_pod/state_pod_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_pod + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_pod") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_replicaset/state_replicaset_integration_test.go b/metricbeat/module/kubernetes/state_replicaset/state_replicaset_integration_test.go new file mode 100644 index 00000000000..c8b55192706 --- /dev/null +++ b/metricbeat/module/kubernetes/state_replicaset/state_replicaset_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_replicaset + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_replicaset") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_resourcequota/state_resourcequota_integration_test.go b/metricbeat/module/kubernetes/state_resourcequota/state_resourcequota_integration_test.go new file mode 100644 index 00000000000..0d3bff3f706 --- /dev/null +++ b/metricbeat/module/kubernetes/state_resourcequota/state_resourcequota_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_resourcequota + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_resourcequota") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_service/state_service_integration_test.go b/metricbeat/module/kubernetes/state_service/state_service_integration_test.go new file mode 100644 index 00000000000..895b4c6cd7d --- /dev/null +++ b/metricbeat/module/kubernetes/state_service/state_service_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_service + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_service") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_statefulset/state_statefulset_integration_test.go b/metricbeat/module/kubernetes/state_statefulset/state_statefulset_integration_test.go new file mode 100644 index 00000000000..bce92bc6ce3 --- /dev/null +++ b/metricbeat/module/kubernetes/state_statefulset/state_statefulset_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_statefulset + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_statefulset") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/state_storageclass/state_storageclass_integration_test.go b/metricbeat/module/kubernetes/state_storageclass/state_storageclass_integration_test.go new file mode 100644 index 00000000000..2db797d0ffb --- /dev/null +++ b/metricbeat/module/kubernetes/state_storageclass/state_storageclass_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package state_storageclass + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeStateMetricsConfig(t, "state_storageclass") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/system/system_integration_test.go b/metricbeat/module/kubernetes/system/system_integration_test.go new file mode 100644 index 00000000000..5b876f0caff --- /dev/null +++ b/metricbeat/module/kubernetes/system/system_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package system + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeletConfig(t, "system") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kubernetes/test/integration.go b/metricbeat/module/kubernetes/test/integration.go new file mode 100644 index 00000000000..64dfc99ab65 --- /dev/null +++ b/metricbeat/module/kubernetes/test/integration.go @@ -0,0 +1,87 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package test + +import ( + "testing" +) + +// GetAPIServerConfig function returns configuration for talking to Kubernetes API server. +func GetAPIServerConfig(t *testing.T, metricSetName string) map[string]interface{} { + t.Helper() + return map[string]interface{}{ + "module": "kubernetes", + "metricsets": []string{metricSetName}, + "host": "${NODE_NAME}", + "hosts": []string{"https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"}, + "bearer_token_file": "/var/run/secrets/kubernetes.io/serviceaccount/token", + "ssl": map[string]interface{}{ + "certificate_authorities": []string{ + "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", + }, + }, + } +} + +// GetKubeStateMetricsConfig function returns configuration for talking to kube-state-metrics. +func GetKubeStateMetricsConfig(t *testing.T, metricSetName string) map[string]interface{} { + t.Helper() + return map[string]interface{}{ + "module": "kubernetes", + "metricsets": []string{metricSetName}, + "host": "${NODE_NAME}", + "hosts": []string{"kube-state-metrics:8080"}, + } +} + +// GetKubeletConfig function returns configuration for talking to Kubelet API. +func GetKubeletConfig(t *testing.T, metricSetName string) map[string]interface{} { + t.Helper() + return map[string]interface{}{ + "module": "kubernetes", + "metricsets": []string{metricSetName}, + "host": "${NODE_NAME}", + "hosts": []string{"https://localhost:10250"}, + "bearer_token_file": "/var/run/secrets/kubernetes.io/serviceaccount/token", + "ssl": map[string]interface{}{ + "verification_mode": "none", + }, + } +} + +// GetKubeProxyConfig function returns configuration for talking to kube-proxy. +func GetKubeProxyConfig(t *testing.T, metricSetName string) map[string]interface{} { + t.Helper() + return map[string]interface{}{ + "module": "kubernetes", + "metricsets": []string{metricSetName}, + "host": "${NODE_NAME}", + "hosts": []string{"localhost:10252"}, + } +} + +// GetSchedulerConfig function returns configuration for talking to kube-proxy. +func GetSchedulerConfig(t *testing.T, metricSetName string) map[string]interface{} { + t.Helper() + return map[string]interface{}{ + "module": "kubernetes", + "metricsets": []string{metricSetName}, + "host": "${NODE_NAME}", + "hosts": []string{"localhost:10251"}, + } +} diff --git a/metricbeat/module/kubernetes/test_kubernetes.py b/metricbeat/module/kubernetes/test_kubernetes.py deleted file mode 100644 index 911f29cfcfc..00000000000 --- a/metricbeat/module/kubernetes/test_kubernetes.py +++ /dev/null @@ -1,88 +0,0 @@ -import os -import sys -import unittest - -sys.path.append(os.path.join(os.path.dirname(__file__), '../../tests/system')) -import metricbeat - - -KUBERNETES_FIELDS = metricbeat.COMMON_FIELDS + ["kubernetes"] - - -class Test(metricbeat.BaseTest): - - # Tests are disabled as current docker-compose settings fail to start in many cases: - # COMPOSE_SERVICES = ['kubernetes'] # 'kubestate'] - - @unittest.skipUnless(False and metricbeat.INTEGRATION_TESTS, "integration test") - def test_kubelet_node(self): - """ Kubernetes kubelet node metricset tests """ - self._test_metricset('node', 1, self.get_kubelet_hosts()) - - @unittest.skipUnless(False and metricbeat.INTEGRATION_TESTS, "integration test") - def test_kubelet_system(self): - """ Kubernetes kubelet system metricset tests """ - self._test_metricset('system', 2, self.get_kubelet_hosts()) - - @unittest.skipUnless(False and metricbeat.INTEGRATION_TESTS, "integration test") - def test_kubelet_pod(self): - """ Kubernetes kubelet pod metricset tests """ - self._test_metricset('pod', 1, self.get_kubelet_hosts()) - - @unittest.skipUnless(False and metricbeat.INTEGRATION_TESTS, "integration test") - def test_kubelet_container(self): - """ Kubernetes kubelet container metricset tests """ - self._test_metricset('container', 1, self.get_kubelet_hosts()) - - @unittest.skipUnless(metricbeat.INTEGRATION_TESTS, "integration test") - @unittest.skip("flacky kube-state-metrics container healthcheck") - def test_state_node(self): - """ Kubernetes state node metricset tests """ - self._test_metricset('state_node', 1, self.get_kube_state_hosts()) - - @unittest.skipUnless(False and metricbeat.INTEGRATION_TESTS, "integration test") - @unittest.skip("flacky kube-state-metrics container healthcheck") - def test_state_pod(self): - """ Kubernetes state pod metricset tests """ - self._test_metricset('state_pod', 1, self.get_kube_state_hosts()) - - @unittest.skipUnless(False and metricbeat.INTEGRATION_TESTS, "integration test") - @unittest.skip("flacky kube-state-metrics container healthcheck") - def test_state_container(self): - """ Kubernetes state container metricset tests """ - self._test_metricset('state_container', 1, self.get_kube_state_hosts()) - - def _test_metricset(self, metricset, expected_events, hosts): - self.render_config_template(modules=[{ - "name": "kubernetes", - "enabled": "true", - "metricsets": [metricset], - "hosts": hosts, - "period": "5s", - "extras": { - "add_metadata": "false", - } - }]) - - proc = self.start_beat() - self.wait_until(lambda: self.output_lines() > 0) - proc.check_kill_and_wait() - - # Ensure no errors or warnings exist in the log. - self.assert_no_logged_warnings() - - output = self.read_output_json() - self.assertEqual(len(output), expected_events) - evt = output[0] - - self.assertCountEqual(self.de_dot(KUBERNETES_FIELDS), evt.keys(), evt) - - self.assert_fields_are_documented(evt) - - @classmethod - def get_kubelet_hosts(cls): - return [self.compose_host("kubernetes")] - - @classmethod - def get_kube_state_hosts(cls): - return [self.compose_host("kubestate")] diff --git a/metricbeat/module/kubernetes/volume/volume_integration_test.go b/metricbeat/module/kubernetes/volume/volume_integration_test.go new file mode 100644 index 00000000000..c0934a8cae0 --- /dev/null +++ b/metricbeat/module/kubernetes/volume/volume_integration_test.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build integration,linux + +package volume + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + "github.com/elastic/beats/v7/metricbeat/module/kubernetes/test" +) + +func TestFetchMetricset(t *testing.T) { + config := test.GetKubeletConfig(t, "volume") + metricSet := mbtest.NewFetcher(t, config) + events, errs := metricSet.FetchEvents() + if len(errs) > 0 { + t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) + } + assert.NotEmpty(t, events) +} diff --git a/metricbeat/module/kvm/_meta/config.reference.yml b/metricbeat/module/kvm/_meta/config.reference.yml index 79f754a52cb..84e584787e1 100644 --- a/metricbeat/module/kvm/_meta/config.reference.yml +++ b/metricbeat/module/kvm/_meta/config.reference.yml @@ -1,5 +1,5 @@ - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] diff --git a/metricbeat/module/kvm/_meta/config.yml b/metricbeat/module/kvm/_meta/config.yml index 2df123ca14d..78509f63aa4 100644 --- a/metricbeat/module/kvm/_meta/config.yml +++ b/metricbeat/module/kvm/_meta/config.yml @@ -1,5 +1,6 @@ - module: kvm #metricsets: # - dommemstat + # - status period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] diff --git a/metricbeat/module/kvm/fields.go b/metricbeat/module/kvm/fields.go index 7dd21356f95..4f509af839a 100644 --- a/metricbeat/module/kvm/fields.go +++ b/metricbeat/module/kvm/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetKvm returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/kvm. +// This is the base64 encoded gzipped contents of module/kvm. func AssetKvm() string { - return "eJyskDFuwzAMRXed4iN7LqChU9ceQq3YQLBoGrLsQrcvnEZFotByh3Lw4G+//8gzBioWw8oGyCFHsjgNK58MkCiSm8ninbIzgKf5I4UpBxktXgyA7T+w+CWSAT4DRT/ba3DG6JgqeJtcJrK4JFmm2xuF9wi5B3lhJp6zy7+Rxtzl3iKN0u5Zp1V5FGogPaFDrZ95I5ZUdLDucu+zPZW4Og1UviR59YtDs8Zur6uqrC4uPZco4+V/RLSmahGel+3UH1a/CrswPlO79+/f/q+dV/R3AAAA///hct15" + return "eJzckzFuxCAQRXtO8bX9XoAiVdocgoTJCpkxFmBHvn3k7BLZeBZHStLsFC485v0HZs7oaNboJlZAdtmTxqmb+KSASJ5MIo1XykYBltJbdEN2odd4UgCWdeBgR08KeHfkbdJfjTN6w1TAS+V5II1LDONweyPwtpA1yAZm4pRN/m5JzLvcW0ui1PssVatshSpIS+hQ61ovxCHOMlh2WfssT6FdnDqaP0K04heHZpXdvayiMhk/tlx86C9/IyIlFQu332wj/jD6ObBx/Z7aPP/22f80c4Ne378x/WYYdoTHGIRl2b9Ogr3+FSnnQe/eZwAAAP//6Q5sWg==" } diff --git a/metricbeat/module/kvm/status/_meta/data.json b/metricbeat/module/kvm/status/_meta/data.json new file mode 100644 index 00000000000..fb7fd3c3595 --- /dev/null +++ b/metricbeat/module/kvm/status/_meta/data.json @@ -0,0 +1,28 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "agent": { + "hostname": "host.example.com", + "name": "host.example.com" + }, + "event":{ + "dataset":"kvm.status", + "module":"kvm", + "duration":4012216 + }, + "metricset":{ + "name":"status" + }, + "service":{ + "address":"unix:///var/run/libvirt/libvirt-sock", + "type":"kvm" + }, + "kvm":{ + "status":{ + "stat":{ + "state":"running" + }, + "id":1, + "name":"generic-2" + } + } +} diff --git a/metricbeat/module/kvm/status/_meta/docs.asciidoc b/metricbeat/module/kvm/status/_meta/docs.asciidoc new file mode 100644 index 00000000000..b94f0f0f147 --- /dev/null +++ b/metricbeat/module/kvm/status/_meta/docs.asciidoc @@ -0,0 +1 @@ +This is the status metricset of the module kvm. diff --git a/metricbeat/module/kvm/status/_meta/fields.yml b/metricbeat/module/kvm/status/_meta/fields.yml new file mode 100644 index 00000000000..9f75085f2cf --- /dev/null +++ b/metricbeat/module/kvm/status/_meta/fields.yml @@ -0,0 +1,23 @@ +- name: status + type: group + description: > + status + release: beta + fields: + - name: stat + type: group + description: > + Memory stat + fields: + - name: state + type: keyword + description: > + domain state + - name: id + type: long + description: > + Domain id + - name: name + type: keyword + description: > + Domain name diff --git a/metricbeat/module/kvm/status/status.go b/metricbeat/module/kvm/status/status.go new file mode 100644 index 00000000000..eab1b947d7f --- /dev/null +++ b/metricbeat/module/kvm/status/status.go @@ -0,0 +1,161 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package status + +import ( + "net" + "net/url" + "time" + + "github.com/pkg/errors" + + "github.com/digitalocean/go-libvirt" + "github.com/digitalocean/go-libvirt/libvirttest" + + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/cfgwarn" + "github.com/elastic/beats/v7/metricbeat/mb" +) + +// init registers the MetricSet with the central registry as soon as the program +// starts. The New function will be called later to instantiate an instance of +// the MetricSet for each host defined in the module's configuration. After the +// MetricSet has been created then Fetch will begin to be called periodically. +func init() { + mb.Registry.MustAddMetricSet("kvm", "status", New, + mb.DefaultMetricSet(), + ) +} + +// MetricSet holds any configuration or state information. It must implement +// the mb.MetricSet interface. And this is best achieved by embedding +// mb.BaseMetricSet because it implements all of the required mb.MetricSet +// interface methods except for Fetch. +type MetricSet struct { + mb.BaseMetricSet + Timeout time.Duration + HostURL *url.URL +} + +// New creates a new instance of the MetricSet. New is responsible for unpacking +// any MetricSet specific configuration options if there are any. +func New(base mb.BaseMetricSet) (mb.MetricSet, error) { + cfgwarn.Beta("The kvm status metricset is beta.") + u, err := url.Parse(base.HostData().URI) + if err != nil { + return nil, err + } + + return &MetricSet{ + BaseMetricSet: base, + Timeout: base.Module().Config().Timeout, + HostURL: u, + }, nil +} + +// Fetch methods implements the data gathering and data conversion to the right +// format. It publishes the event which is then forwarded to the output. In case +// of an error set the Error field of mb.Event or simply call report.Error(). +func (m *MetricSet) Fetch(report mb.ReporterV2) error { + var ( + c net.Conn + err error + ) + + u := m.HostURL + + if u.Scheme == "test" { + // when running tests, a mock Libvirt server is used + c = libvirttest.New() + } else { + address := u.Host + if u.Host == "" { + address = u.Path + } + + c, err = net.DialTimeout(u.Scheme, address, m.Timeout) + if err != nil { + return errors.Wrapf(err, "cannot connect to %v", u) + } + } + + defer c.Close() + + l := libvirt.New(c) + if err = l.Connect(); err != nil { + return errors.Wrap(err, "error connecting to libvirtd") + } + defer func() { + if err = l.Disconnect(); err != nil { + msg := errors.Wrap(err, "failed to disconnect") + report.Error(msg) + m.Logger().Error(msg) + } + }() + + domains, err := l.Domains() + if err != nil { + return errors.Wrap(err, "error listing domains") + } + + for _, d := range domains { + state, err := l.DomainState(d.Name) + if err != nil { + continue + } + reported := report.Event(mb.Event{ + ModuleFields: common.MapStr{ + "id": d.ID, + "name": d.Name, + }, + MetricSetFields: common.MapStr{ + "stat": common.MapStr{ + "state": getDomainStateName(state), + }, + }, + }) + if !reported { + return nil + } + } + + return nil +} + +func getDomainStateName(tag libvirt.DomainState) string { + switch tag { + case 0: + return "no state" + case 1: + return "running" + case 2: + return "blocked" + case 3: + return "paused" + case 4: + return "shutdown" + case 5: + return "shutoff" + case 6: + return "crashed" + case 7: + return "suspended" + default: + return "unidentified" + } +} diff --git a/metricbeat/module/kvm/status/status_test.go b/metricbeat/module/kvm/status/status_test.go new file mode 100644 index 00000000000..e484cd775be --- /dev/null +++ b/metricbeat/module/kvm/status/status_test.go @@ -0,0 +1,69 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package status + +import ( + "testing" + + "github.com/digitalocean/go-libvirt/libvirttest" + "github.com/stretchr/testify/assert" + + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" +) + +func TestFetchEventContents(t *testing.T) { + conn := libvirttest.New() + + f := mbtest.NewReportingMetricSetV2Error(t, getConfig(conn)) + + events, errs := mbtest.ReportingFetchV2Error(f) + if len(errs) > 0 { + t.Fatal(errs) + } + if len(events) == 0 { + t.Fatal("no events received") + } + + for _, e := range events { + if e.Error != nil { + t.Fatalf("received error: %+v", e.Error) + } + } + if len(events) == 0 { + t.Fatal("received no events") + } + + e := events[0] + + t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), e) + + statName, err := e.MetricSetFields.GetValue("stat.state") + if err == nil { + assert.EqualValues(t, statName.(string), "running") + } else { + t.Errorf("error while getting value from event: %v", err) + } +} + +func getConfig(conn *libvirttest.MockLibvirt) map[string]interface{} { + return map[string]interface{}{ + "module": "kvm", + "metricsets": []string{"status"}, + "hosts": []string{"test://" + conn.RemoteAddr().String() + ":123"}, + } +} diff --git a/metricbeat/module/logstash/fields.go b/metricbeat/module/logstash/fields.go index a1f6963e9c0..bf8f9746c58 100644 --- a/metricbeat/module/logstash/fields.go +++ b/metricbeat/module/logstash/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetLogstash returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/logstash. +// This is the base64 encoded gzipped contents of module/logstash. func AssetLogstash() string { return "eJyslM2OmzAQgO88xYhzwwNw6KmtmqpV95TLarWyYALeGA/yDKzy9isCZMEx+d055OCJP3/jGbOCHe5TMFSwKC4jANFiMIX477AURwA5cuZ0LZpsCt8jAIAxDRXljcEIwKFBxZhCoSIARhFtC07hOWY28TeIS5E6fokAthpNzumBswKrKpwZdCH7uiM5auphJeAwJ01plnI8LoZoi8Q+vP3z0sbwD58KlMQyS4wSymjFXqZWUvZbku6nI3j/qHThVC8qrvGzZwrp4jexwAl0NG3RsSZ7oyyja3WGSXj3Q7rHydoE2KP1W1sFjf0eX3Hen80/WNsteYlQdy/f26fJDvfv5PJA/oJPF6HSp4fXOgRebhocG1c7ypA5CRPON+5K+af+CFj/CL7LhEUJP/o6Xw8UqFCczjh56LFii1b8K7t7nn4eaOBXuSQxFdHL42TIFve1Y20zqrQthjIho8YKumTRghr/0/UVGv8bKegWja02gg6XB/1+l18D+sTlIwAA//9sYbU7" } diff --git a/metricbeat/module/memcached/fields.go b/metricbeat/module/memcached/fields.go index d9fa12ddab8..9e213961d65 100644 --- a/metricbeat/module/memcached/fields.go +++ b/metricbeat/module/memcached/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetMemcached returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/memcached. +// This is the base64 encoded gzipped contents of module/memcached. func AssetMemcached() string { return "eJzEVj2TGzcM7fUrMGpsz9j6ASrSJE2KpHJ/Q5FYiTmS2BCgZP37DMhdfcerS9a+be7EXT68B+CB/AKveFxDxGiN3aFbAIiXgGtY/jGuLRcADtlm34untIZfFgAAp/cQyZWACwDeUZYXS6nz2zV0JrCuZgxoGNewNQuAzmNwvK4QXyCZiNfh9ZFjr59nKv2w8iD+NdQlHIsRPq0+gvtXyPbcAlwrGJ/b8JcUeu+u1kcSgdL25sV3eOjza8kZk0CfySIz/P4bUAeyw4sCiOHX1WnnQ0KlFx9xxWhn4nWOzpj3mMcI36chu4zG8Uwc/ixxg7nlo+JCYXSwOdb82CFzd0yHVE5QtZQSWg3NqwFqdtrUY7oMBEIgO893nD+rt0pwsMGqjU3EO9y9CQWBEpgQhn0MrmSftpAoRxM0YDYa6g3ihcSE+aQ35Vys1qArYYwFRgRjL+ckDOVinyyCF9gZhg1iUn9mQTehYYuy2nmZv9kuuC+3KEuwFKNJjuFjLRpo1E8D78q19J/B+b13tXrxDnvo2KWN7qUitlIKqYjWzCNwNjJlMhUePTPOL70zPqAbZGf8uyCLFsWawgiJZKfNdjAMQ/t2lFsxX/F4h1vf4fhp06x7hQgoTNXXRrfa4vymvKlpRot+r05s9RyaspVVJYOlkkR1+zqY73UeMONl11DWfU/I4x8hj6/kqR5vz/KargluOmxXm6M832Gdzh/BNTzaNcH9qw4gSCcFFeJcly5TrH2UUA6UX5ubTvNjQsohexFM762GMbk2+P6jDi8Yf9xBVdHHEzUcwadLah8Guz9F8U3Hyf9J9C17FsroAJt96/90JWMFX/WHZ0gEy2i++Vhi3XyHXS0/julNETBtCR34ZLPeFus9RIMdIeGhwoxha/bwqZzh3rdj+OdmjDZ/oRU1WaQrj7VzSAi6jAil17s75WOd86PM26hwOiDO15qM7a+XDwxjqgcsRqnj9GPw0ctLNN+qlk8TqaofvdUB86Srefhsj3oN1ZSo2noy1NZ/hn/V/B7sL69cnvUOSQesM0krp2K0e80WV4t/AgAA//8M190C" } diff --git a/metricbeat/module/mongodb/fields.go b/metricbeat/module/mongodb/fields.go index ae38c9b8724..8ee97315e84 100644 --- a/metricbeat/module/mongodb/fields.go +++ b/metricbeat/module/mongodb/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetMongodb returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/mongodb. +// This is the base64 encoded gzipped contents of module/mongodb. func AssetMongodb() string { return "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" } diff --git a/metricbeat/module/munin/fields.go b/metricbeat/module/munin/fields.go index 4201760b398..0d105e45fc3 100644 --- a/metricbeat/module/munin/fields.go +++ b/metricbeat/module/munin/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetMunin returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/munin. +// This is the base64 encoded gzipped contents of module/munin. func AssetMunin() string { return "eJx8kUFuwyAURPc+xSibSJHiA7DoDdorRNhMKA0GBN9qffvKhlaOanX5Z/5oHp8rHlwUpjm40AHixFPh9LrOpw4wLGN2SVwMCi8dAGweQjTERMluLOBXilmYOyDTUxcqWN0Bd0dvitpyVwQ9sXX1LdpfNg+QJVEhDh8cpUl1uFXHxHnw/OvcJp2SC7atnS/ntnNAvtHvkAsNhgUayc/WBcQ7dMWrz9OWQfoD+Lrfr9IT/oPLZ8zmf4I3PXGtknf+FI/Re47igl3V8nvYg+6nPpvjnJqyP3VN7b/iOwAA//84wJVl" } diff --git a/metricbeat/module/mysql/fields.go b/metricbeat/module/mysql/fields.go index 166d37fe757..283f3e4df4b 100644 --- a/metricbeat/module/mysql/fields.go +++ b/metricbeat/module/mysql/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetMysql returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/mysql. +// This is the base64 encoded gzipped contents of module/mysql. func AssetMysql() string { return "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" } diff --git a/metricbeat/module/nats/fields.go b/metricbeat/module/nats/fields.go index 215ffc0c5d2..39228af4d46 100644 --- a/metricbeat/module/nats/fields.go +++ b/metricbeat/module/nats/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetNats returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/nats. +// This is the base64 encoded gzipped contents of module/nats. func AssetNats() string { return "eJzUmM+S2zYMxu9+CkxO7SH7AD50ptNecugeuul5Q1OwzIlIKADojfP0HUqWLdGSLTdKtqujKeP76QP4B3wPn/GwhmBUVgDqtMI1vHs0Ku9WAAWKZVero7CG31YA0LwJf1ERK1wBMFZoBNdQmhXA1mFVyLp57z0E4/EUOT16qNObTLE+/jISPz2f0p8+gaWgxgUBUaNO1FkB3RmFF2QERlPAlsnD41miT9CnEOQ98oMrTiMdzmc8vBD3f5+ASs/HHR5DwYc/p0TUebyQKYziPI2nNn6KArQFj8rOgmU06e0LUUshoE1DciHat/qG6h+d101+z0FTho1iceToawxz3z15BvqsSmqqwUhH6oJiiZyNXeHtshGi3yAno2xkxqDVAYxVt0ewlcOgcuEYU1Rc0Kwm3tvxibF0oshY5E6c6liNLujP3vCi7sQ6m2FnxopCmQ1sib3RNRSRhxNotnU1sqMC9Dz3nUCs4RdB++sooUf/sDkMi2wW5NifZhAeSz/pEh8giimbtePx949PUDNZFBkFtcQTkAsUWkWls6ZqRRr7+jzAMQhk+Thx1XGUSqypsHjeVmR0wsIa2WLIR+8w0dZx4KBcdbCZqs/jizBcS/ldVjYq1aHdBbC4WNr6RIyepkpv4eVjRKmjcPlEG19EZsobTzFoknfBknehTBuqyUtgZNGAwbSUlNfcGriWphmAVyAnFTuksQl/k+faavF9vJcRO1CK+ZRaKKEUtaT/e0JPkG8koSfe6YRKRS9p3ZLokX/MqpUk4CTRO6BRaLaCUa6dal5P31VpKV537Djtie0+fme1MX55zo9GtxFnYHaojF8iivb7nbFimcDso0Z2o+O3QGfCZsBQOFF2m9g0DBTAU3BKnArwn78/PE3EuPYhkJ/Xv02+dmNu3flZcFHFO6fpnHLE6H/btMvD9uz12RuKe9H3hl+fPEHcCy5xI69P3lDci85E+T7788kTxBR43+NOZcFecRD27fTUxxuHKVP6x2JBvthBltlu29hANbadroALmaGVE51sGvbz+9U7O4YU+79xeaN294O41HkUMK1G6um3FEORDmNgBnzjLaqxO3wQ9238JmKRTktipSCojWOpgW5Er+DsnD7z8KYRfkrznES7ZIFhhA2mlYPTxMU9Hi9qb+FvTaCoD958XdZTb746Hz208dvTXwGbw1wgs88L7XipS3FT5WbP6X72yKbEKZ5/AwAA//9SJQAz" } diff --git a/metricbeat/module/nginx/fields.go b/metricbeat/module/nginx/fields.go index 3cd6e0856ee..e848db4b940 100644 --- a/metricbeat/module/nginx/fields.go +++ b/metricbeat/module/nginx/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetNginx returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/nginx. +// This is the base64 encoded gzipped contents of module/nginx. func AssetNginx() string { return "eJzElM9u2zAMxu9+ig8+r3kAH/YIOw3YYRhSVaJtIYrkkXTSvP0gR26cwC1SoMF0skWJ349/xCfs6NQgdj6+VoB6DdSg/pH/6wpwJJb9oD7FBt8rAJhsEOIDMUSNjoI9KXsrsCkEskoOLac9DoZ9yubkxkCyqQDpE+vWptj6rkFrglAFMAUyQg06k8+Qqo+dNPhdi4T6G+pedaj/VEDrKThpJpInRLOnC31eehqyG07jUHZWQsjrebr1DJuiGh8F2tNbHNobxZGYIJbNMMczXdkUF0uSJY3o+HJOy5tpDesDtAnv4uZzjPlI7F63OWPb7GR79rI9F2Gu2GA62iwUryswr9sYl3H2STR/XRnnSHd0OiZ2N7YP4r101ux3s6pqrPrDumZIsfuc4M+eYEdmioo47l+IkdoiARt83rcpRrLZg8BHG0bnY4dfxucWXVrf47U0qHwhsCY14Qo3K5BbAV5H6k10gW5r85VIReFuIsdpGB5KVBTuJmL6O5I8tG4FZVZa5yjd+dB+vwuEyeTGfyzI4qkd+zzZzhPByyw/TbcCip6MI17HPfL0Pv8XbpEvuDKkKIQXY3fQNG2ec/4Ou3k8u3dhdcIVbbSJYeZMb6p/AQAA//9pr1BQ" } diff --git a/metricbeat/module/php_fpm/fields.go b/metricbeat/module/php_fpm/fields.go index 7293bc9e2e2..1150b149093 100644 --- a/metricbeat/module/php_fpm/fields.go +++ b/metricbeat/module/php_fpm/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetPhpFpm returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/php_fpm. +// This is the base64 encoded gzipped contents of module/php_fpm. func AssetPhpFpm() string { return "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" } diff --git a/metricbeat/module/postgresql/fields.go b/metricbeat/module/postgresql/fields.go index 065acbda723..75826021f79 100644 --- a/metricbeat/module/postgresql/fields.go +++ b/metricbeat/module/postgresql/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetPostgresql returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/postgresql. +// This is the base64 encoded gzipped contents of module/postgresql. func AssetPostgresql() string { return "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" } diff --git a/metricbeat/module/prometheus/fields.go b/metricbeat/module/prometheus/fields.go index d8b0eb230f4..e93b578c7b7 100644 --- a/metricbeat/module/prometheus/fields.go +++ b/metricbeat/module/prometheus/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetPrometheus returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/prometheus. +// This is the base64 encoded gzipped contents of module/prometheus. func AssetPrometheus() string { return "eJzMkkGO2zAMRfc+xYe7G2TmAF70BAXaosuiCBT7O1ZHllSSniC3LxzHGU0yQJF2Uy75RfLxi4945rFBljTSBk5aAeYtsEH95ZKsK6CjtuKz+RQbfKwA4Js5U2grLrNDL2mEw2sVGLucfLSnCtAhiW3bFHu/b9C7oKwAYaBTNti7+Q3NfNxrg++1aqg3qAezXP+ogN4zdNqc5j4iupFX1HPYMc+9JE35nCnL5viAz9JR4BV+zEnMRcNA4QbB7RgUBx8CRmftgN6L2gY2EEI1OCG6NO0CL/1WlKX46eEirDBp95OtFeklsV3UZx4PSbpCfsfmNQpnR5r49jz1BmZR76e52u2Nuh1dzj7uz0/rh/ovoW9of02U4//G+uLCdPr1Kdh627P89VPB//Z639nqZqfyNP8Ac2qwfiVLHy5jdzRX5K9vfUURjsm4PYg3/gvR0genPivYqzNn45TyQrmD9ncAAAD//1baTA8=" } diff --git a/metricbeat/module/prometheus/remote_write/_meta/docs.asciidoc b/metricbeat/module/prometheus/remote_write/_meta/docs.asciidoc index 386f7e5729d..2f46dc6e47c 100644 --- a/metricbeat/module/prometheus/remote_write/_meta/docs.asciidoc +++ b/metricbeat/module/prometheus/remote_write/_meta/docs.asciidoc @@ -8,7 +8,7 @@ remote_write: ------------------------------------------------------------------------------ -TIP: In order to assure the health of the whole queue, the following two configuration +TIP: In order to assure the health of the whole queue, the following configuration https://prometheus.io/docs/practices/remote_write/#parameters[parameters] should be considered: - `max_shards`: Sets the maximum number of parallelism with which Prometheus will try to send samples to Metricbeat. diff --git a/metricbeat/module/rabbitmq/fields.go b/metricbeat/module/rabbitmq/fields.go index e737f5f5808..07ea6fba3fe 100644 --- a/metricbeat/module/rabbitmq/fields.go +++ b/metricbeat/module/rabbitmq/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetRabbitmq returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/rabbitmq. +// This is the base64 encoded gzipped contents of module/rabbitmq. func AssetRabbitmq() string { return "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" } diff --git a/metricbeat/module/redis/fields.go b/metricbeat/module/redis/fields.go index 5ec77a24318..ccdb515cba3 100644 --- a/metricbeat/module/redis/fields.go +++ b/metricbeat/module/redis/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetRedis returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/redis. +// This is the base64 encoded gzipped contents of module/redis. func AssetRedis() string { return "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" } diff --git a/metricbeat/module/system/fields.go b/metricbeat/module/system/fields.go index 472019559a2..28135f5fa95 100644 --- a/metricbeat/module/system/fields.go +++ b/metricbeat/module/system/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetSystem returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/system. +// This is the base64 encoded gzipped contents of module/system. func AssetSystem() string { return "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" } diff --git a/metricbeat/module/system/filesystem/_meta/docs.asciidoc b/metricbeat/module/system/filesystem/_meta/docs.asciidoc index dd10e665cf7..97029f24a83 100644 --- a/metricbeat/module/system/filesystem/_meta/docs.asciidoc +++ b/metricbeat/module/system/filesystem/_meta/docs.asciidoc @@ -48,6 +48,6 @@ metricbeat.modules: period: 30s metricsets: ["filesystem"] processors: - - drop_event.when.regexp: - system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host)($|/)' + - drop_event.when.regexp: + system.filesystem.mount_point: '^/(sys|cgroup|proc|dev|etc|host)($|/)' ---- diff --git a/metricbeat/module/traefik/fields.go b/metricbeat/module/traefik/fields.go index d18ad7398e8..19bd3b052b9 100644 --- a/metricbeat/module/traefik/fields.go +++ b/metricbeat/module/traefik/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetTraefik returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/traefik. +// This is the base64 encoded gzipped contents of module/traefik. func AssetTraefik() string { return "eJy00bFu2zAQBuBdT/HDS4ECdncNBTx2aFEUzWxQ1ElmLPGIO8qI3z6gLDmyojhGgtzIo3if/lvjQKccUQxV7pAB0cWGcqyGk1UGlKRWXIiOfY6fGQD8P3chdCRRQhB+OuEHGjYlCtMYb0nQUhRnNQOEGjJKOWqTAZWjptS8f2kNb1qaClLFU0iXhbswnCwgPgJJNR0/JezJNHF/OV5S3JCk+n2eAy6icZ5KVMLtaPymwwRs//4C+TKw83Hy/XVIY821U3EXomtpo2Sv2qO8YV/PGjfwqR76B8HVJVjnNaYQ4TyULPtSFylCGtgrLULmEd4h+Te8N9vdrVSmHMvdVbbvBnMHKdWfri1IUkDjD89lLwZzrHf9frrXdz4v2R5JTE0XB/rNOY/WWeGlVU1tGk3sdGe5JN18f5PHxSPZpRzPjd0XxIlAMvCQeNlzAAAA//9iMTnZ" } diff --git a/metricbeat/module/uwsgi/fields.go b/metricbeat/module/uwsgi/fields.go index 120cb4a8a24..78247b12e18 100644 --- a/metricbeat/module/uwsgi/fields.go +++ b/metricbeat/module/uwsgi/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetUwsgi returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/uwsgi. +// This is the base64 encoded gzipped contents of module/uwsgi. func AssetUwsgi() string { return "eJzEl8tu6zYQhvd+ikFWLWAb7daLAgGSRRdtUzvnslNocWQTpkhlOLQiP/0BdfFVVowDGoc7yfT/f8PLzGgCG6xm4Eu3UiMAVqxxBg/188MIQKJLSRWsrJnBXyMAaOZCbqXXOAIg1CgczmAlRgCZQi3drJ44ASNyPIiHwVURppL1Rfumx+FU5ljKsWDv9q/79MI4ZepGr1MzasRpow45MqnUIbcYRzPPuY7Z2LLQU8J3j47dyZSOU1uzOvthACqM1yAKnSishZEa5YA/fqRYq8UluCJ76l2SYkyQyNKd4q8doMfhfBOEvA+HkJ+7F0pGcn0hm6JzoPp3vLS0QZpGs/tW633iFi+61q64KUiR1gfwwuNnzRcsGMFmrf4Y/gSVgWOlNey9wGB5OHyW10ilcgh/DJFGzgD/+nyJFEj3IA5pixKWFfBaudZ3CEmiZpH8IjAQGSN1D8qF7GyFRAnlGk24U+kaJfzz+D2ZP///5XnxuhiKJXqCe94LAgnlrmTX1n0tSGwUqSS13nAkgieyRYHysI5hAVWO1vMQi1MrI3S0ZcgVM0poVeEywF775N2jx+hHqqMohaovIltY4mD566jOO4QDzwar0tJ59rotSbWtwW/pGkUxhkJ4h+NAOYald9UYlNT4+2BacBGp5uiURMOwQIaF2uEUcswtVZB6IjSsK/CuuYqiy7DTkOGELkXlYIdkgamCt8mk+eeEsLDEb2Br17AJx41bb0xbt4u0818VsRf6Mh6ndgjChfOAMhyDO4ZD3hhlVkm4e5EreCsNF9IXxcMVojRR08u80fz8RvNHrG6JhHH7fLIbjFhsVwnFCvVxiyRWCGEZrXF4fb1TSxivc2qL299P172aKUnE5lCEi95Zvwx5d4VlWjeo0ZM1nzbpTRdwA01IqiqNXztqWciUxoalv1k/ZSHrGWNtzbwWg96Gq9/eZlnTFEUi+K/TuwHiDp9th83IhNKh1QsegwsR+4vtAiFYuNGPAAAA//9dpnuJ" } diff --git a/metricbeat/module/vsphere/fields.go b/metricbeat/module/vsphere/fields.go index 1490fb00f37..b0588e42a72 100644 --- a/metricbeat/module/vsphere/fields.go +++ b/metricbeat/module/vsphere/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetVsphere returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/vsphere. +// This is the base64 encoded gzipped contents of module/vsphere. func AssetVsphere() string { return "eJzMls1u2zoQhfd+ikH28QNocTe5SLtxWyBNtwYjjSzWooYgRwnUpy9ISoZ+6CapqLZcGLBInfNJ1JzhLZyxy+DZ6goN7gBYco0Z3Dw/+Cs3O4ACbW6kZklNBv/tAAD6WVBUtLW7zWCNwmIGJ7EDKCXWhc380ltohMKxhRvcabfYUKv7KxGXqdBYrBAsLNNFLi55VbafiohMn2MYc4wxivudTAwkZ+xeyBSzuV/wuPH/wLTUHQxL6/TTWd7LGm1nGRUshAfPXGiRS+72TCzq/VPHaKMENTWn99l/dYrgFYFK4AqjG+NGSUYJzmBpv+AsDWJSzHuDmJyytVgkpXy0WGxDqXNOyajR5NjwWyn75ZfZRSBUZHlNFszu/9sx8JEsX0+AXLdhU1T1I+Wm3H15BNnAYaY6tg31n843VP8bjH1Bp/P15fyKrUJFZqsiPXhxZx+Tfr06e7it8jgR3jYxvBauQX4hcz66f3Gw3yraT0EWprKX84803IpaibySzapzy1Wl96eWi729nD/n2uCaCU7M3M9GWTk0k1mWb5XR38I2wCHsw/W4poTf2Nz0s0YjWDYneAhHuH+taYxD9NSi5c2ilEr44AxWh5Zn9R/rhqihUJKkf/rXOukByd6rbwbpYcctYT1r3lomdQyhGSWkp++4OAyHi8cVlX3njSFi/Efb1s8AAAD//5A1D00=" } diff --git a/metricbeat/module/windows/fields.go b/metricbeat/module/windows/fields.go index 1087083cc13..2cf2c008578 100644 --- a/metricbeat/module/windows/fields.go +++ b/metricbeat/module/windows/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetWindows returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/windows. +// This is the base64 encoded gzipped contents of module/windows. func AssetWindows() string { - return "eJysVlFv2zYQfvevOOSlQJEYa/s0PxTw4mXzsKRFk8IYEMCiqZN1C0WqvKMdA/vxAyXZkW05dozqoVB51Hffd99HM1fwhKsBLMmmbsk9ACExOICLSb1y0QNIkbWnUsjZAXzuAQDcujQYhMx5mGw+5dx5mWpnM5oPIFOGsQfg0aBiHMBc9QAyQpPyoAK5AqsKbDePj6zKuNm7UDYrHf23gdpgJfqscHaz3gUYnw2tGYpqrXc2q5996F0SLzTIsiircQugZvKEq6Xz6VZlq+t/WyWAcYMFC2UC9ju6FSieNPff9993NHSzf1HLVqFemtb1zDh1sDwtVFmSnTd7L95fHCb+eYf4bUWrps3gUYK3mL4I2DOP0S9oa2jd5r3SNWkwEtDOiiLLIDkCi5LA7cCum3G/KxVz9arNLaPTHdGHTQbAZ1WU8Xjlk093f93oD8NyZ8er8wQYQrD0IyCMR5WWSlqtow9jAWJQkCvOwWVVsVA6J4vvGP74Ph6Bsmlc3sNtMCpNHf60Bcd/z5E8Qc0L/Ta5Dy/yTqGWEpdGraZnU2yS8fsCrcC1Mwa1OP92zg2RitbaibVNr0tgUb4+eG8QcMoUI24oK5hdRtWG0jHTzOD6tCqPkAyDuEIJ6eRyDzX5zTlJLiEZEauZwTS+3yoblEkuq6Al9ysWLJKTJJ/r2d0DDL8//Pnl2/jhn8e/nVbmfu9H5IQZDbV2wUrtWLApeljmpHNQmwD6YPmIlFJJfraS68HjZHw3+jK5f+RqcJ8+PvJC546lj88IV0/Q1gdXb/ztuAnGrOBHUIYywrQiC+KqKGRkECRXAhTJFGiF2xnZt5+sNiElOwfl56H64LjP8pNTrbQEZWrk01N97ayQDWTnXbH+qgJXpfq1zvW3YG2zeB/junl31eW4CXz8P6bHEo/PFP9OSn/iNG6cf2m/udpAcuJ4KcTBoPfOQ2xbO73JNZbOC+9BLnO09eGMLosDbtTG0RLDkoyBGVbYc7QYr/qdu3UPs8UhWIO8FTIovVtQGm1aL11xiZoy0q0vj53BAzeycXZ+6Oh9+OXXj2fMe52K7nkrZqdJSTxq3uko9ut4dIR9KIUK7Be7dhzUkDlfKBlAGryKZHfKZMsg0/WmgowhRu1sutvg9Jv4HTcsoTEHUyC7hd3v/R8AAP//KJpZdg==" + return "eJysVl1v2zYUffevuMhLgSIx1vZpfiiQxcvmYUmLJoUxIIDFkFfWXShS5b20Y2A/fqAkO5Y/YscoHwKFpM49555D0RfwhIsBzMkZP+cegJBYHMDZuJk56wEYZB2oEvJuAJ97AAA33kSLkPsA49WrXPggE+1dTtMB5Moy9gACWlSMA5iqHkBOaA0PapALcKrE9eJpyKJKm4OPVTuzo34XaB2swpCX3q3mdwGm0aW1HDtLNWMbeJPCCwlyLMpp7AA0PJ5wMffBdFY6Vf/rLAGMWiyYKRuxv6NaiRJIc/99//2Ogv7xX9TSWWimJs16br3auzwpVVWRm7Z7z96f7Sf+eYP4TU2roc0QUGJwaF4EbFnHGGbUadpu616pmrUYGWjvRJFjkAKBRUnk9bgui3H/YCa2bV4z2myI3m8yAD6rskqHqxh/uv3rWn+4rDZ2vNpPgEuIjn5EhNGw1lJLa3T0YSRADAoKxQX4vF4slS7I4TuGP76PhqCcSdNbuC1GrWmHP+uC099TJI9R80y/Te79i7xjqBniyqrF5GSKbTJ+n6ETuPLWohYf3s65JVLTWjqxtOl1CSwqNAfvDQKO6WLCjVUNs8mo3lB5Znq0uDytKiBkl1F8qYR0dr6Fmv3mvWTnkA2J1aNFk55vlIvKZud10LK7BQuW2VGST/Xs9h4uv9//+eXb6P6fh7+9VvZu6yNyRI8utfbRSeNYdAYDzAvSBahVAEN0fEBKpaQ4WcnV4GE8uh1+Gd89cN24Tx8feKYLz9LHZ4SLJ1jXBxdv/HZcR2sX8CMqSzmhqcmC+DoKOVkEKZQAJTIlOuH1jGzbT07baMhNQYVprF847LP85FQrLVHZBvn4VF95J+QiuemuWH9Vkeul5rHJ9bfoXDt5l+K6evb15bgKfPofzaHE4zOlX0nmJ3bj2oeX8qurDaQgTpdCagyG4AOkso3Tq1xj5YPwFuS8QNcczuSyeOBWbWotMczJWnjEGnuKDtNVv3G3bmGucYjOIndCBlXwMzLJpuXUBVeoKSe99uahM7jnRrbeTfcdvQ+//PrxhH4vU7G734rZa1KSjlrwOon9OhoeYB8roRL75aYdezXkPpRKBmBiUInsxjK5Kspkuakka4lRe2c2Cxx/E7/jliW05qABch3sfu//AAAA//8t/Vii" } diff --git a/metricbeat/module/windows/perfmon/_meta/fields.yml b/metricbeat/module/windows/perfmon/_meta/fields.yml index 9e07225e17d..177af776297 100644 --- a/metricbeat/module/windows/perfmon/_meta/fields.yml +++ b/metricbeat/module/windows/perfmon/_meta/fields.yml @@ -1,6 +1,6 @@ - name: perfmon type: group - release: beta + release: ga description: > perfmon fields: diff --git a/metricbeat/module/windows/perfmon/data.go b/metricbeat/module/windows/perfmon/data.go index 7db0a338de2..68de068ed05 100644 --- a/metricbeat/module/windows/perfmon/data.go +++ b/metricbeat/module/windows/perfmon/data.go @@ -38,54 +38,52 @@ var processRegexp = regexp.MustCompile(`(.+?)#[1-9]+`) func (re *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.Event { eventMap := make(map[string]*mb.Event) for counterPath, values := range counters { - hasCounter, counter := re.getCounter(counterPath) - for ind, val := range values { - // Some counters, such as rate counters, require two counter values in order to compute a displayable value. In this case we must call PdhCollectQueryData twice before calling PdhGetFormattedCounterValue. - // For more information, see Collecting Performance Data (https://docs.microsoft.com/en-us/windows/desktop/PerfCtrs/collecting-performance-data). - if val.Err != nil && !re.executed { - re.log.Debugw("Ignoring the first measurement because the data isn't ready", - "error", val.Err, logp.Namespace("perfmon"), "query", counterPath) - continue - } - - var eventKey string - if re.config.GroupMeasurements && val.Err == nil { - // Send measurements with the same instance label as part of the same event - eventKey = val.Instance - } else { - // Send every measurement as an individual event - // If a counter contains an error, it will always be sent as an individual event - eventKey = counterPath + strconv.Itoa(ind) - } - - // Create a new event if the key doesn't exist in the map - if _, ok := eventMap[eventKey]; !ok { - eventMap[eventKey] = &mb.Event{ - MetricSetFields: common.MapStr{}, - Error: errors.Wrapf(val.Err, "failed on query=%v", counterPath), + if hasCounter, counter := re.getCounter(counterPath); hasCounter { + for ind, val := range values { + // Some counters, such as rate counters, require two counter values in order to compute a displayable value. In this case we must call PdhCollectQueryData twice before calling PdhGetFormattedCounterValue. + // For more information, see Collecting Performance Data (https://docs.microsoft.com/en-us/windows/desktop/PerfCtrs/collecting-performance-data). + if val.Err != nil && !re.executed { + re.log.Debugw("Ignoring the first measurement because the data isn't ready", + "error", val.Err, logp.Namespace("perfmon"), "query", counterPath) + continue } - if val.Instance != "" && hasCounter { - //will ignore instance counter - if ok, match := matchesParentProcess(val.Instance); ok { - eventMap[eventKey].MetricSetFields.Put(counter.InstanceField, match) - } else { - eventMap[eventKey].MetricSetFields.Put(counter.InstanceField, val.Instance) + var eventKey string + if re.config.GroupMeasurements && val.Err == nil { + // Send measurements with the same instance label as part of the same event + eventKey = val.Instance + } else { + // Send every measurement as an individual event + // If a counter contains an error, it will always be sent as an individual event + eventKey = counterPath + strconv.Itoa(ind) + } + // Create a new event if the key doesn't exist in the map + if _, ok := eventMap[eventKey]; !ok { + eventMap[eventKey] = &mb.Event{ + MetricSetFields: common.MapStr{}, + Error: errors.Wrapf(val.Err, "failed on query=%v", counterPath), + } + if val.Instance != "" { + //will ignore instance counter + if ok, match := matchesParentProcess(val.Instance); ok { + eventMap[eventKey].MetricSetFields.Put(counter.InstanceField, match) + } else { + eventMap[eventKey].MetricSetFields.Put(counter.InstanceField, val.Instance) + } } } - } - event := eventMap[eventKey] - if val.Measurement != nil { - event.MetricSetFields.Put(counter.QueryField, val.Measurement) - } else { - event.MetricSetFields.Put(counter.QueryField, 0) - } - if counter.ObjectField != "" { - event.MetricSetFields.Put(counter.ObjectField, counter.ObjectName) + if val.Measurement != nil { + eventMap[eventKey].MetricSetFields.Put(counter.QueryField, val.Measurement) + } else { + eventMap[eventKey].MetricSetFields.Put(counter.QueryField, 0) + } + if counter.ObjectField != "" { + eventMap[eventKey].MetricSetFields.Put(counter.ObjectField, counter.ObjectName) + } } } } // Write the values into the map. - events := make([]mb.Event, 0, len(eventMap)) + var events []mb.Event for _, val := range eventMap { events = append(events, *val) } @@ -98,30 +96,34 @@ func (re *Reader) groupToSingleEvent(counters map[string][]pdh.CounterValue) mb. } measurements := make(map[string]float64, 0) for counterPath, values := range counters { - _, readerCounter := re.getCounter(counterPath) - for _, val := range values { - // Some counters, such as rate counters, require two counter values in order to compute a displayable value. In this case we must call PdhCollectQueryData twice before calling PdhGetFormattedCounterValue. - // For more information, see Collecting Performance Data (https://docs.microsoft.com/en-us/windows/desktop/PerfCtrs/collecting-performance-data). - if val.Err != nil && !re.executed { - re.log.Debugw("Ignoring the first measurement because the data isn't ready", - "error", val.Err, logp.Namespace("perfmon"), "query", counterPath) - continue - } - var counterVal float64 - switch val.Measurement.(type) { - case int64: - counterVal = float64(val.Measurement.(int64)) - case int: - counterVal = float64(val.Measurement.(int)) - default: - counterVal = val.Measurement.(float64) - } - if _, ok := measurements[readerCounter.QueryField]; !ok { - measurements[readerCounter.QueryField] = counterVal - measurements[readerCounter.QueryField+instanceCountLabel] = 1 - } else { - measurements[readerCounter.QueryField+instanceCountLabel] = measurements[readerCounter.QueryField+instanceCountLabel] + 1 - measurements[readerCounter.QueryField] = measurements[readerCounter.QueryField] + counterVal + if hasCounter, readerCounter := re.getCounter(counterPath); hasCounter { + for _, val := range values { + // Some counters, such as rate counters, require two counter values in order to compute a displayable value. In this case we must call PdhCollectQueryData twice before calling PdhGetFormattedCounterValue. + // For more information, see Collecting Performance Data (https://docs.microsoft.com/en-us/windows/desktop/PerfCtrs/collecting-performance-data). + if val.Err != nil && !re.executed { + re.log.Debugw("Ignoring the first measurement because the data isn't ready", + "error", val.Err, logp.Namespace("perfmon"), "query", counterPath) + continue + } + if val.Measurement == nil { + continue + } + var counterVal float64 + switch val.Measurement.(type) { + case int64: + counterVal = float64(val.Measurement.(int64)) + case int: + counterVal = float64(val.Measurement.(int)) + default: + counterVal = val.Measurement.(float64) + } + if _, ok := measurements[readerCounter.QueryField]; !ok { + measurements[readerCounter.QueryField] = counterVal + measurements[readerCounter.QueryField+instanceCountLabel] = 1 + } else { + measurements[readerCounter.QueryField+instanceCountLabel] = measurements[readerCounter.QueryField+instanceCountLabel] + 1 + measurements[readerCounter.QueryField] = measurements[readerCounter.QueryField] + counterVal + } } } } diff --git a/metricbeat/module/windows/perfmon/perfmon.go b/metricbeat/module/windows/perfmon/perfmon.go index c0490a34430..7f4712a5f3b 100644 --- a/metricbeat/module/windows/perfmon/perfmon.go +++ b/metricbeat/module/windows/perfmon/perfmon.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" - "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/metricbeat/mb" ) @@ -43,8 +42,6 @@ type MetricSet struct { // New create a new instance of the MetricSet. func New(base mb.BaseMetricSet) (mb.MetricSet, error) { - cfgwarn.Beta("The perfmon metricset is beta") - var config Config if err := base.Module().UnpackConfig(&config); err != nil { return nil, err diff --git a/metricbeat/module/windows/service/_meta/docs.asciidoc b/metricbeat/module/windows/service/_meta/docs.asciidoc index bba2cd8e8f6..21ef4b45047 100644 --- a/metricbeat/module/windows/service/_meta/docs.asciidoc +++ b/metricbeat/module/windows/service/_meta/docs.asciidoc @@ -32,6 +32,6 @@ except for the events for the firewall service. See metricsets: ["service"] period: 60s processors: - - drop_event.when.not.equals: - windows.service.display_name: Windows Firewall + - drop_event.when.not.equals: + windows.service.display_name: Windows Firewall ---- diff --git a/metricbeat/module/zookeeper/fields.go b/metricbeat/module/zookeeper/fields.go index 9ab4ca9a74b..0087aaab85b 100644 --- a/metricbeat/module/zookeeper/fields.go +++ b/metricbeat/module/zookeeper/fields.go @@ -30,7 +30,7 @@ func init() { } // AssetZookeeper returns asset data. -// This is the base64 encoded gzipped contents of ../metricbeat/module/zookeeper. +// This is the base64 encoded gzipped contents of module/zookeeper. func AssetZookeeper() string { return "eJy0WM2O2zgMvucpiF56afMAc1ig2O6hWLSLLbqXXlxGZmJhZNGV6PzM0y8kW/5J7CQz4/EpiCx+Hyn+fPJHeKTTAzwxPxJV5FYAosXQA7z7yfx3/O/dCiAnr5yuRLN9gD9WAADdOpQkTisPio0hJZTD5gRSEGy5dh8NiYSX2Gphp+0OFJcl2tyvVwC+YCeZYrvVuwfYovG0AnBkCD09wA5XAFtNJvcPEfUjWCxpzDg8cqrC647rqv1ngnJ4fnU7f4FiK6itj2STF44qdq0TnY/d9iH38Ay5DfkptpZUAO+WpkiGZ+xsemboNxySbT/4/5zJkI22Qo68ZFz50QuJk2G7O1u4QiA8X1qTcG4yYf6uqaZ8IbR/o7EZz3tMR4r0fjHU7625m7ierCyE+WcPNTabsEor7mZOXQH5FQzckfvnBdzsO0vEWAzrm8l8LTcL9hJ+TcbvkU4HdufneSOEfWdKtteTyFhVjo+6RKEsR8HM66dpGs8/xk+9bQhmgbcDXgFtmpNBIatOa9yfA76YyZ4c7ihZhg3JgcgCWU/lxlAMkgdtodTGaE+KB/1tzI6qgkpyaHymuF4s57/V5YZcCFEHAE+Wc5qhsWVj+EBuqV7W43eWwYcYtWWgaufISozUNKMSj9lWG8oSErtFI/QVj7qsS7A9U20IejQPGJnnsGUXSffZVjlW5GdimRKuxOPCXFPC3ZVZHQ1tl6Kh7fNp2LrM0Og9ZXMtf4ksG9gG4cFJSYEC6LqMMyeIbKbJckX2TbPu23y2BexUHndmGtfiBW2u7S5z9LsmL8uHdgACCaQJqyXKQ7Q3lGj2c06Z2gu5ad4VqkcSv15YXPSc+/hZkgO7xwTZ6ZnrxBZUH/eQCnAzhKg5XX+yavmjba1DtB5OUqFzp3Di4yLqBlvXzKfZenJ7cpkXlAXFx3c2FHrNuDASpxkmJ6soz95urDUAg+nWCb1DQRYQwrAdBQS0B0OYzxXFnpwfX256pmg0nvtQoRRNyLWi9fTuu2Vdux3Q5rCptcnBi2sqvnFrmvMBRRVv1CCjbfKDxu1JgJs8MKzQ3Nsmo/B5I5aNqLqQ+Tf5jUvmebeP982mF1w4vNvHi0pzyXj/qlvGa8f5YHgbTVaGJ93absZL8OIiTBc6Z5LDeRjnfBrau7wnXPXpwq90O8AyJFzwT3RJIPhItpOS7QkKgyNfsY2OYhuJNGZnKV4qy+dRTHryLSleqM5nUmy15sIUO3qcv3hCfQ2dPRDqGKzhS2id3Uz6AFJoDyWegLQU5IJEano/sOuGxhr+CYsH7ekDaAkDIiotNGxpWky/rpH9YEEzuPHE3nVLWb6yuDvJ2Hy9ApRR4H6EQNFREeUenshxMz8Hp9sqNg8lOxopUNsGDYM0EU5NdvnvWBPeJHupzV5pUC8WkxOwwdYdkO04z/I5ITaxMIL+HARLm+M/0+fdNCQGjVpbqP10rj4d9XS07yix/6z+XRPs0dQTNMSh9RhHD3z5HHKIIloYG16HMPE2lmPFqoiSBiEWTazUmDLkBTdG+2Iwspv6DK9rH7yK3SQnIVdqS/EdobJihwbY5RT1URhfBdrdTB29tlYHrvoUiJaobh2c+ax0vnIv7l8xaHdG/lMKstc7q7eaGi1SkdOcB4aHQocjSLUcirXlv/o/AAD//7suFxk=" } diff --git a/metricbeat/modules.d/kvm.yml.disabled b/metricbeat/modules.d/kvm.yml.disabled index 878e279b969..8450e1afc6d 100644 --- a/metricbeat/modules.d/kvm.yml.disabled +++ b/metricbeat/modules.d/kvm.yml.disabled @@ -4,5 +4,6 @@ - module: kvm #metricsets: # - dommemstat + # - status period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] diff --git a/metricbeat/scripts/assets/assets.go b/metricbeat/scripts/assets/assets.go deleted file mode 100644 index 51c24afe452..00000000000 --- a/metricbeat/scripts/assets/assets.go +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package main - -import ( - "flag" - "fmt" - "io/ioutil" - "os" - "path" - - "github.com/elastic/beats/v7/libbeat/asset" - "github.com/elastic/beats/v7/libbeat/generator/fields" - "github.com/elastic/beats/v7/licenses" -) - -func main() { - - flag.Parse() - args := flag.Args() - - if len(args) != 1 { - fmt.Fprintln(os.Stderr, "Module path must be set") - os.Exit(1) - } - - dir := args[0] - - modules, err := fields.GetModules(dir) - if err != nil { - fmt.Fprintf(os.Stderr, "Error fetching modules: %s\n", err) - os.Exit(1) - } - - for _, module := range modules { - files, err := fields.CollectFiles(module, dir) - if err != nil { - fmt.Fprintf(os.Stderr, "Error fetching files for module %s: %s\n", module, err) - os.Exit(1) - } - - data, err := fields.GenerateFieldsYml(files) - if err != nil { - fmt.Fprintf(os.Stderr, "Error fetching files for module %s: %s\n", module, err) - os.Exit(1) - } - - bs, err := asset.CreateAsset(licenses.ASL2, "metricbeat", module, module, data, "asset.ModuleFieldsPri", dir+"/"+module) - if err != nil { - fmt.Fprintf(os.Stderr, "Error creating golang file from template: %s\n", err) - os.Exit(1) - } - - err = ioutil.WriteFile(path.Join(dir, module, "fields.go"), bs, 0644) - if err != nil { - fmt.Fprintf(os.Stderr, "Error writing fields.go: %s\n", err) - os.Exit(1) - } - } -} diff --git a/metricbeat/scripts/mage/config.go b/metricbeat/scripts/mage/config.go index ffa30d27bad..e49e223753f 100644 --- a/metricbeat/scripts/mage/config.go +++ b/metricbeat/scripts/mage/config.go @@ -18,36 +18,25 @@ package mage import ( + "github.com/magefile/mage/mg" + devtools "github.com/elastic/beats/v7/dev-tools/mage" ) -const modulesConfigYml = "build/config.modules.yml" +const modulesConfigYml = "build/config.modules.yml.tmpl" func configFileParams(moduleDirs ...string) devtools.ConfigFileParams { collectModuleConfig := func() error { return devtools.GenerateModuleReferenceConfig(modulesConfigYml, moduleDirs...) } + mg.Deps(collectModuleConfig) - return devtools.ConfigFileParams{ - ShortParts: []string{ - devtools.OSSBeatDir("_meta/common.yml"), - devtools.OSSBeatDir("_meta/setup.yml"), - devtools.LibbeatDir("_meta/config.yml.tmpl"), - }, - ReferenceDeps: []interface{}{collectModuleConfig}, - ReferenceParts: []string{ - devtools.OSSBeatDir("_meta/common.reference.yml"), - modulesConfigYml, - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - }, - DockerParts: []string{ - devtools.OSSBeatDir("_meta/beat.docker.yml"), - devtools.LibbeatDir("_meta/config.docker.yml"), - }, - ExtraVars: map[string]interface{}{ - "UseKubernetesMetadataProcessor": true, - }, + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, devtools.OSSBeatDir("_meta/config/*.tmpl"), modulesConfigYml) + p.ExtraVars = map[string]interface{}{ + "UseKubernetesMetadataProcessor": true, } + return p } // OSSConfigFileParams returns the default ConfigFileParams for generating @@ -59,11 +48,5 @@ func OSSConfigFileParams(moduleDirs ...string) devtools.ConfigFileParams { // XPackConfigFileParams returns the default ConfigFileParams for generating // metricbeat*.yml files. func XPackConfigFileParams() devtools.ConfigFileParams { - args := configFileParams(devtools.OSSBeatDir("module"), "module") - args.ReferenceParts = []string{ - devtools.OSSBeatDir("_meta/common.reference.yml"), - modulesConfigYml, - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - } - return args + return configFileParams(devtools.OSSBeatDir("module"), "module") } diff --git a/metricbeat/scripts/mage/target/metricset/metricset.go b/metricbeat/scripts/mage/target/metricset/metricset.go new file mode 100644 index 00000000000..46bd2171295 --- /dev/null +++ b/metricbeat/scripts/mage/target/metricset/metricset.go @@ -0,0 +1,55 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package metricset + +import ( + "os" + "path/filepath" + + "github.com/magefile/mage/sh" + + devtools "github.com/elastic/beats/v7/dev-tools/mage" +) + +// CreateMetricset creates a new metricset. +// +// Required ENV variables: +// * MODULE: Name of the module +// * METRICSET: Name of the metricset +func CreateMetricset() error { + ve, err := devtools.PythonVirtualenv() + if err != nil { + return err + } + python, err := devtools.LookVirtualenvPath(ve, "python") + if err != nil { + return err + } + beatsDir, err := devtools.ElasticBeatsDir() + if err != nil { + return err + } + scriptPath := filepath.Join(beatsDir, "metricbeat", "scripts", "create_metricset.py") + + _, err = sh.Exec( + map[string]string{}, os.Stdout, os.Stderr, python, scriptPath, + "--path", devtools.CWD(), "--es_beats", beatsDir, + "--module", os.Getenv("MODULE"), "--metricset", os.Getenv("METRICSET"), + ) + return err +} diff --git a/packetbeat/Dockerfile b/packetbeat/Dockerfile index 1fcb4814bc8..c0abe357b8e 100644 --- a/packetbeat/Dockerfile +++ b/packetbeat/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.13.7 +FROM golang:1.13.10 RUN \ apt-get update \ diff --git a/packetbeat/_meta/beat.docker.yml b/packetbeat/_meta/config/beat.docker.yml.tmpl similarity index 99% rename from packetbeat/_meta/beat.docker.yml rename to packetbeat/_meta/config/beat.docker.yml.tmpl index 90c4c24e665..f4f0db1f7e6 100644 --- a/packetbeat/_meta/beat.docker.yml +++ b/packetbeat/_meta/config/beat.docker.yml.tmpl @@ -36,4 +36,3 @@ packetbeat.protocols.cassandra: packetbeat.protocols.tls: ports: [443, 993, 995, 5223, 8443, 8883, 9243] - diff --git a/packetbeat/_meta/beat.reference.yml b/packetbeat/_meta/config/beat.reference.yml.tmpl similarity index 98% rename from packetbeat/_meta/beat.reference.yml rename to packetbeat/_meta/config/beat.reference.yml.tmpl index 6ddd057b4c9..1a3aab315d7 100644 --- a/packetbeat/_meta/beat.reference.yml +++ b/packetbeat/_meta/config/beat.reference.yml.tmpl @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/packetbeat/index.html -#============================== Network device ================================ +{{header "Network device"}} # Select the network interface to sniff the data. You can use the "any" # keyword to sniff on all connected interfaces. @@ -47,7 +47,7 @@ packetbeat.interfaces.device: {{ call .device .GOOS }} # can stay enabled even after beat is shut down. #packetbeat.interfaces.auto_promisc_mode: true -#================================== Flows ===================================== +{{header "Flows"}} packetbeat.flows: # Enable Network flows. Default: true @@ -63,7 +63,7 @@ packetbeat.flows: # Set to true to publish fields with null values in events. #keep_null: false -#========================== Transaction protocols ============================= +{{header "Transaction protocols"}} packetbeat.protocols: - type: icmp @@ -531,7 +531,7 @@ packetbeat.protocols: # Set to true to publish fields with null values in events. #keep_null: false -#=========================== Monitored processes ============================== +{{header "Monitored processes"}} # Packetbeat can enrich events with information about the process associated # the socket that sent or received the packet if Packetbeat is monitoring diff --git a/packetbeat/_meta/beat.yml b/packetbeat/_meta/config/beat.yml.tmpl similarity index 91% rename from packetbeat/_meta/beat.yml rename to packetbeat/_meta/config/beat.yml.tmpl index 8c8037a5e7c..fb221cba3c9 100644 --- a/packetbeat/_meta/beat.yml +++ b/packetbeat/_meta/config/beat.yml.tmpl @@ -7,13 +7,13 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/packetbeat/index.html -#============================== Network device ================================ +{{header "Network device"}} # Select the network interface to sniff the data. On Linux, you can use the # "any" keyword to sniff on all connected interfaces. packetbeat.interfaces.device: {{ call .device .GOOS }} -#================================== Flows ===================================== +{{header "Flows"}} # Set `enabled: false` or comment out all options to disable flows reporting. packetbeat.flows: @@ -24,7 +24,7 @@ packetbeat.flows: # Configure reporting period. If set to -1, only killed flows will be reported period: 10s -#========================== Transaction protocols ============================= +{{header "Transaction protocols"}} packetbeat.protocols: - type: icmp @@ -101,7 +101,7 @@ packetbeat.protocols: - 8883 # Secure MQTT - 9243 # Elasticsearch -#==================== Elasticsearch template setting ========================== +{{header "Elasticsearch template setting"}} setup.template.settings: index.number_of_shards: 1 diff --git a/packetbeat/docs/fields.asciidoc b/packetbeat/docs/fields.asciidoc index f76a5d2da72..2b273f13379 100644 --- a/packetbeat/docs/fields.asciidoc +++ b/packetbeat/docs/fields.asciidoc @@ -452,7 +452,8 @@ Contains common beat fields available in all event types. *`agent.hostname`*:: + -- -Hostname of the agent. +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. + type: keyword diff --git a/packetbeat/docs/packetbeat-filtering.asciidoc b/packetbeat/docs/packetbeat-filtering.asciidoc index 3ac4d31f7c9..a3513659cf4 100644 --- a/packetbeat/docs/packetbeat-filtering.asciidoc +++ b/packetbeat/docs/packetbeat-filtering.asciidoc @@ -55,10 +55,10 @@ following configuration: [source,yaml] ---- processors: - - drop_event: - when: + - drop_event: + when: equals: - http.response.status_code: 200 + http.response.status_code: 200 ---- @@ -67,11 +67,11 @@ If you don't want to export raw data for the successful transactions: [source,yaml] ---- processors: - - drop_fields: - when: + - drop_fields: + when: equals: - http.response.status_code: 200 - fields: ["request", "response"] + http.response.status_code: 200 + fields: ["request", "response"] ---- include::{libbeat-dir}/processors-using.asciidoc[] diff --git a/packetbeat/include/fields.go b/packetbeat/include/fields.go index 68beee23b26..805706ca77f 100644 --- a/packetbeat/include/fields.go +++ b/packetbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/packetbeat/magefile.go b/packetbeat/magefile.go index 8e381260a92..6aade6766cb 100644 --- a/packetbeat/magefile.go +++ b/packetbeat/magefile.go @@ -36,6 +36,8 @@ import ( // mage:import "github.com/elastic/beats/v7/dev-tools/mage/target/common" // mage:import + _ "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" + // mage:import _ "github.com/elastic/beats/v7/dev-tools/mage/target/integtest/notests" // mage:import _ "github.com/elastic/beats/v7/dev-tools/mage/target/test" diff --git a/packetbeat/packetbeat.reference.yml b/packetbeat/packetbeat.reference.yml index 993ddb4b06c..8e66830c5cf 100644 --- a/packetbeat/packetbeat.reference.yml +++ b/packetbeat/packetbeat.reference.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/packetbeat/index.html -#============================== Network device ================================ +# =============================== Network device =============================== # Select the network interface to sniff the data. You can use the "any" # keyword to sniff on all connected interfaces. @@ -47,7 +47,7 @@ packetbeat.interfaces.device: any # can stay enabled even after beat is shut down. #packetbeat.interfaces.auto_promisc_mode: true -#================================== Flows ===================================== +# =================================== Flows ==================================== packetbeat.flows: # Enable Network flows. Default: true @@ -63,7 +63,7 @@ packetbeat.flows: # Set to true to publish fields with null values in events. #keep_null: false -#========================== Transaction protocols ============================= +# =========================== Transaction protocols ============================ packetbeat.protocols: - type: icmp @@ -531,7 +531,7 @@ packetbeat.protocols: # Set to true to publish fields with null values in events. #keep_null: false -#=========================== Monitored processes ============================== +# ============================ Monitored processes ============================= # Packetbeat can enrich events with information about the process associated # the socket that sent or received the packet if Packetbeat is monitoring @@ -545,7 +545,7 @@ packetbeat.procs.enabled: false # false. packetbeat.ignore_outgoing: false -#================================ General ====================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -653,7 +653,7 @@ packetbeat.ignore_outgoing: false # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -670,153 +670,153 @@ packetbeat.ignore_outgoing: false # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Packetbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -829,11 +829,11 @@ packetbeat.ignore_outgoing: false # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -953,7 +953,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -1067,7 +1088,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -1221,6 +1242,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -1243,7 +1267,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -1361,7 +1385,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -1395,7 +1419,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -1408,7 +1432,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Packetbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -1434,11 +1458,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -1482,8 +1508,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1537,7 +1562,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1551,13 +1576,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'packetbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "packetbeat" +#setup.ilm.rollover_alias: 'packetbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'packetbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1572,7 +1597,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1627,9 +1652,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1696,8 +1720,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Packetbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1807,6 +1830,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1818,7 +1862,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1842,12 +1887,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/packetbeat/packetbeat.yml b/packetbeat/packetbeat.yml index 9d06f982f61..66e1bc85991 100644 --- a/packetbeat/packetbeat.yml +++ b/packetbeat/packetbeat.yml @@ -7,13 +7,13 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/packetbeat/index.html -#============================== Network device ================================ +# =============================== Network device =============================== # Select the network interface to sniff the data. On Linux, you can use the # "any" keyword to sniff on all connected interfaces. packetbeat.interfaces.device: any -#================================== Flows ===================================== +# =================================== Flows ==================================== # Set `enabled: false` or comment out all options to disable flows reporting. packetbeat.flows: @@ -24,7 +24,7 @@ packetbeat.flows: # Configure reporting period. If set to -1, only killed flows will be reported period: 10s -#========================== Transaction protocols ============================= +# =========================== Transaction protocols ============================ packetbeat.protocols: - type: icmp @@ -101,14 +101,14 @@ packetbeat.protocols: - 8883 # Secure MQTT - 9243 # Elasticsearch -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false -#================================ General ===================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -123,8 +123,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -136,7 +135,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -153,7 +152,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Packetbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -166,11 +165,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -183,7 +182,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -198,7 +197,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -207,7 +206,8 @@ processors: - add_cloud_metadata: ~ - add_docker_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -218,8 +218,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# packetbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Packetbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -240,7 +240,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/packetbeat/scripts/mage/config.go b/packetbeat/scripts/mage/config.go index fac457cd7ef..a143cda22e7 100644 --- a/packetbeat/scripts/mage/config.go +++ b/packetbeat/scripts/mage/config.go @@ -43,24 +43,10 @@ func device(goos string) string { // ConfigFileParams returns the default ConfigFileParams for generating // packetbeat*.yml files. func ConfigFileParams() devtools.ConfigFileParams { - return devtools.ConfigFileParams{ - ShortParts: []string{ - devtools.OSSBeatDir("_meta/beat.yml"), - configTemplateGlob, - devtools.LibbeatDir("_meta/config.yml.tmpl"), - }, - ReferenceParts: []string{ - devtools.OSSBeatDir("_meta/beat.reference.yml"), - configTemplateGlob, - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - }, - DockerParts: []string{ - devtools.OSSBeatDir("_meta/beat.docker.yml"), - configTemplateGlob, - devtools.LibbeatDir("_meta/config.docker.yml"), - }, - ExtraVars: map[string]interface{}{ - "device": device, - }, + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, devtools.OSSBeatDir("_meta/config/*.tmpl")) + p.ExtraVars = map[string]interface{}{ + "device": device, } + return p } diff --git a/packetbeat/tests/system/test_0099_golden_files.py b/packetbeat/tests/system/test_0099_golden_files.py index 5a543af2251..5f747a3c83c 100644 --- a/packetbeat/tests/system/test_0099_golden_files.py +++ b/packetbeat/tests/system/test_0099_golden_files.py @@ -66,6 +66,7 @@ def clean_keys(obj): "agent.ephemeral_id", "agent.hostname", "agent.id", + "agent.name", "agent.type", "agent.version", "ecs.version", diff --git a/testing/environments/docker/elasticsearch/kerberos/init.sh b/testing/environments/docker/elasticsearch/kerberos/init.sh new file mode 100644 index 00000000000..ac7fe70fa69 --- /dev/null +++ b/testing/environments/docker/elasticsearch/kerberos/init.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# setup Keberos +echo elasticsearch_kerberos.elastic > /etc/hostname && echo "127.0.0.1 elasticsearch_kerberos.elastic" >> /etc/hosts + +/scripts/installkdc.sh +/scripts/addprincs.sh + +# add test user +bin/elasticsearch-users useradd beats -r superuser -p testing | /usr/local/bin/docker-entrypoint.sh eswrapper diff --git a/testing/environments/docker/elasticsearch/kerberos/installkdc.sh b/testing/environments/docker/elasticsearch/kerberos/installkdc.sh new file mode 100644 index 00000000000..f35848d004c --- /dev/null +++ b/testing/environments/docker/elasticsearch/kerberos/installkdc.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -e + +# KDC installation steps and considerations based on https://web.mit.edu/kerberos/krb5-latest/doc/admin/install_kdc.html +# and helpful input from https://help.ubuntu.com/community/Kerberos + +LOCALSTATEDIR=/etc +LOGDIR=/var/log/krb5 + +#MARKER_FILE=/etc/marker + +# Transfer and interpolate krb5.conf +cp /config/krb5.conf.template $LOCALSTATEDIR/krb5.conf +sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf + + +# Transfer and interpolate the kdc.conf +mkdir -p $LOCALSTATEDIR/krb5kdc +cp /config/kdc.conf.template $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf +sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf + +# Touch logging locations +mkdir -p $LOGDIR +touch $LOGDIR/kadmin.log +touch $LOGDIR/krb5kdc.log +touch $LOGDIR/krb5lib.log + +# Update package manager +yum update -qqy + +# Install krb5 packages +yum install -qqy krb5-{server,libs,workstation} + +# Create kerberos database with stash file and garbage password +kdb5_util create -s -r $REALM_NAME -P zyxwvutsrpqonmlk9876 + +# Set up admin acls +cat << EOF > /etc/krb5kdc/kadm5.acl +*/admin@$REALM_NAME * +*@$REALM_NAME * +*/*@$REALM_NAME i +EOF + +# Create admin principal +kadmin.local -q "addprinc -pw elastic admin/admin@$REALM_NAME" +kadmin.local -q "ktadd -k /etc/admin.keytab admin/admin@$REALM_NAME" + +# Create a link so addprinc.sh is on path +ln -s /scripts/addprinc.sh /usr/bin/ diff --git a/testing/environments/docker/elasticsearch_kerberos/Dockerfile b/testing/environments/docker/elasticsearch_kerberos/Dockerfile new file mode 100644 index 00000000000..59e5de735ad --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/Dockerfile @@ -0,0 +1,15 @@ +FROM docker.elastic.co/elasticsearch/elasticsearch:8.0.0-SNAPSHOT + +ADD scripts /scripts +ADD config /config +ADD healthcheck.sh /healthcheck.sh +ADD start.sh /start.sh + +ENV REALM_NAME ELASTIC +ENV KDC_NAME elasticsearch_kerberos.elastic +ENV BUILD_ZONE elastic +ENV ELASTIC_ZONE $BUILD_ZONE + +USER root +RUN /scripts/installkdc.sh && /scripts/addprincs.sh +USER elasticsearch diff --git a/testing/environments/docker/elasticsearch_kerberos/config/kdc.conf.template b/testing/environments/docker/elasticsearch_kerberos/config/kdc.conf.template new file mode 100644 index 00000000000..0d32b8d411f --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/config/kdc.conf.template @@ -0,0 +1,34 @@ +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[kdcdefaults] + kdc_listen = 1088 + kdc_tcp_listen = 1088 + +[realms] + ${REALM_NAME} = { + kadmind_port = 1749 + max_life = 12h 0m 0s + max_renewable_life = 7d 0h 0m 0s + master_key_type = aes256-cts + supported_enctypes = aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal + } + +[logging] + kdc = FILE:/var/log/krb5/krb5kdc.log + admin_server = FILE:/var/log/krb5/kadmin.log + default = FILE:/var/log/krb5/krb5lib.log diff --git a/testing/environments/docker/elasticsearch_kerberos/config/krb5.conf b/testing/environments/docker/elasticsearch_kerberos/config/krb5.conf new file mode 100644 index 00000000000..1b34299558c --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/config/krb5.conf @@ -0,0 +1,25 @@ +[libdefaults] + default_realm = ELASTIC + dns_canonicalize_hostname = false + dns_lookup_kdc = false + dns_lookup_realm = false + dns_uri_lookup = false + forwardable = true + ignore_acceptor_hostname = true + rdns = false + default_tgs_enctypes = aes128-cts-hmac-sha1-96 + default_tkt_enctypes = aes128-cts-hmac-sha1-96 + permitted_enctypes = aes128-cts-hmac-sha1-96 + kdc_timeout = 3000 + +[realms] + ELASTIC = { + kdc = elasticsearch_kerberos.elastic:88 + admin_server = elasticsearch_kerberos.elastic:749 + default_domain = elastic + } + +[domain_realm] + .elastic = ELASTIC + elastic = ELASTIC + diff --git a/testing/environments/docker/elasticsearch_kerberos/config/krb5.conf.template b/testing/environments/docker/elasticsearch_kerberos/config/krb5.conf.template new file mode 100644 index 00000000000..75245ab7733 --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/config/krb5.conf.template @@ -0,0 +1,43 @@ +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +[libdefaults] + default_realm = ${REALM_NAME} + dns_canonicalize_hostname = false + dns_lookup_kdc = false + dns_lookup_realm = false + dns_uri_lookup = false + forwardable = true + ignore_acceptor_hostname = true + rdns = false + default_tgs_enctypes = aes128-cts-hmac-sha1-96 + default_tkt_enctypes = aes128-cts-hmac-sha1-96 + permitted_enctypes = aes128-cts-hmac-sha1-96 + udp_preference_limit = 1 + kdc_timeout = 3000 + +[realms] + ${REALM_NAME} = { + kdc = localhost:1088 + admin_server = localhost:1749 + default_domain = ${BUILD_ZONE} + } + +[domain_realm] + .${ELASTIC_ZONE} = ${REALM_NAME} + ${ELASTIC_ZONE} = ${REALM_NAME} + diff --git a/testing/environments/docker/elasticsearch_kerberos/healthcheck.sh b/testing/environments/docker/elasticsearch_kerberos/healthcheck.sh new file mode 100755 index 00000000000..a0932afaa94 --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/healthcheck.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# check if service principal is OK +KRB5_CONFIG=/etc/krb5.conf \ + kinit -k -t /etc/HTTP_elasticsearch_kerberos.elastic.keytab HTTP/elasticsearch_kerberos.elastic@ELASTIC + + +# check if beats user can connect +echo testing | KRB5_CONFIG=/etc/krb5.conf kinit beats@ELASTIC +klist +curl --negotiate -u : -XGET http://elasticsearch_kerberos.elastic:9200/ diff --git a/testing/environments/docker/elasticsearch_kerberos/init.sh b/testing/environments/docker/elasticsearch_kerberos/init.sh new file mode 100755 index 00000000000..e69de29bb2d diff --git a/testing/environments/docker/elasticsearch_kerberos/scripts/addprinc.sh b/testing/environments/docker/elasticsearch_kerberos/scripts/addprinc.sh new file mode 100755 index 00000000000..97493df7c51 --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/scripts/addprinc.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -e + +if [[ $# -lt 1 ]]; then + echo 'Usage: addprinc.sh principalName [password]' + echo ' principalName user principal name without realm' + echo ' password If provided then will set password for user else it will provision user with keytab' + exit 1 +fi + +PRINC="$1" +PASSWD="$2" +USER=$(echo $PRINC | tr "/" "_") +REALM=ELASTIC + +VDIR=/usr/share/kerberos +BUILD_DIR=/var/build +LOCALSTATEDIR=/etc +LOGDIR=/var/log/krb5 + +ADMIN_PRIN=admin/admin@$REALM +ADMIN_KTAB=$LOCALSTATEDIR/admin.keytab + +USER_PRIN=$PRINC@$REALM +USER_KTAB=$LOCALSTATEDIR/$USER.keytab + +if [ -f $USER_KTAB ] && [ -z "$PASSWD" ]; then + echo "Principal '${PRINC}@${REALM}' already exists. Re-copying keytab..." + sudo cp $USER_KTAB $KEYTAB_DIR/$USER.keytab +else + if [ -z "$PASSWD" ]; then + echo "Provisioning '${PRINC}@${REALM}' principal and keytab..." + sudo kadmin -p $ADMIN_PRIN -kt $ADMIN_KTAB -q "addprinc -randkey $USER_PRIN" + sudo kadmin -p $ADMIN_PRIN -kt $ADMIN_KTAB -q "ktadd -k $USER_KTAB $USER_PRIN" + sudo chmod 777 $USER_KTAB + sudo cp $USER_KTAB /usr/share/elasticsearch/config + sudo chown elasticsearch:elasticsearch /usr/share/elasticsearch/config/$USER.keytab + else + echo "Provisioning '${PRINC}@${REALM}' principal with password..." + sudo kadmin -p $ADMIN_PRIN -kt $ADMIN_KTAB -q "addprinc -pw $PASSWD $PRINC" + fi +fi + +echo "Done provisioning $USER" diff --git a/testing/environments/docker/elasticsearch_kerberos/scripts/addprincs.sh b/testing/environments/docker/elasticsearch_kerberos/scripts/addprincs.sh new file mode 100755 index 00000000000..7ee85889f0d --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/scripts/addprincs.sh @@ -0,0 +1,7 @@ +set -e + +krb5kdc +kadmind + +addprinc.sh HTTP/elasticsearch_kerberos.elastic +addprinc.sh beats testing diff --git a/testing/environments/docker/elasticsearch_kerberos/scripts/installkdc.sh b/testing/environments/docker/elasticsearch_kerberos/scripts/installkdc.sh new file mode 100755 index 00000000000..50ab0ff0a6a --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/scripts/installkdc.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# Licensed to Elasticsearch under one or more contributor +# license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright +# ownership. Elasticsearch licenses this file to you under +# the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -e + +LOCALSTATEDIR=/etc +KDC_CONFIG=/var/kerberos +LOGDIR=/var/log/krb5 + +#MARKER_FILE=/etc/marker + +# Transfer and interpolate krb5.conf +cp /config/krb5.conf.template $LOCALSTATEDIR/krb5.conf +sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $LOCALSTATEDIR/krb5.conf +sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf + + +# Transfer and interpolate the kdc.conf +mkdir -p $KDC_CONFIG/krb5kdc +cp /config/kdc.conf.template $KDC_CONFIG/krb5kdc/kdc.conf +sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $KDC_CONFIG/krb5kdc/kdc.conf +sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $KDC_CONFIG/krb5kdc/kdc.conf +sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $KDC_CONFIG/krb5kdc/kdc.conf +sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf + +# Touch logging locations +mkdir -p $LOGDIR +touch $LOGDIR/kadmin.log +touch $LOGDIR/krb5kdc.log +touch $LOGDIR/krb5lib.log + +# Update package manager +yum update -qqy + +# Install krb5 packages +yum install -qqy krb5-{server,libs,workstation} sudo + +# Create kerberos database with stash file and garbage password +kdb5_util create -s -r $REALM_NAME -P zyxwvutsrpqonmlk9876 + +# Set up admin acls +cat << EOF > /var/kerberos/krb5kdc/kadm5.acl +*/admin@$REALM_NAME * +*@$REALM_NAME * +*/*@$REALM_NAME i +EOF + +# Create admin principal +kadmin.local -q "addprinc -pw elastic admin/admin@$REALM_NAME" +kadmin.local -q "ktadd -k /etc/admin.keytab admin/admin@$REALM_NAME" + +# set ownership for ES +chown -R elasticsearch:elasticsearch $LOGDIR +chown -R elasticsearch:elasticsearch $KDC_CONFIG +chown -R elasticsearch:elasticsearch $LOCALSTATEDIR/krb5.conf +chown -R elasticsearch:elasticsearch $LOCALSTATEDIR/admin.keytab + + +# Create a link so addprinc.sh is on path +ln -s /scripts/addprinc.sh /usr/bin/ diff --git a/testing/environments/docker/elasticsearch_kerberos/start.sh b/testing/environments/docker/elasticsearch_kerberos/start.sh new file mode 100755 index 00000000000..522f6c20474 --- /dev/null +++ b/testing/environments/docker/elasticsearch_kerberos/start.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# start Kerberos services +krb5kdc +kadmind + +# start ES +/usr/local/bin/docker-entrypoint.sh eswrapper diff --git a/testing/environments/docker/kerberos_kdc/Dockerfile b/testing/environments/docker/kerberos_kdc/Dockerfile new file mode 100644 index 00000000000..629fbaebcd5 --- /dev/null +++ b/testing/environments/docker/kerberos_kdc/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:14.04 +ADD scripts /scripts + +ENV REALM_NAME ELASTIC +ENV KDC_NAME kerberos_kdc +ENV BUILD_ZONE elastic +ENV ELASTIC_ZONE $BUILD_ZONE + +RUN echo kerberos_kdc.elastic > /etc/hostname && echo "127.0.0.1 kerberos_kdc.elastic" >> /etc/hosts +RUN bash /scripts/installkdc.sh + +EXPOSE 88 +EXPOSE 749 + +CMD sleep infinity diff --git a/vendor/github.com/armon/go-radix/.gitignore b/vendor/github.com/armon/go-radix/.gitignore new file mode 100644 index 00000000000..00268614f04 --- /dev/null +++ b/vendor/github.com/armon/go-radix/.gitignore @@ -0,0 +1,22 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/vendor/github.com/armon/go-radix/.travis.yml b/vendor/github.com/armon/go-radix/.travis.yml new file mode 100644 index 00000000000..1a0bbea6c77 --- /dev/null +++ b/vendor/github.com/armon/go-radix/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - tip diff --git a/vendor/github.com/armon/go-radix/LICENSE b/vendor/github.com/armon/go-radix/LICENSE new file mode 100644 index 00000000000..a5df10e675d --- /dev/null +++ b/vendor/github.com/armon/go-radix/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Armon Dadgar + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/armon/go-radix/README.md b/vendor/github.com/armon/go-radix/README.md new file mode 100644 index 00000000000..26f42a2837c --- /dev/null +++ b/vendor/github.com/armon/go-radix/README.md @@ -0,0 +1,38 @@ +go-radix [![Build Status](https://travis-ci.org/armon/go-radix.png)](https://travis-ci.org/armon/go-radix) +========= + +Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree). +The package only provides a single `Tree` implementation, optimized for sparse nodes. + +As a radix tree, it provides the following: + * O(k) operations. In many cases, this can be faster than a hash table since + the hash function is an O(k) operation, and hash tables have very poor cache locality. + * Minimum / Maximum value lookups + * Ordered iteration + +For an immutable variant, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix). + +Documentation +============= + +The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix). + +Example +======= + +Below is a simple example of usage + +```go +// Create a tree +r := radix.New() +r.Insert("foo", 1) +r.Insert("bar", 2) +r.Insert("foobar", 2) + +// Find the longest prefix match +m, _, _ := r.LongestPrefix("foozip") +if m != "foo" { + panic("should be foo") +} +``` + diff --git a/vendor/github.com/armon/go-radix/go.mod b/vendor/github.com/armon/go-radix/go.mod new file mode 100644 index 00000000000..4336aa29ea2 --- /dev/null +++ b/vendor/github.com/armon/go-radix/go.mod @@ -0,0 +1 @@ +module github.com/armon/go-radix diff --git a/vendor/github.com/armon/go-radix/radix.go b/vendor/github.com/armon/go-radix/radix.go new file mode 100644 index 00000000000..e2bb22eb91d --- /dev/null +++ b/vendor/github.com/armon/go-radix/radix.go @@ -0,0 +1,540 @@ +package radix + +import ( + "sort" + "strings" +) + +// WalkFn is used when walking the tree. Takes a +// key and value, returning if iteration should +// be terminated. +type WalkFn func(s string, v interface{}) bool + +// leafNode is used to represent a value +type leafNode struct { + key string + val interface{} +} + +// edge is used to represent an edge node +type edge struct { + label byte + node *node +} + +type node struct { + // leaf is used to store possible leaf + leaf *leafNode + + // prefix is the common prefix we ignore + prefix string + + // Edges should be stored in-order for iteration. + // We avoid a fully materialized slice to save memory, + // since in most cases we expect to be sparse + edges edges +} + +func (n *node) isLeaf() bool { + return n.leaf != nil +} + +func (n *node) addEdge(e edge) { + n.edges = append(n.edges, e) + n.edges.Sort() +} + +func (n *node) updateEdge(label byte, node *node) { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + n.edges[idx].node = node + return + } + panic("replacing missing edge") +} + +func (n *node) getEdge(label byte) *node { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + return n.edges[idx].node + } + return nil +} + +func (n *node) delEdge(label byte) { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + copy(n.edges[idx:], n.edges[idx+1:]) + n.edges[len(n.edges)-1] = edge{} + n.edges = n.edges[:len(n.edges)-1] + } +} + +type edges []edge + +func (e edges) Len() int { + return len(e) +} + +func (e edges) Less(i, j int) bool { + return e[i].label < e[j].label +} + +func (e edges) Swap(i, j int) { + e[i], e[j] = e[j], e[i] +} + +func (e edges) Sort() { + sort.Sort(e) +} + +// Tree implements a radix tree. This can be treated as a +// Dictionary abstract data type. The main advantage over +// a standard hash map is prefix-based lookups and +// ordered iteration, +type Tree struct { + root *node + size int +} + +// New returns an empty Tree +func New() *Tree { + return NewFromMap(nil) +} + +// NewFromMap returns a new tree containing the keys +// from an existing map +func NewFromMap(m map[string]interface{}) *Tree { + t := &Tree{root: &node{}} + for k, v := range m { + t.Insert(k, v) + } + return t +} + +// Len is used to return the number of elements in the tree +func (t *Tree) Len() int { + return t.size +} + +// longestPrefix finds the length of the shared prefix +// of two strings +func longestPrefix(k1, k2 string) int { + max := len(k1) + if l := len(k2); l < max { + max = l + } + var i int + for i = 0; i < max; i++ { + if k1[i] != k2[i] { + break + } + } + return i +} + +// Insert is used to add a newentry or update +// an existing entry. Returns if updated. +func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { + var parent *node + n := t.root + search := s + for { + // Handle key exhaution + if len(search) == 0 { + if n.isLeaf() { + old := n.leaf.val + n.leaf.val = v + return old, true + } + + n.leaf = &leafNode{ + key: s, + val: v, + } + t.size++ + return nil, false + } + + // Look for the edge + parent = n + n = n.getEdge(search[0]) + + // No edge, create one + if n == nil { + e := edge{ + label: search[0], + node: &node{ + leaf: &leafNode{ + key: s, + val: v, + }, + prefix: search, + }, + } + parent.addEdge(e) + t.size++ + return nil, false + } + + // Determine longest prefix of the search key on match + commonPrefix := longestPrefix(search, n.prefix) + if commonPrefix == len(n.prefix) { + search = search[commonPrefix:] + continue + } + + // Split the node + t.size++ + child := &node{ + prefix: search[:commonPrefix], + } + parent.updateEdge(search[0], child) + + // Restore the existing node + child.addEdge(edge{ + label: n.prefix[commonPrefix], + node: n, + }) + n.prefix = n.prefix[commonPrefix:] + + // Create a new leaf node + leaf := &leafNode{ + key: s, + val: v, + } + + // If the new key is a subset, add to to this node + search = search[commonPrefix:] + if len(search) == 0 { + child.leaf = leaf + return nil, false + } + + // Create a new edge for the node + child.addEdge(edge{ + label: search[0], + node: &node{ + leaf: leaf, + prefix: search, + }, + }) + return nil, false + } +} + +// Delete is used to delete a key, returning the previous +// value and if it was deleted +func (t *Tree) Delete(s string) (interface{}, bool) { + var parent *node + var label byte + n := t.root + search := s + for { + // Check for key exhaution + if len(search) == 0 { + if !n.isLeaf() { + break + } + goto DELETE + } + + // Look for an edge + parent = n + label = search[0] + n = n.getEdge(label) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + return nil, false + +DELETE: + // Delete the leaf + leaf := n.leaf + n.leaf = nil + t.size-- + + // Check if we should delete this node from the parent + if parent != nil && len(n.edges) == 0 { + parent.delEdge(label) + } + + // Check if we should merge this node + if n != t.root && len(n.edges) == 1 { + n.mergeChild() + } + + // Check if we should merge the parent's other child + if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { + parent.mergeChild() + } + + return leaf.val, true +} + +// DeletePrefix is used to delete the subtree under a prefix +// Returns how many nodes were deleted +// Use this to delete large subtrees efficiently +func (t *Tree) DeletePrefix(s string) int { + return t.deletePrefix(nil, t.root, s) +} + +// delete does a recursive deletion +func (t *Tree) deletePrefix(parent, n *node, prefix string) int { + // Check for key exhaustion + if len(prefix) == 0 { + // Remove the leaf node + subTreeSize := 0 + //recursively walk from all edges of the node to be deleted + recursiveWalk(n, func(s string, v interface{}) bool { + subTreeSize++ + return false + }) + if n.isLeaf() { + n.leaf = nil + } + n.edges = nil // deletes the entire subtree + + // Check if we should merge the parent's other child + if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { + parent.mergeChild() + } + t.size -= subTreeSize + return subTreeSize + } + + // Look for an edge + label := prefix[0] + child := n.getEdge(label) + if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) { + return 0 + } + + // Consume the search prefix + if len(child.prefix) > len(prefix) { + prefix = prefix[len(prefix):] + } else { + prefix = prefix[len(child.prefix):] + } + return t.deletePrefix(n, child, prefix) +} + +func (n *node) mergeChild() { + e := n.edges[0] + child := e.node + n.prefix = n.prefix + child.prefix + n.leaf = child.leaf + n.edges = child.edges +} + +// Get is used to lookup a specific key, returning +// the value and if it was found +func (t *Tree) Get(s string) (interface{}, bool) { + n := t.root + search := s + for { + // Check for key exhaution + if len(search) == 0 { + if n.isLeaf() { + return n.leaf.val, true + } + break + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + return nil, false +} + +// LongestPrefix is like Get, but instead of an +// exact match, it will return the longest prefix match. +func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { + var last *leafNode + n := t.root + search := s + for { + // Look for a leaf node + if n.isLeaf() { + last = n.leaf + } + + // Check for key exhaution + if len(search) == 0 { + break + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + if last != nil { + return last.key, last.val, true + } + return "", nil, false +} + +// Minimum is used to return the minimum value in the tree +func (t *Tree) Minimum() (string, interface{}, bool) { + n := t.root + for { + if n.isLeaf() { + return n.leaf.key, n.leaf.val, true + } + if len(n.edges) > 0 { + n = n.edges[0].node + } else { + break + } + } + return "", nil, false +} + +// Maximum is used to return the maximum value in the tree +func (t *Tree) Maximum() (string, interface{}, bool) { + n := t.root + for { + if num := len(n.edges); num > 0 { + n = n.edges[num-1].node + continue + } + if n.isLeaf() { + return n.leaf.key, n.leaf.val, true + } + break + } + return "", nil, false +} + +// Walk is used to walk the tree +func (t *Tree) Walk(fn WalkFn) { + recursiveWalk(t.root, fn) +} + +// WalkPrefix is used to walk the tree under a prefix +func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { + n := t.root + search := prefix + for { + // Check for key exhaution + if len(search) == 0 { + recursiveWalk(n, fn) + return + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + + } else if strings.HasPrefix(n.prefix, search) { + // Child may be under our search prefix + recursiveWalk(n, fn) + return + } else { + break + } + } + +} + +// WalkPath is used to walk the tree, but only visiting nodes +// from the root down to a given leaf. Where WalkPrefix walks +// all the entries *under* the given prefix, this walks the +// entries *above* the given prefix. +func (t *Tree) WalkPath(path string, fn WalkFn) { + n := t.root + search := path + for { + // Visit the leaf values if any + if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { + return + } + + // Check for key exhaution + if len(search) == 0 { + return + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + return + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } +} + +// recursiveWalk is used to do a pre-order walk of a node +// recursively. Returns true if the walk should be aborted +func recursiveWalk(n *node, fn WalkFn) bool { + // Visit the leaf values if any + if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { + return true + } + + // Recurse on the children + for _, e := range n.edges { + if recursiveWalk(e.node, fn) { + return true + } + } + return false +} + +// ToMap is used to walk the tree and convert it into a map +func (t *Tree) ToMap() map[string]interface{} { + out := make(map[string]interface{}, t.size) + t.Walk(func(k string, v interface{}) bool { + out[k] = v + return false + }) + return out +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/README.md b/vendor/github.com/denisenkom/go-mssqldb/README.md index ea06b900a8f..46e4eeabb5a 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/README.md +++ b/vendor/github.com/denisenkom/go-mssqldb/README.md @@ -56,7 +56,7 @@ Other supported formats are listed below. * `hostNameInCertificate` - Specifies the Common Name (CN) in the server certificate. Default value is the server host. * `ServerSPN` - The kerberos SPN (Service Principal Name) for the server. Default is MSSQLSvc/host:port. * `Workstation ID` - The workstation name (default is the host name) -* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. +* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. The `database` must be specified when connecting with `Application Intent` set to `ReadOnly`. ### The connection string can be specified in one of three formats: @@ -106,6 +106,26 @@ Other supported formats are listed below. * `odbc:server=localhost;user id=sa;password={foo{bar}` // Literal `{`, password is "foo{bar" * `odbc:server=localhost;user id=sa;password={foo}}bar}` // Escaped `} with `}}`, password is "foo}bar" +### Azure Active Directory authentication - preview + +The configuration of functionality might change in the future. + +Azure Active Directory (AAD) access tokens are relatively short lived and need to be +valid when a new connection is made. Authentication is supported using a callback func that +provides a fresh and valid token using a connector: +``` golang +conn, err := mssql.NewAccessTokenConnector( + "Server=test.database.windows.net;Database=testdb", + tokenProvider) +if err != nil { + // handle errors in DSN +} +db := sql.OpenDB(conn) +``` +Where `tokenProvider` is a function that returns a fresh access token or an error. None of these statements +actually trigger the retrieval of a token, this happens when the first statment is issued and a connection +is created. + ## Executing Stored Procedures To run a stored procedure, set the query text to the procedure name: @@ -117,6 +137,66 @@ _, err := db.ExecContext(ctx, "sp_RunMe", ) ``` +## Reading Output Parameters from a Stored Procedure with Resultset + +To read output parameters from a stored procedure with resultset, make sure you read all the rows before reading the output parameters: +```go +sqltextcreate := ` +CREATE PROCEDURE spwithoutputandrows + @bitparam BIT OUTPUT +AS BEGIN + SET @bitparam = 1 + SELECT 'Row 1' +END +` +var bitout int64 +rows, err := db.QueryContext(ctx, "spwithoutputandrows", sql.Named("bitparam", sql.Out{Dest: &bitout})) +var strrow string +for rows.Next() { + err = rows.Scan(&strrow) +} +fmt.Printf("bitparam is %d", bitout) +``` + +## Caveat for local temporary tables + +Due to protocol limitations, temporary tables will only be allocated on the connection +as a result of executing a query with zero parameters. The following query +will, due to the use of a parameter, execute in its own session, +and `#mytemp` will be de-allocated right away: + +```go +conn, err := pool.Conn(ctx) +defer conn.Close() +_, err := conn.ExecContext(ctx, "select @p1 as x into #mytemp", 1) +// at this point #mytemp is already dropped again as the session of the ExecContext is over +``` + +To work around this, always explicitly create the local temporary +table in a query without any parameters. As a special case, the driver +will then be able to execute the query directly on the +connection-scoped session. The following example works: + +```go +conn, err := pool.Conn(ctx) + +// Set us up so that temp table is always cleaned up, since conn.Close() +// merely returns conn to pool, rather than actually closing the connection. +defer func() { + _, _ = conn.ExecContext(ctx, "drop table #mytemp") // always clean up + conn.Close() // merely returns conn to pool +}() + + +// Since we not pass any parameters below, the query will execute on the scope of +// the connection and succeed in creating the table. +_, err := conn.ExecContext(ctx, "create table #mytemp ( x int )") + +// #mytemp is now available even if you pass parameters +_, err := conn.ExecContext(ctx, "insert into #mytemp (x) values (@p1)", 1) + +``` + ## Return Status To get the procedure return status, pass into the parameters a @@ -127,6 +207,19 @@ _, err := db.ExecContext(ctx, "theproc", &rs) log.Printf("status=%d", rs) ``` +or + +``` +var rs mssql.ReturnStatus +_, err := db.QueryContext(ctx, "theproc", &rs) +for rows.Next() { + err = rows.Scan(&val) +} +log.Printf("status=%d", rs) +``` + +Limitation: ReturnStatus cannot be retrieved using `QueryRow`. + ## Parameters The `sqlserver` driver uses normal MS SQL Server syntax and expects parameters in @@ -147,9 +240,10 @@ are supported: * time.Time -> datetimeoffset or datetime (TDS version dependent) * mssql.DateTime1 -> datetime * mssql.DateTimeOffset -> datetimeoffset - * "cloud.google.com/go/civil".Date -> date - * "cloud.google.com/go/civil".DateTime -> datetime2 - * "cloud.google.com/go/civil".Time -> time + * "github.com/golang-sql/civil".Date -> date + * "github.com/golang-sql/civil".DateTime -> datetime2 + * "github.com/golang-sql/civil".Time -> time + * mssql.TVP -> Table Value Parameter (TDS version dependent) ## Important Notes diff --git a/vendor/github.com/denisenkom/go-mssqldb/accesstokenconnector.go b/vendor/github.com/denisenkom/go-mssqldb/accesstokenconnector.go new file mode 100644 index 00000000000..8dbe5099e49 --- /dev/null +++ b/vendor/github.com/denisenkom/go-mssqldb/accesstokenconnector.go @@ -0,0 +1,51 @@ +// +build go1.10 + +package mssql + +import ( + "context" + "database/sql/driver" + "errors" + "fmt" +) + +var _ driver.Connector = &accessTokenConnector{} + +// accessTokenConnector wraps Connector and injects a +// fresh access token when connecting to the database +type accessTokenConnector struct { + Connector + + accessTokenProvider func() (string, error) +} + +// NewAccessTokenConnector creates a new connector from a DSN and a token provider. +// The token provider func will be called when a new connection is requested and should return a valid access token. +// The returned connector may be used with sql.OpenDB. +func NewAccessTokenConnector(dsn string, tokenProvider func() (string, error)) (driver.Connector, error) { + if tokenProvider == nil { + return nil, errors.New("mssql: tokenProvider cannot be nil") + } + + conn, err := NewConnector(dsn) + if err != nil { + return nil, err + } + + c := &accessTokenConnector{ + Connector: *conn, + accessTokenProvider: tokenProvider, + } + return c, nil +} + +// Connect returns a new database connection +func (c *accessTokenConnector) Connect(ctx context.Context) (driver.Conn, error) { + var err error + c.Connector.params.fedAuthAccessToken, err = c.accessTokenProvider() + if err != nil { + return nil, fmt.Errorf("mssql: error retrieving access token: %+v", err) + } + + return c.Connector.Connect(ctx) +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/appveyor.yml b/vendor/github.com/denisenkom/go-mssqldb/appveyor.yml index 2ae5456d5cb..c4d2bb060ee 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/appveyor.yml +++ b/vendor/github.com/denisenkom/go-mssqldb/appveyor.yml @@ -10,7 +10,7 @@ environment: SQLUSER: sa SQLPASSWORD: Password12! DATABASE: test - GOVERSION: 110 + GOVERSION: 111 matrix: - GOVERSION: 18 SQLINSTANCE: SQL2016 @@ -18,6 +18,8 @@ environment: SQLINSTANCE: SQL2016 - GOVERSION: 110 SQLINSTANCE: SQL2016 + - GOVERSION: 111 + SQLINSTANCE: SQL2016 - SQLINSTANCE: SQL2014 - SQLINSTANCE: SQL2012SP1 - SQLINSTANCE: SQL2008R2SP2 @@ -27,7 +29,7 @@ install: - set PATH=%GOPATH%\bin;%GOROOT%\bin;%PATH% - go version - go env - - go get -u cloud.google.com/go/civil + - go get -u github.com/golang-sql/civil build_script: - go build diff --git a/vendor/github.com/denisenkom/go-mssqldb/buf.go b/vendor/github.com/denisenkom/go-mssqldb/buf.go index 927d75d1b78..ba39b40f173 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/buf.go +++ b/vendor/github.com/denisenkom/go-mssqldb/buf.go @@ -221,23 +221,27 @@ func (r *tdsBuffer) uint16() uint16 { } func (r *tdsBuffer) BVarChar() string { - l := int(r.byte()) - return r.readUcs2(l) + return readBVarCharOrPanic(r) } -func (r *tdsBuffer) UsVarChar() string { - l := int(r.uint16()) - return r.readUcs2(l) +func readBVarCharOrPanic(r io.Reader) string { + s, err := readBVarChar(r) + if err != nil { + badStreamPanic(err) + } + return s } -func (r *tdsBuffer) readUcs2(numchars int) string { - b := make([]byte, numchars*2) - r.ReadFull(b) - res, err := ucs22str(b) +func readUsVarCharOrPanic(r io.Reader) string { + s, err := readUsVarChar(r) if err != nil { badStreamPanic(err) } - return res + return s +} + +func (r *tdsBuffer) UsVarChar() string { + return readUsVarCharOrPanic(r) } func (r *tdsBuffer) Read(buf []byte) (copied int, err error) { diff --git a/vendor/github.com/denisenkom/go-mssqldb/bulkcopy.go b/vendor/github.com/denisenkom/go-mssqldb/bulkcopy.go index 3b319af893f..1d5eacb3818 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/bulkcopy.go +++ b/vendor/github.com/denisenkom/go-mssqldb/bulkcopy.go @@ -7,9 +7,10 @@ import ( "fmt" "math" "reflect" - "strconv" "strings" "time" + + "github.com/denisenkom/go-mssqldb/internal/decimal" ) type Bulk struct { @@ -42,6 +43,11 @@ type BulkOptions struct { type DataValue interface{} +const ( + sqlDateFormat = "2006-01-02" + sqlTimeFormat = "2006-01-02 15:04:05.999999999Z07:00" +) + func (cn *Conn) CreateBulk(table string, columns []string) (_ *Bulk) { b := Bulk{ctx: context.Background(), cn: cn, tablename: table, headerSent: false, columnsName: columns} b.Debug = false @@ -334,7 +340,7 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) case int64: intvalue = val default: - err = fmt.Errorf("mssql: invalid type for int column") + err = fmt.Errorf("mssql: invalid type for int column: %T", val) return } @@ -361,7 +367,7 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) case int64: floatvalue = float64(val) default: - err = fmt.Errorf("mssql: invalid type for float column: %s", val) + err = fmt.Errorf("mssql: invalid type for float column: %T %s", val, val) return } @@ -380,7 +386,7 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) case []byte: res.buffer = val default: - err = fmt.Errorf("mssql: invalid type for nvarchar column: %s", val) + err = fmt.Errorf("mssql: invalid type for nvarchar column: %T %s", val, val) return } res.ti.Size = len(res.buffer) @@ -392,14 +398,14 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) case []byte: res.buffer = val default: - err = fmt.Errorf("mssql: invalid type for varchar column: %s", val) + err = fmt.Errorf("mssql: invalid type for varchar column: %T %s", val, val) return } res.ti.Size = len(res.buffer) case typeBit, typeBitN: if reflect.TypeOf(val).Kind() != reflect.Bool { - err = fmt.Errorf("mssql: invalid type for bit column: %s", val) + err = fmt.Errorf("mssql: invalid type for bit column: %T %s", val, val) return } res.ti.TypeId = typeBitN @@ -413,18 +419,31 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) case time.Time: res.buffer = encodeDateTime2(val, int(col.ti.Scale)) res.ti.Size = len(res.buffer) + case string: + var t time.Time + if t, err = time.Parse(sqlTimeFormat, val); err != nil { + return res, fmt.Errorf("bulk: unable to convert string to date: %v", err) + } + res.buffer = encodeDateTime2(t, int(col.ti.Scale)) + res.ti.Size = len(res.buffer) default: - err = fmt.Errorf("mssql: invalid type for datetime2 column: %s", val) + err = fmt.Errorf("mssql: invalid type for datetime2 column: %T %s", val, val) return } case typeDateTimeOffsetN: switch val := val.(type) { case time.Time: - res.buffer = encodeDateTimeOffset(val, int(res.ti.Scale)) + res.buffer = encodeDateTimeOffset(val, int(col.ti.Scale)) + res.ti.Size = len(res.buffer) + case string: + var t time.Time + if t, err = time.Parse(sqlTimeFormat, val); err != nil { + return res, fmt.Errorf("bulk: unable to convert string to date: %v", err) + } + res.buffer = encodeDateTimeOffset(t, int(col.ti.Scale)) res.ti.Size = len(res.buffer) - default: - err = fmt.Errorf("mssql: invalid type for datetimeoffset column: %s", val) + err = fmt.Errorf("mssql: invalid type for datetimeoffset column: %T %s", val, val) return } case typeDateN: @@ -432,69 +451,79 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) case time.Time: res.buffer = encodeDate(val) res.ti.Size = len(res.buffer) + case string: + var t time.Time + if t, err = time.ParseInLocation(sqlDateFormat, val, time.UTC); err != nil { + return res, fmt.Errorf("bulk: unable to convert string to date: %v", err) + } + res.buffer = encodeDate(t) + res.ti.Size = len(res.buffer) default: - err = fmt.Errorf("mssql: invalid type for date column: %s", val) + err = fmt.Errorf("mssql: invalid type for date column: %T %s", val, val) return } case typeDateTime, typeDateTimeN, typeDateTim4: + var t time.Time switch val := val.(type) { case time.Time: - if col.ti.Size == 4 { - res.buffer = encodeDateTim4(val) - res.ti.Size = len(res.buffer) - } else if col.ti.Size == 8 { - res.buffer = encodeDateTime(val) - res.ti.Size = len(res.buffer) - } else { - err = fmt.Errorf("mssql: invalid size of column") + t = val + case string: + if t, err = time.Parse(sqlTimeFormat, val); err != nil { + return res, fmt.Errorf("bulk: unable to convert string to date: %v", err) } - default: - err = fmt.Errorf("mssql: invalid type for datetime column: %s", val) + err = fmt.Errorf("mssql: invalid type for datetime column: %T %s", val, val) + return + } + + if col.ti.Size == 4 { + res.buffer = encodeDateTim4(t) + res.ti.Size = len(res.buffer) + } else if col.ti.Size == 8 { + res.buffer = encodeDateTime(t) + res.ti.Size = len(res.buffer) + } else { + err = fmt.Errorf("mssql: invalid size of column %d", col.ti.Size) } // case typeMoney, typeMoney4, typeMoneyN: case typeDecimal, typeDecimalN, typeNumeric, typeNumericN: - var value float64 + prec := col.ti.Prec + scale := col.ti.Scale + var dec decimal.Decimal switch v := val.(type) { case int: - value = float64(v) + dec = decimal.Int64ToDecimalScale(int64(v), 0) case int8: - value = float64(v) + dec = decimal.Int64ToDecimalScale(int64(v), 0) case int16: - value = float64(v) + dec = decimal.Int64ToDecimalScale(int64(v), 0) case int32: - value = float64(v) + dec = decimal.Int64ToDecimalScale(int64(v), 0) case int64: - value = float64(v) + dec = decimal.Int64ToDecimalScale(int64(v), 0) case float32: - value = float64(v) + dec, err = decimal.Float64ToDecimalScale(float64(v), scale) case float64: - value = v + dec, err = decimal.Float64ToDecimalScale(float64(v), scale) case string: - if value, err = strconv.ParseFloat(v, 64); err != nil { - return res, fmt.Errorf("bulk: unable to convert string to float: %v", err) - } + dec, err = decimal.StringToDecimalScale(v, scale) default: - return res, fmt.Errorf("unknown value for decimal: %#v", v) + return res, fmt.Errorf("unknown value for decimal: %T %#v", v, v) } - perc := col.ti.Prec - scale := col.ti.Scale - var dec Decimal - dec, err = Float64ToDecimalScale(value, scale) if err != nil { return res, err } - dec.prec = perc + dec.SetPrec(prec) var length byte switch { - case perc <= 9: + case prec <= 9: length = 4 - case perc <= 19: + case prec <= 19: length = 8 - case perc <= 28: + case prec <= 28: length = 12 default: length = 16 @@ -504,7 +533,7 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) // first byte length written by typeInfo.writer res.ti.Size = int(length) + 1 // second byte sign - if value < 0 { + if !dec.IsPositive() { buf[0] = 0 } else { buf[0] = 1 @@ -527,7 +556,7 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) res.ti.Size = len(val) res.buffer = val default: - err = fmt.Errorf("mssql: invalid type for Binary column: %s", val) + err = fmt.Errorf("mssql: invalid type for Binary column: %T %s", val, val) return } case typeGuid: @@ -536,7 +565,7 @@ func (b *Bulk) makeParam(val DataValue, col columnStruct) (res param, err error) res.ti.Size = len(val) res.buffer = val default: - err = fmt.Errorf("mssql: invalid type for Guid column: %s", val) + err = fmt.Errorf("mssql: invalid type for Guid column: %T %s", val, val) return } diff --git a/vendor/github.com/denisenkom/go-mssqldb/conn_str.go b/vendor/github.com/denisenkom/go-mssqldb/conn_str.go new file mode 100644 index 00000000000..26ac50f38df --- /dev/null +++ b/vendor/github.com/denisenkom/go-mssqldb/conn_str.go @@ -0,0 +1,471 @@ +package mssql + +import ( + "fmt" + "net" + "net/url" + "os" + "strconv" + "strings" + "time" + "unicode" +) + +const defaultServerPort = 1433 + +type connectParams struct { + logFlags uint64 + port uint64 + host string + instance string + database string + user string + password string + dial_timeout time.Duration + conn_timeout time.Duration + keepAlive time.Duration + encrypt bool + disableEncryption bool + trustServerCertificate bool + certificate string + hostInCertificate string + hostInCertificateProvided bool + serverSPN string + workstation string + appname string + typeFlags uint8 + failOverPartner string + failOverPort uint64 + packetSize uint16 + fedAuthAccessToken string +} + +func parseConnectParams(dsn string) (connectParams, error) { + var p connectParams + + var params map[string]string + if strings.HasPrefix(dsn, "odbc:") { + parameters, err := splitConnectionStringOdbc(dsn[len("odbc:"):]) + if err != nil { + return p, err + } + params = parameters + } else if strings.HasPrefix(dsn, "sqlserver://") { + parameters, err := splitConnectionStringURL(dsn) + if err != nil { + return p, err + } + params = parameters + } else { + params = splitConnectionString(dsn) + } + + strlog, ok := params["log"] + if ok { + var err error + p.logFlags, err = strconv.ParseUint(strlog, 10, 64) + if err != nil { + return p, fmt.Errorf("Invalid log parameter '%s': %s", strlog, err.Error()) + } + } + server := params["server"] + parts := strings.SplitN(server, `\`, 2) + p.host = parts[0] + if p.host == "." || strings.ToUpper(p.host) == "(LOCAL)" || p.host == "" { + p.host = "localhost" + } + if len(parts) > 1 { + p.instance = parts[1] + } + p.database = params["database"] + p.user = params["user id"] + p.password = params["password"] + + p.port = 0 + strport, ok := params["port"] + if ok { + var err error + p.port, err = strconv.ParseUint(strport, 10, 16) + if err != nil { + f := "Invalid tcp port '%v': %v" + return p, fmt.Errorf(f, strport, err.Error()) + } + } + + // https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-network-packet-size-server-configuration-option + // Default packet size remains at 4096 bytes + p.packetSize = 4096 + strpsize, ok := params["packet size"] + if ok { + var err error + psize, err := strconv.ParseUint(strpsize, 0, 16) + if err != nil { + f := "Invalid packet size '%v': %v" + return p, fmt.Errorf(f, strpsize, err.Error()) + } + + // Ensure packet size falls within the TDS protocol range of 512 to 32767 bytes + // NOTE: Encrypted connections have a maximum size of 16383 bytes. If you request + // a higher packet size, the server will respond with an ENVCHANGE request to + // alter the packet size to 16383 bytes. + p.packetSize = uint16(psize) + if p.packetSize < 512 { + p.packetSize = 512 + } else if p.packetSize > 32767 { + p.packetSize = 32767 + } + } + + // https://msdn.microsoft.com/en-us/library/dd341108.aspx + // + // Do not set a connection timeout. Use Context to manage such things. + // Default to zero, but still allow it to be set. + if strconntimeout, ok := params["connection timeout"]; ok { + timeout, err := strconv.ParseUint(strconntimeout, 10, 64) + if err != nil { + f := "Invalid connection timeout '%v': %v" + return p, fmt.Errorf(f, strconntimeout, err.Error()) + } + p.conn_timeout = time.Duration(timeout) * time.Second + } + p.dial_timeout = 15 * time.Second + if strdialtimeout, ok := params["dial timeout"]; ok { + timeout, err := strconv.ParseUint(strdialtimeout, 10, 64) + if err != nil { + f := "Invalid dial timeout '%v': %v" + return p, fmt.Errorf(f, strdialtimeout, err.Error()) + } + p.dial_timeout = time.Duration(timeout) * time.Second + } + + // default keep alive should be 30 seconds according to spec: + // https://msdn.microsoft.com/en-us/library/dd341108.aspx + p.keepAlive = 30 * time.Second + if keepAlive, ok := params["keepalive"]; ok { + timeout, err := strconv.ParseUint(keepAlive, 10, 64) + if err != nil { + f := "Invalid keepAlive value '%s': %s" + return p, fmt.Errorf(f, keepAlive, err.Error()) + } + p.keepAlive = time.Duration(timeout) * time.Second + } + encrypt, ok := params["encrypt"] + if ok { + if strings.EqualFold(encrypt, "DISABLE") { + p.disableEncryption = true + } else { + var err error + p.encrypt, err = strconv.ParseBool(encrypt) + if err != nil { + f := "Invalid encrypt '%s': %s" + return p, fmt.Errorf(f, encrypt, err.Error()) + } + } + } else { + p.trustServerCertificate = true + } + trust, ok := params["trustservercertificate"] + if ok { + var err error + p.trustServerCertificate, err = strconv.ParseBool(trust) + if err != nil { + f := "Invalid trust server certificate '%s': %s" + return p, fmt.Errorf(f, trust, err.Error()) + } + } + p.certificate = params["certificate"] + p.hostInCertificate, ok = params["hostnameincertificate"] + if ok { + p.hostInCertificateProvided = true + } else { + p.hostInCertificate = p.host + p.hostInCertificateProvided = false + } + + serverSPN, ok := params["serverspn"] + if ok { + p.serverSPN = serverSPN + } else { + p.serverSPN = generateSpn(p.host, resolveServerPort(p.port)) + } + + workstation, ok := params["workstation id"] + if ok { + p.workstation = workstation + } else { + workstation, err := os.Hostname() + if err == nil { + p.workstation = workstation + } + } + + appname, ok := params["app name"] + if !ok { + appname = "go-mssqldb" + } + p.appname = appname + + appintent, ok := params["applicationintent"] + if ok { + if appintent == "ReadOnly" { + if p.database == "" { + return p, fmt.Errorf("Database must be specified when ApplicationIntent is ReadOnly") + } + p.typeFlags |= fReadOnlyIntent + } + } + + failOverPartner, ok := params["failoverpartner"] + if ok { + p.failOverPartner = failOverPartner + } + + failOverPort, ok := params["failoverport"] + if ok { + var err error + p.failOverPort, err = strconv.ParseUint(failOverPort, 0, 16) + if err != nil { + f := "Invalid tcp port '%v': %v" + return p, fmt.Errorf(f, failOverPort, err.Error()) + } + } + + return p, nil +} + +func splitConnectionString(dsn string) (res map[string]string) { + res = map[string]string{} + parts := strings.Split(dsn, ";") + for _, part := range parts { + if len(part) == 0 { + continue + } + lst := strings.SplitN(part, "=", 2) + name := strings.TrimSpace(strings.ToLower(lst[0])) + if len(name) == 0 { + continue + } + var value string = "" + if len(lst) > 1 { + value = strings.TrimSpace(lst[1]) + } + res[name] = value + } + return res +} + +// Splits a URL of the form sqlserver://username:password@host/instance?param1=value¶m2=value +func splitConnectionStringURL(dsn string) (map[string]string, error) { + res := map[string]string{} + + u, err := url.Parse(dsn) + if err != nil { + return res, err + } + + if u.Scheme != "sqlserver" { + return res, fmt.Errorf("scheme %s is not recognized", u.Scheme) + } + + if u.User != nil { + res["user id"] = u.User.Username() + p, exists := u.User.Password() + if exists { + res["password"] = p + } + } + + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + host = u.Host + } + + if len(u.Path) > 0 { + res["server"] = host + "\\" + u.Path[1:] + } else { + res["server"] = host + } + + if len(port) > 0 { + res["port"] = port + } + + query := u.Query() + for k, v := range query { + if len(v) > 1 { + return res, fmt.Errorf("key %s provided more than once", k) + } + res[strings.ToLower(k)] = v[0] + } + + return res, nil +} + +// Splits a URL in the ODBC format +func splitConnectionStringOdbc(dsn string) (map[string]string, error) { + res := map[string]string{} + + type parserState int + const ( + // Before the start of a key + parserStateBeforeKey parserState = iota + + // Inside a key + parserStateKey + + // Beginning of a value. May be bare or braced + parserStateBeginValue + + // Inside a bare value + parserStateBareValue + + // Inside a braced value + parserStateBracedValue + + // A closing brace inside a braced value. + // May be the end of the value or an escaped closing brace, depending on the next character + parserStateBracedValueClosingBrace + + // After a value. Next character should be a semicolon or whitespace. + parserStateEndValue + ) + + var state = parserStateBeforeKey + + var key string + var value string + + for i, c := range dsn { + switch state { + case parserStateBeforeKey: + switch { + case c == '=': + return res, fmt.Errorf("Unexpected character = at index %d. Expected start of key or semi-colon or whitespace.", i) + case !unicode.IsSpace(c) && c != ';': + state = parserStateKey + key += string(c) + } + + case parserStateKey: + switch c { + case '=': + key = normalizeOdbcKey(key) + state = parserStateBeginValue + + case ';': + // Key without value + key = normalizeOdbcKey(key) + res[key] = value + key = "" + value = "" + state = parserStateBeforeKey + + default: + key += string(c) + } + + case parserStateBeginValue: + switch { + case c == '{': + state = parserStateBracedValue + case c == ';': + // Empty value + res[key] = value + key = "" + state = parserStateBeforeKey + case unicode.IsSpace(c): + // Ignore whitespace + default: + state = parserStateBareValue + value += string(c) + } + + case parserStateBareValue: + if c == ';' { + res[key] = strings.TrimRightFunc(value, unicode.IsSpace) + key = "" + value = "" + state = parserStateBeforeKey + } else { + value += string(c) + } + + case parserStateBracedValue: + if c == '}' { + state = parserStateBracedValueClosingBrace + } else { + value += string(c) + } + + case parserStateBracedValueClosingBrace: + if c == '}' { + // Escaped closing brace + value += string(c) + state = parserStateBracedValue + continue + } + + // End of braced value + res[key] = value + key = "" + value = "" + + // This character is the first character past the end, + // so it needs to be parsed like the parserStateEndValue state. + state = parserStateEndValue + switch { + case c == ';': + state = parserStateBeforeKey + case unicode.IsSpace(c): + // Ignore whitespace + default: + return res, fmt.Errorf("Unexpected character %c at index %d. Expected semi-colon or whitespace.", c, i) + } + + case parserStateEndValue: + switch { + case c == ';': + state = parserStateBeforeKey + case unicode.IsSpace(c): + // Ignore whitespace + default: + return res, fmt.Errorf("Unexpected character %c at index %d. Expected semi-colon or whitespace.", c, i) + } + } + } + + switch state { + case parserStateBeforeKey: // Okay + case parserStateKey: // Unfinished key. Treat as key without value. + key = normalizeOdbcKey(key) + res[key] = value + case parserStateBeginValue: // Empty value + res[key] = value + case parserStateBareValue: + res[key] = strings.TrimRightFunc(value, unicode.IsSpace) + case parserStateBracedValue: + return res, fmt.Errorf("Unexpected end of braced value at index %d.", len(dsn)) + case parserStateBracedValueClosingBrace: // End of braced value + res[key] = value + case parserStateEndValue: // Okay + } + + return res, nil +} + +// Normalizes the given string as an ODBC-format key +func normalizeOdbcKey(s string) string { + return strings.ToLower(strings.TrimRightFunc(s, unicode.IsSpace)) +} + +func resolveServerPort(port uint64) uint64 { + if port == 0 { + return defaultServerPort + } + + return port +} + +func generateSpn(host string, port uint64) string { + return fmt.Sprintf("MSSQLSvc/%s:%d", host, port) +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/decimal.go b/vendor/github.com/denisenkom/go-mssqldb/decimal.go deleted file mode 100644 index 372f64b4eb1..00000000000 --- a/vendor/github.com/denisenkom/go-mssqldb/decimal.go +++ /dev/null @@ -1,131 +0,0 @@ -package mssql - -import ( - "encoding/binary" - "errors" - "math" - "math/big" -) - -// http://msdn.microsoft.com/en-us/library/ee780893.aspx -type Decimal struct { - integer [4]uint32 - positive bool - prec uint8 - scale uint8 -} - -var scaletblflt64 [39]float64 - -func (d Decimal) ToFloat64() float64 { - val := float64(0) - for i := 3; i >= 0; i-- { - val *= 0x100000000 - val += float64(d.integer[i]) - } - if !d.positive { - val = -val - } - if d.scale != 0 { - val /= scaletblflt64[d.scale] - } - return val -} - -const autoScale = 100 - -func Float64ToDecimal(f float64) (Decimal, error) { - return Float64ToDecimalScale(f, autoScale) -} - -func Float64ToDecimalScale(f float64, scale uint8) (Decimal, error) { - var dec Decimal - if math.IsNaN(f) { - return dec, errors.New("NaN") - } - if math.IsInf(f, 0) { - return dec, errors.New("Infinity can't be converted to decimal") - } - dec.positive = f >= 0 - if !dec.positive { - f = math.Abs(f) - } - if f > 3.402823669209385e+38 { - return dec, errors.New("Float value is out of range") - } - dec.prec = 20 - var integer float64 - for dec.scale = 0; dec.scale <= scale; dec.scale++ { - integer = f * scaletblflt64[dec.scale] - _, frac := math.Modf(integer) - if frac == 0 && scale == autoScale { - break - } - } - for i := 0; i < 4; i++ { - mod := math.Mod(integer, 0x100000000) - integer -= mod - integer /= 0x100000000 - dec.integer[i] = uint32(mod) - } - return dec, nil -} - -func init() { - var acc float64 = 1 - for i := 0; i <= 38; i++ { - scaletblflt64[i] = acc - acc *= 10 - } -} - -func (d Decimal) BigInt() big.Int { - bytes := make([]byte, 16) - binary.BigEndian.PutUint32(bytes[0:4], d.integer[3]) - binary.BigEndian.PutUint32(bytes[4:8], d.integer[2]) - binary.BigEndian.PutUint32(bytes[8:12], d.integer[1]) - binary.BigEndian.PutUint32(bytes[12:16], d.integer[0]) - var x big.Int - x.SetBytes(bytes) - if !d.positive { - x.Neg(&x) - } - return x -} - -func (d Decimal) Bytes() []byte { - x := d.BigInt() - return scaleBytes(x.String(), d.scale) -} - -func (d Decimal) UnscaledBytes() []byte { - x := d.BigInt() - return x.Bytes() -} - -func scaleBytes(s string, scale uint8) []byte { - z := make([]byte, 0, len(s)+1) - if s[0] == '-' || s[0] == '+' { - z = append(z, byte(s[0])) - s = s[1:] - } - pos := len(s) - int(scale) - if pos <= 0 { - z = append(z, byte('0')) - } else if pos > 0 { - z = append(z, []byte(s[:pos])...) - } - if scale > 0 { - z = append(z, byte('.')) - for pos < 0 { - z = append(z, byte('0')) - pos++ - } - z = append(z, []byte(s[pos:])...) - } - return z -} - -func (d Decimal) String() string { - return string(d.Bytes()) -} diff --git a/vendor/github.com/denisenkom/go-mssqldb/go.mod b/vendor/github.com/denisenkom/go-mssqldb/go.mod new file mode 100644 index 00000000000..ebc02ab88f9 --- /dev/null +++ b/vendor/github.com/denisenkom/go-mssqldb/go.mod @@ -0,0 +1,8 @@ +module github.com/denisenkom/go-mssqldb + +go 1.11 + +require ( + github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe + golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c +) diff --git a/vendor/github.com/denisenkom/go-mssqldb/go.sum b/vendor/github.com/denisenkom/go-mssqldb/go.sum new file mode 100644 index 00000000000..1887801bbf4 --- /dev/null +++ b/vendor/github.com/denisenkom/go-mssqldb/go.sum @@ -0,0 +1,5 @@ +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/denisenkom/go-mssqldb/internal/decimal/decimal.go b/vendor/github.com/denisenkom/go-mssqldb/internal/decimal/decimal.go new file mode 100644 index 00000000000..7da0375d91d --- /dev/null +++ b/vendor/github.com/denisenkom/go-mssqldb/internal/decimal/decimal.go @@ -0,0 +1,252 @@ +package decimal + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + "math/big" + "strings" +) + +// Decimal represents decimal type in the Microsoft Open Specifications: http://msdn.microsoft.com/en-us/library/ee780893.aspx +type Decimal struct { + integer [4]uint32 // Little-endian + positive bool + prec uint8 + scale uint8 +} + +var ( + scaletblflt64 [39]float64 + int10 big.Int + int1e5 big.Int +) + +func init() { + var acc float64 = 1 + for i := 0; i <= 38; i++ { + scaletblflt64[i] = acc + acc *= 10 + } + + int10.SetInt64(10) + int1e5.SetInt64(1e5) +} + +const autoScale = 100 + +// SetInteger sets the ind'th element in the integer array +func (d *Decimal) SetInteger(integer uint32, ind uint8) { + d.integer[ind] = integer +} + +// SetPositive sets the positive member +func (d *Decimal) SetPositive(positive bool) { + d.positive = positive +} + +// SetPrec sets the prec member +func (d *Decimal) SetPrec(prec uint8) { + d.prec = prec +} + +// SetScale sets the scale member +func (d *Decimal) SetScale(scale uint8) { + d.scale = scale +} + +// IsPositive returns true if the Decimal is positive +func (d *Decimal) IsPositive() bool { + return d.positive +} + +// ToFloat64 converts decimal to a float64 +func (d Decimal) ToFloat64() float64 { + val := float64(0) + for i := 3; i >= 0; i-- { + val *= 0x100000000 + val += float64(d.integer[i]) + } + if !d.positive { + val = -val + } + if d.scale != 0 { + val /= scaletblflt64[d.scale] + } + return val +} + +// BigInt converts decimal to a bigint +func (d Decimal) BigInt() big.Int { + bytes := make([]byte, 16) + binary.BigEndian.PutUint32(bytes[0:4], d.integer[3]) + binary.BigEndian.PutUint32(bytes[4:8], d.integer[2]) + binary.BigEndian.PutUint32(bytes[8:12], d.integer[1]) + binary.BigEndian.PutUint32(bytes[12:16], d.integer[0]) + var x big.Int + x.SetBytes(bytes) + if !d.positive { + x.Neg(&x) + } + return x +} + +// Bytes converts decimal to a scaled byte slice +func (d Decimal) Bytes() []byte { + x := d.BigInt() + return ScaleBytes(x.String(), d.scale) +} + +// UnscaledBytes converts decimal to a unscaled byte slice +func (d Decimal) UnscaledBytes() []byte { + x := d.BigInt() + return x.Bytes() +} + +// String converts decimal to a string +func (d Decimal) String() string { + return string(d.Bytes()) +} + +// Float64ToDecimal converts float64 to decimal +func Float64ToDecimal(f float64) (Decimal, error) { + return Float64ToDecimalScale(f, autoScale) +} + +// Float64ToDecimalScale converts float64 to decimal; user can specify the scale +func Float64ToDecimalScale(f float64, scale uint8) (Decimal, error) { + var dec Decimal + if math.IsNaN(f) { + return dec, errors.New("NaN") + } + if math.IsInf(f, 0) { + return dec, errors.New("Infinity can't be converted to decimal") + } + dec.positive = f >= 0 + if !dec.positive { + f = math.Abs(f) + } + if f > 3.402823669209385e+38 { + return dec, errors.New("Float value is out of range") + } + dec.prec = 20 + var integer float64 + for dec.scale = 0; dec.scale <= scale; dec.scale++ { + integer = f * scaletblflt64[dec.scale] + _, frac := math.Modf(integer) + if frac == 0 && scale == autoScale { + break + } + } + for i := 0; i < 4; i++ { + mod := math.Mod(integer, 0x100000000) + integer -= mod + integer /= 0x100000000 + dec.integer[i] = uint32(mod) + if mod-math.Trunc(mod) >= 0.5 { + dec.integer[i] = uint32(mod) + 1 + } + } + return dec, nil +} + +// Int64ToDecimalScale converts float64 to decimal; user can specify the scale +func Int64ToDecimalScale(v int64, scale uint8) Decimal { + positive := v >= 0 + if !positive { + if v == math.MinInt64 { + // Special case - can't negate + return Decimal{ + integer: [4]uint32{0, 0x80000000, 0, 0}, + positive: false, + prec: 20, + scale: 0, + } + } + v = -v + } + return Decimal{ + integer: [4]uint32{uint32(v), uint32(v >> 32), 0, 0}, + positive: positive, + prec: 20, + scale: scale, + } +} + +// StringToDecimalScale converts string to decimal +func StringToDecimalScale(v string, outScale uint8) (Decimal, error) { + var r big.Int + var unscaled string + var inScale int + + point := strings.LastIndexByte(v, '.') + if point == -1 { + inScale = 0 + unscaled = v + } else { + inScale = len(v) - point - 1 + unscaled = v[:point] + v[point+1:] + } + if inScale > math.MaxUint8 { + return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: scale too large", v) + } + + _, ok := r.SetString(unscaled, 10) + if !ok { + return Decimal{}, fmt.Errorf("can't parse %q as a decimal number", v) + } + + if inScale > int(outScale) { + return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: scale %d is larger than the scale %d of the target column", v, inScale, outScale) + } + for inScale < int(outScale) { + if int(outScale)-inScale >= 5 { + r.Mul(&r, &int1e5) + inScale += 5 + } else { + r.Mul(&r, &int10) + inScale++ + } + } + + bytes := r.Bytes() + if len(bytes) > 16 { + return Decimal{}, fmt.Errorf("can't parse %q as a decimal number: precision too large", v) + } + var out [4]uint32 + for i, b := range bytes { + pos := len(bytes) - i - 1 + out[pos/4] += uint32(b) << uint(pos%4*8) + } + return Decimal{ + integer: out, + positive: r.Sign() >= 0, + prec: 20, + scale: uint8(inScale), + }, nil +} + +// ScaleBytes converts a stringified decimal to a scaled byte slice +func ScaleBytes(s string, scale uint8) []byte { + z := make([]byte, 0, len(s)+1) + if s[0] == '-' || s[0] == '+' { + z = append(z, byte(s[0])) + s = s[1:] + } + pos := len(s) - int(scale) + if pos <= 0 { + z = append(z, byte('0')) + } else if pos > 0 { + z = append(z, []byte(s[:pos])...) + } + if scale > 0 { + z = append(z, byte('.')) + for pos < 0 { + z = append(z, byte('0')) + pos++ + } + z = append(z, []byte(s[pos:])...) + } + return z +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/parser.go b/vendor/github.com/denisenkom/go-mssqldb/internal/querytext/parser.go similarity index 89% rename from vendor/github.com/denisenkom/go-mssqldb/parser.go rename to vendor/github.com/denisenkom/go-mssqldb/internal/querytext/parser.go index 8021ca603c9..14650e38944 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/parser.go +++ b/vendor/github.com/denisenkom/go-mssqldb/internal/querytext/parser.go @@ -1,4 +1,8 @@ -package mssql +// Package querytext is the old query parser and parameter substitute process. +// Do not use on new code. +// +// This package is not subject to any API compatibility guarantee. +package querytext import ( "bytes" @@ -40,7 +44,11 @@ func (p *parser) write(ch rune) { type stateFunc func(*parser) stateFunc -func parseParams(query string) (string, int) { +// ParseParams rewrites the query from using "?" placeholders +// to using "@pN" parameter names that SQL Server will accept. +// +// This function and package is not subject to any API compatibility guarantee. +func ParseParams(query string) (string, int) { p := &parser{ r: bytes.NewReader([]byte(query)), namedParams: map[string]bool{}, diff --git a/vendor/github.com/denisenkom/go-mssqldb/mssql.go b/vendor/github.com/denisenkom/go-mssqldb/mssql.go index aa173b3ed51..a74bc7e3fc4 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/mssql.go +++ b/vendor/github.com/denisenkom/go-mssqldb/mssql.go @@ -14,6 +14,8 @@ import ( "strings" "time" "unicode" + + "github.com/denisenkom/go-mssqldb/internal/querytext" ) // ReturnStatus may be used to return the return value from a proc. @@ -29,24 +31,19 @@ var driverInstanceNoProcess = &Driver{processQueryText: false} func init() { sql.Register("mssql", driverInstance) sql.Register("sqlserver", driverInstanceNoProcess) - createDialer = func(p *connectParams) dialer { - return tcpDialer{&net.Dialer{KeepAlive: p.keepAlive}} + createDialer = func(p *connectParams) Dialer { + return netDialer{&net.Dialer{KeepAlive: p.keepAlive}} } } -// Abstract the dialer for testing and for non-TCP based connections. -type dialer interface { - Dial(ctx context.Context, addr string) (net.Conn, error) -} - -var createDialer func(p *connectParams) dialer +var createDialer func(p *connectParams) Dialer -type tcpDialer struct { +type netDialer struct { nd *net.Dialer } -func (d tcpDialer) Dial(ctx context.Context, addr string) (net.Conn, error) { - return d.nd.DialContext(ctx, "tcp", addr) +func (d netDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) { + return d.nd.DialContext(ctx, network, addr) } type Driver struct { @@ -125,6 +122,21 @@ type Connector struct { // SessionInitSQL is optional. The session will be reset even if // SessionInitSQL is empty. SessionInitSQL string + + // Dialer sets a custom dialer for all network operations. + // If Dialer is not set, normal net dialers are used. + Dialer Dialer +} + +type Dialer interface { + DialContext(ctx context.Context, network string, addr string) (net.Conn, error) +} + +func (c *Connector) getDialer(p *connectParams) Dialer { + if c != nil && c.Dialer != nil { + return c.Dialer + } + return createDialer(p) } type Conn struct { @@ -310,12 +322,12 @@ func (d *Driver) open(ctx context.Context, dsn string) (*Conn, error) { if err != nil { return nil, err } - return d.connect(ctx, params) + return d.connect(ctx, nil, params) } // connect to the server, using the provided context for dialing only. -func (d *Driver) connect(ctx context.Context, params connectParams) (*Conn, error) { - sess, err := connect(ctx, d.log, params) +func (d *Driver) connect(ctx context.Context, c *Connector, params connectParams) (*Conn, error) { + sess, err := connect(ctx, c, d.log, params) if err != nil { // main server failed, try fail-over partner if params.failOverPartner == "" { @@ -327,7 +339,7 @@ func (d *Driver) connect(ctx context.Context, params connectParams) (*Conn, erro params.port = params.failOverPort } - sess, err = connect(ctx, d.log, params) + sess, err = connect(ctx, c, d.log, params) if err != nil { // fail-over partner also failed, now fail return nil, err @@ -335,12 +347,12 @@ func (d *Driver) connect(ctx context.Context, params connectParams) (*Conn, erro } conn := &Conn{ + connector: c, sess: sess, transactionCtx: context.Background(), processQueryText: d.processQueryText, connectionGood: true, } - conn.sess.log = d.log return conn, nil } @@ -375,7 +387,7 @@ func (c *Conn) Prepare(query string) (driver.Stmt, error) { func (c *Conn) prepareContext(ctx context.Context, query string) (*Stmt, error) { paramCount := -1 if c.processQueryText { - query, paramCount = parseParams(query) + query, paramCount = querytext.ParseParams(query) } return &Stmt{c, query, paramCount, nil}, nil } @@ -385,7 +397,10 @@ func (s *Stmt) Close() error { } func (s *Stmt) SetQueryNotification(id, options string, timeout time.Duration) { - to := uint32(timeout / time.Second) + // 2.2.5.3.1 Query Notifications Header + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/e168d373-a7b7-41aa-b6ca-25985466a7e0 + // Timeout in milliseconds in TDS protocol. + to := uint32(timeout / time.Millisecond) if to < 1 { to = 1 } @@ -445,13 +460,13 @@ func (s *Stmt) sendQuery(args []namedValue) (err error) { var params []param if isProc(s.query) { proc.name = s.query - params, _, err = s.makeRPCParams(args, 0) + params, _, err = s.makeRPCParams(args, true) if err != nil { return } } else { var decls []string - params, decls, err = s.makeRPCParams(args, 2) + params, decls, err = s.makeRPCParams(args, false) if err != nil { return } @@ -523,8 +538,12 @@ func isProc(s string) bool { return true } -func (s *Stmt) makeRPCParams(args []namedValue, offset int) ([]param, []string, error) { +func (s *Stmt) makeRPCParams(args []namedValue, isProc bool) ([]param, []string, error) { var err error + var offset int + if !isProc { + offset = 2 + } params := make([]param, len(args)+offset) decls := make([]string, len(args)) for i, val := range args { @@ -535,7 +554,7 @@ func (s *Stmt) makeRPCParams(args []namedValue, offset int) ([]param, []string, var name string if len(val.Name) > 0 { name = "@" + val.Name - } else { + } else if !isProc { name = fmt.Sprintf("@p%d", val.Ordinal) } params[i+offset].Name = name @@ -597,11 +616,13 @@ loop: break loop case doneStruct: if token.isError() { + cancel() return nil, s.c.checkBadConn(token.getError()) } case ReturnStatus: s.c.setReturnStatus(token) case error: + cancel() return nil, s.c.checkBadConn(token) } } @@ -700,6 +721,8 @@ func (rc *Rows) Next(dest []driver.Value) error { if tokdata.isError() { return rc.stmt.c.checkBadConn(tokdata.getError()) } + case ReturnStatus: + rc.stmt.c.setReturnStatus(tokdata) case error: return rc.stmt.c.checkBadConn(tokdata) } @@ -858,29 +881,6 @@ func (r *Result) RowsAffected() (int64, error) { return r.rowsAffected, nil } -func (r *Result) LastInsertId() (int64, error) { - s, err := r.c.Prepare("select cast(@@identity as bigint)") - if err != nil { - return 0, err - } - defer s.Close() - rows, err := s.Query(nil) - if err != nil { - return 0, err - } - defer rows.Close() - dest := make([]driver.Value, 1) - err = rows.Next(dest) - if err != nil { - return 0, err - } - if dest[0] == nil { - return -1, errors.New("There is no generated identity value") - } - lastInsertId := dest[0].(int64) - return lastInsertId, nil -} - var _ driver.Pinger = &Conn{} // Ping is used to check if the remote server is available and satisfies the Pinger interface. diff --git a/vendor/github.com/denisenkom/go-mssqldb/mssql_go110.go b/vendor/github.com/denisenkom/go-mssqldb/mssql_go110.go index 3d5ab57a526..6d76fbad08f 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/mssql_go110.go +++ b/vendor/github.com/denisenkom/go-mssqldb/mssql_go110.go @@ -5,6 +5,7 @@ package mssql import ( "context" "database/sql/driver" + "errors" ) var _ driver.Connector = &Connector{} @@ -34,10 +35,7 @@ func (c *Conn) ResetSession(ctx context.Context) error { // Connect to the server and return a TDS connection. func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) { - conn, err := c.driver.connect(ctx, c.params) - if conn != nil { - conn.connector = c - } + conn, err := c.driver.connect(ctx, c, c.params) if err == nil { err = conn.ResetSession(ctx) } @@ -48,3 +46,7 @@ func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) { func (c *Connector) Driver() driver.Driver { return c.driver } + +func (r *Result) LastInsertId() (int64, error) { + return -1, errors.New("LastInsertId is not supported. Please use the OUTPUT clause or add `select ID = convert(bigint, SCOPE_IDENTITY())` to the end of your query.") +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/mssql_go110pre.go b/vendor/github.com/denisenkom/go-mssqldb/mssql_go110pre.go new file mode 100644 index 00000000000..3baae0c4048 --- /dev/null +++ b/vendor/github.com/denisenkom/go-mssqldb/mssql_go110pre.go @@ -0,0 +1,31 @@ +// +build !go1.10 + +package mssql + +import ( + "database/sql/driver" + "errors" +) + +func (r *Result) LastInsertId() (int64, error) { + s, err := r.c.Prepare("select cast(@@identity as bigint)") + if err != nil { + return 0, err + } + defer s.Close() + rows, err := s.Query(nil) + if err != nil { + return 0, err + } + defer rows.Close() + dest := make([]driver.Value, 1) + err = rows.Next(dest) + if err != nil { + return 0, err + } + if dest[0] == nil { + return -1, errors.New("There is no generated identity value") + } + lastInsertId := dest[0].(int64) + return lastInsertId, nil +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/mssql_go19.go b/vendor/github.com/denisenkom/go-mssqldb/mssql_go19.go index 65a11720da2..a2bd1167ba3 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/mssql_go19.go +++ b/vendor/github.com/denisenkom/go-mssqldb/mssql_go19.go @@ -11,7 +11,7 @@ import ( "time" // "github.com/cockroachdb/apd" - "cloud.google.com/go/civil" + "github.com/golang-sql/civil" ) // Type alias provided for compatibility. @@ -112,6 +112,8 @@ func (c *Conn) CheckNamedValue(nv *driver.NamedValue) error { *v = 0 // By default the return value should be zero. c.returnStatus = v return driver.ErrRemoveArgument + case TVP: + return nil default: var err error nv.Value, err = convertInputParameter(nv.Value) @@ -160,6 +162,29 @@ func (s *Stmt) makeParamExtra(val driver.Value) (res param, err error) { case sql.Out: res, err = s.makeParam(val.Dest) res.Flags = fByRevValue + case TVP: + err = val.check() + if err != nil { + return + } + schema, name, errGetName := getSchemeAndName(val.TypeName) + if errGetName != nil { + return + } + res.ti.UdtInfo.TypeName = name + res.ti.UdtInfo.SchemaName = schema + res.ti.TypeId = typeTvp + columnStr, tvpFieldIndexes, errCalTypes := val.columnTypes() + if errCalTypes != nil { + err = errCalTypes + return + } + res.buffer, err = val.encode(schema, name, columnStr, tvpFieldIndexes) + if err != nil { + return + } + res.ti.Size = len(res.buffer) + default: err = fmt.Errorf("mssql: unknown type for %T", val) } diff --git a/vendor/github.com/denisenkom/go-mssqldb/net.go b/vendor/github.com/denisenkom/go-mssqldb/net.go index e3864d1a222..94858cc74fe 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/net.go +++ b/vendor/github.com/denisenkom/go-mssqldb/net.go @@ -9,9 +9,6 @@ import ( type timeoutConn struct { c net.Conn timeout time.Duration - buf *tdsBuffer - packetPending bool - continueRead bool } func newTimeoutConn(conn net.Conn, timeout time.Duration) *timeoutConn { @@ -22,32 +19,6 @@ func newTimeoutConn(conn net.Conn, timeout time.Duration) *timeoutConn { } func (c *timeoutConn) Read(b []byte) (n int, err error) { - if c.buf != nil { - if c.packetPending { - c.packetPending = false - err = c.buf.FinishPacket() - if err != nil { - err = fmt.Errorf("Cannot send handshake packet: %s", err.Error()) - return - } - c.continueRead = false - } - if !c.continueRead { - var packet packetType - packet, err = c.buf.BeginRead() - if err != nil { - err = fmt.Errorf("Cannot read handshake packet: %s", err.Error()) - return - } - if packet != packPrelogin { - err = fmt.Errorf("unexpected packet %d, expecting prelogin", packet) - return - } - c.continueRead = true - } - n, err = c.buf.Read(b) - return - } if c.timeout > 0 { err = c.c.SetDeadline(time.Now().Add(c.timeout)) if err != nil { @@ -58,17 +29,6 @@ func (c *timeoutConn) Read(b []byte) (n int, err error) { } func (c *timeoutConn) Write(b []byte) (n int, err error) { - if c.buf != nil { - if !c.packetPending { - c.buf.BeginPacket(packPrelogin, false) - c.packetPending = true - } - n, err = c.buf.Write(b) - if err != nil { - return - } - return - } if c.timeout > 0 { err = c.c.SetDeadline(time.Now().Add(c.timeout)) if err != nil { @@ -101,3 +61,108 @@ func (c timeoutConn) SetReadDeadline(t time.Time) error { func (c timeoutConn) SetWriteDeadline(t time.Time) error { panic("Not implemented") } + +// this connection is used during TLS Handshake +// TDS protocol requires TLS handshake messages to be sent inside TDS packets +type tlsHandshakeConn struct { + buf *tdsBuffer + packetPending bool + continueRead bool +} + +func (c *tlsHandshakeConn) Read(b []byte) (n int, err error) { + if c.packetPending { + c.packetPending = false + err = c.buf.FinishPacket() + if err != nil { + err = fmt.Errorf("Cannot send handshake packet: %s", err.Error()) + return + } + c.continueRead = false + } + if !c.continueRead { + var packet packetType + packet, err = c.buf.BeginRead() + if err != nil { + err = fmt.Errorf("Cannot read handshake packet: %s", err.Error()) + return + } + if packet != packPrelogin { + err = fmt.Errorf("unexpected packet %d, expecting prelogin", packet) + return + } + c.continueRead = true + } + return c.buf.Read(b) +} + +func (c *tlsHandshakeConn) Write(b []byte) (n int, err error) { + if !c.packetPending { + c.buf.BeginPacket(packPrelogin, false) + c.packetPending = true + } + return c.buf.Write(b) +} + +func (c *tlsHandshakeConn) Close() error { + panic("Not implemented") +} + +func (c *tlsHandshakeConn) LocalAddr() net.Addr { + panic("Not implemented") +} + +func (c *tlsHandshakeConn) RemoteAddr() net.Addr { + panic("Not implemented") +} + +func (c *tlsHandshakeConn) SetDeadline(t time.Time) error { + panic("Not implemented") +} + +func (c *tlsHandshakeConn) SetReadDeadline(t time.Time) error { + panic("Not implemented") +} + +func (c *tlsHandshakeConn) SetWriteDeadline(t time.Time) error { + panic("Not implemented") +} + +// this connection just delegates all methods to it's wrapped connection +// it also allows switching underlying connection on the fly +// it is needed because tls.Conn does not allow switching underlying connection +type passthroughConn struct { + c net.Conn +} + +func (c passthroughConn) Read(b []byte) (n int, err error) { + return c.c.Read(b) +} + +func (c passthroughConn) Write(b []byte) (n int, err error) { + return c.c.Write(b) +} + +func (c passthroughConn) Close() error { + return c.c.Close() +} + +func (c passthroughConn) LocalAddr() net.Addr { + panic("Not implemented") +} + +func (c passthroughConn) RemoteAddr() net.Addr { + panic("Not implemented") +} + +func (c passthroughConn) SetDeadline(t time.Time) error { + panic("Not implemented") +} + +func (c passthroughConn) SetReadDeadline(t time.Time) error { + panic("Not implemented") +} + +func (c passthroughConn) SetWriteDeadline(t time.Time) error { + panic("Not implemented") +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/ntlm.go b/vendor/github.com/denisenkom/go-mssqldb/ntlm.go index 7c0cc4f785c..3ba48ff8d2c 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/ntlm.go +++ b/vendor/github.com/denisenkom/go-mssqldb/ntlm.go @@ -4,11 +4,14 @@ package mssql import ( "crypto/des" + "crypto/hmac" "crypto/md5" "crypto/rand" "encoding/binary" "errors" + "fmt" "strings" + "time" "unicode/utf16" "golang.org/x/crypto/md4" @@ -198,86 +201,209 @@ func ntlmSessionResponse(clientNonce [8]byte, serverChallenge [8]byte, password return response(hash, passwordHash) } -func (auth *ntlmAuth) NextBytes(bytes []byte) ([]byte, error) { - if string(bytes[0:8]) != "NTLMSSP\x00" { - return nil, errorNTLM +func ntlmHashNoPadding(val string) []byte { + hash := make([]byte, 16) + h := md4.New() + h.Write(utf16le(val)) + h.Sum(hash[:0]) + + return hash +} + +func hmacMD5(passwordHash, data []byte) []byte { + hmacEntity := hmac.New(md5.New, passwordHash) + hmacEntity.Write(data) + + return hmacEntity.Sum(nil) +} + +func getNTLMv2AndLMv2ResponsePayloads(target, username, password string, challenge, nonce [8]byte, targetInfoFields []byte, timestamp time.Time) (ntlmV2Payload, lmV2Payload []byte) { + // NTLMv2 response payload: http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response + + ntlmHash := ntlmHashNoPadding(password) + usernameAndTargetBytes := utf16le(strings.ToUpper(username) + target) + ntlmV2Hash := hmacMD5(ntlmHash, usernameAndTargetBytes) + targetInfoLength := len(targetInfoFields) + blob := make([]byte, 32+targetInfoLength) + binary.BigEndian.PutUint32(blob[:4], 0x01010000) + binary.BigEndian.PutUint32(blob[4:8], 0x00000000) + binary.BigEndian.PutUint64(blob[8:16], uint64(timestamp.UnixNano())) + copy(blob[16:24], nonce[:]) + binary.BigEndian.PutUint32(blob[24:28], 0x00000000) + copy(blob[28:], targetInfoFields) + binary.BigEndian.PutUint32(blob[28+targetInfoLength:], 0x00000000) + challengeLength := len(challenge) + blobLength := len(blob) + challengeAndBlob := make([]byte, challengeLength + blobLength) + copy(challengeAndBlob[:challengeLength], challenge[:]) + copy(challengeAndBlob[challengeLength:], blob) + hashedChallenge := hmacMD5(ntlmV2Hash, challengeAndBlob) + ntlmV2Payload = append(hashedChallenge, blob...) + + // LMv2 response payload: http://davenport.sourceforge.net/ntlm.html#theLmv2Response + ntlmV2hash := hmacMD5(ntlmHash, usernameAndTargetBytes) + challengeAndNonce := make([]byte, 16) + copy(challengeAndNonce[:8], challenge[:]) + copy(challengeAndNonce[8:], nonce[:]) + hashedChallenge = hmacMD5(ntlmV2hash, challengeAndNonce) + lmV2Payload = append(hashedChallenge, nonce[:]...) + + return +} + +func negotiateExtendedSessionSecurity(flags uint32, message []byte, challenge [8]byte, username, password string) (lm, nt []byte, err error) { + nonce := clientChallenge() + + // Official specification: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4 + // Unofficial walk through referenced by https://www.freetds.org/userguide/domains.htm: http://davenport.sourceforge.net/ntlm.html + if (flags & _NEGOTIATE_TARGET_INFO) != 0 { + targetInfoFields, target, err := getNTLMv2TargetInfoFields(message) + if err != nil { + return lm, nt, err + } + + nt, lm = getNTLMv2AndLMv2ResponsePayloads(target, username, password, challenge, nonce, targetInfoFields, time.Now()) + + return lm, nt, nil } - if binary.LittleEndian.Uint32(bytes[8:12]) != _CHALLENGE_MESSAGE { - return nil, errorNTLM + + var lm_bytes [24]byte + copy(lm_bytes[:8], nonce[:]) + lm = lm_bytes[:] + nt_bytes := ntlmSessionResponse(nonce, challenge, password) + nt = nt_bytes[:] + + return lm, nt, nil +} + +func getNTLMv2TargetInfoFields(type2Message []byte) (info []byte, target string, err error) { + type2MessageError := "mssql: while parsing NTLMv2 type 2 message, length %d too small for offset %d" + type2MessageLength := len(type2Message) + if type2MessageLength < 20 { + return nil, target, fmt.Errorf(type2MessageError, type2MessageLength, 20) } - flags := binary.LittleEndian.Uint32(bytes[20:24]) - var challenge [8]byte - copy(challenge[:], bytes[24:32]) - var lm, nt []byte - if (flags & _NEGOTIATE_EXTENDED_SESSIONSECURITY) != 0 { - nonce := clientChallenge() - var lm_bytes [24]byte - copy(lm_bytes[:8], nonce[:]) - lm = lm_bytes[:] - nt_bytes := ntlmSessionResponse(nonce, challenge, auth.Password) - nt = nt_bytes[:] - } else { - lm_bytes := lmResponse(challenge, auth.Password) - lm = lm_bytes[:] - nt_bytes := ntResponse(challenge, auth.Password) - nt = nt_bytes[:] + targetNameAllocated := binary.LittleEndian.Uint16(type2Message[14:16]) + targetNameOffset := binary.LittleEndian.Uint32(type2Message[16:20]) + endOfOffset := int(targetNameOffset + uint32(targetNameAllocated)) + if type2MessageLength < endOfOffset { + return nil, target, fmt.Errorf(type2MessageError, type2MessageLength, endOfOffset) + } + + target, err = ucs22str(type2Message[targetNameOffset:targetNameOffset+uint32(targetNameAllocated)]) + if err != nil { + return nil, target, err + } + + targetInformationAllocated := binary.LittleEndian.Uint16(type2Message[42:44]) + targetInformationDataOffset := binary.LittleEndian.Uint32(type2Message[44:48]) + endOfOffset = int(targetInformationDataOffset + uint32(targetInformationAllocated)) + if type2MessageLength < endOfOffset { + return nil, target, fmt.Errorf(type2MessageError, type2MessageLength, endOfOffset) } + + targetInformationBytes := make([]byte, targetInformationAllocated) + copy(targetInformationBytes, type2Message[targetInformationDataOffset:targetInformationDataOffset+uint32(targetInformationAllocated)]) + + return targetInformationBytes, target, nil +} + +func buildNTLMResponsePayload(lm, nt []byte, flags uint32, domain, workstation, username string) ([]byte, error) { lm_len := len(lm) nt_len := len(nt) - - domain16 := utf16le(auth.Domain) + domain16 := utf16le(domain) domain_len := len(domain16) - user16 := utf16le(auth.UserName) + user16 := utf16le(username) user_len := len(user16) - workstation16 := utf16le(auth.Workstation) + workstation16 := utf16le(workstation) workstation_len := len(workstation16) - msg := make([]byte, 88+lm_len+nt_len+domain_len+user_len+workstation_len) copy(msg, []byte("NTLMSSP\x00")) binary.LittleEndian.PutUint32(msg[8:], _AUTHENTICATE_MESSAGE) + // Lm Challenge Response Fields binary.LittleEndian.PutUint16(msg[12:], uint16(lm_len)) binary.LittleEndian.PutUint16(msg[14:], uint16(lm_len)) binary.LittleEndian.PutUint32(msg[16:], 88) + // Nt Challenge Response Fields binary.LittleEndian.PutUint16(msg[20:], uint16(nt_len)) binary.LittleEndian.PutUint16(msg[22:], uint16(nt_len)) binary.LittleEndian.PutUint32(msg[24:], uint32(88+lm_len)) + // Domain Name Fields binary.LittleEndian.PutUint16(msg[28:], uint16(domain_len)) binary.LittleEndian.PutUint16(msg[30:], uint16(domain_len)) binary.LittleEndian.PutUint32(msg[32:], uint32(88+lm_len+nt_len)) + // User Name Fields binary.LittleEndian.PutUint16(msg[36:], uint16(user_len)) binary.LittleEndian.PutUint16(msg[38:], uint16(user_len)) binary.LittleEndian.PutUint32(msg[40:], uint32(88+lm_len+nt_len+domain_len)) + // Workstation Fields binary.LittleEndian.PutUint16(msg[44:], uint16(workstation_len)) binary.LittleEndian.PutUint16(msg[46:], uint16(workstation_len)) binary.LittleEndian.PutUint32(msg[48:], uint32(88+lm_len+nt_len+domain_len+user_len)) + // Encrypted Random Session Key Fields binary.LittleEndian.PutUint16(msg[52:], 0) binary.LittleEndian.PutUint16(msg[54:], 0) binary.LittleEndian.PutUint32(msg[56:], uint32(88+lm_len+nt_len+domain_len+user_len+workstation_len)) + // Negotiate Flags binary.LittleEndian.PutUint32(msg[60:], flags) + // Version binary.LittleEndian.PutUint32(msg[64:], 0) binary.LittleEndian.PutUint32(msg[68:], 0) + // MIC binary.LittleEndian.PutUint32(msg[72:], 0) binary.LittleEndian.PutUint32(msg[76:], 0) binary.LittleEndian.PutUint32(msg[88:], 0) binary.LittleEndian.PutUint32(msg[84:], 0) + // Payload copy(msg[88:], lm) copy(msg[88+lm_len:], nt) copy(msg[88+lm_len+nt_len:], domain16) copy(msg[88+lm_len+nt_len+domain_len:], user16) copy(msg[88+lm_len+nt_len+domain_len+user_len:], workstation16) + return msg, nil } +func (auth *ntlmAuth) NextBytes(bytes []byte) ([]byte, error) { + signature := string(bytes[0:8]) + if signature != "NTLMSSP\x00" { + return nil, errorNTLM + } + + messageTypeIndicator := binary.LittleEndian.Uint32(bytes[8:12]) + if messageTypeIndicator != _CHALLENGE_MESSAGE { + return nil, errorNTLM + } + + var challenge [8]byte + copy(challenge[:], bytes[24:32]) + flags := binary.LittleEndian.Uint32(bytes[20:24]) + if (flags & _NEGOTIATE_EXTENDED_SESSIONSECURITY) != 0 { + lm, nt, err := negotiateExtendedSessionSecurity(flags, bytes, challenge, auth.UserName, auth.Password) + if err != nil { + return nil, err + } + + return buildNTLMResponsePayload(lm, nt, flags, auth.Domain, auth.Workstation, auth.UserName) + } + + lm_bytes := lmResponse(challenge, auth.Password) + lm := lm_bytes[:] + nt_bytes := ntResponse(challenge, auth.Password) + nt := nt_bytes[:] + + return buildNTLMResponsePayload(lm, nt, flags, auth.Domain, auth.Workstation, auth.UserName) +} + func (auth *ntlmAuth) Free() { } diff --git a/vendor/github.com/denisenkom/go-mssqldb/tds.go b/vendor/github.com/denisenkom/go-mssqldb/tds.go index a45711d5517..832c4fd23a0 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/tds.go +++ b/vendor/github.com/denisenkom/go-mssqldb/tds.go @@ -10,13 +10,9 @@ import ( "io" "io/ioutil" "net" - "net/url" - "os" "sort" "strconv" "strings" - "time" - "unicode" "unicode/utf16" "unicode/utf8" ) @@ -50,17 +46,14 @@ func parseInstances(msg []byte) map[string]map[string]string { return results } -func getInstances(ctx context.Context, address string) (map[string]map[string]string, error) { - maxTime := 5 * time.Second - dialer := &net.Dialer{ - Timeout: maxTime, - } - conn, err := dialer.DialContext(ctx, "udp", address+":1434") +func getInstances(ctx context.Context, d Dialer, address string) (map[string]map[string]string, error) { + conn, err := d.DialContext(ctx, "udp", address+":1434") if err != nil { return nil, err } defer conn.Close() - conn.SetDeadline(time.Now().Add(maxTime)) + deadline, _ := ctx.Deadline() + conn.SetDeadline(deadline) _, err = conn.Write([]byte{3}) if err != nil { return nil, err @@ -107,13 +100,15 @@ const ( // prelogin fields // http://msdn.microsoft.com/en-us/library/dd357559.aspx const ( - preloginVERSION = 0 - preloginENCRYPTION = 1 - preloginINSTOPT = 2 - preloginTHREADID = 3 - preloginMARS = 4 - preloginTRACEID = 5 - preloginTERMINATOR = 0xff + preloginVERSION = 0 + preloginENCRYPTION = 1 + preloginINSTOPT = 2 + preloginTHREADID = 3 + preloginMARS = 4 + preloginTRACEID = 5 + preloginFEDAUTHREQUIRED = 6 + preloginNONCEOPT = 7 + preloginTERMINATOR = 0xff ) const ( @@ -252,6 +247,12 @@ const ( fReadOnlyIntent = 32 ) +// OptionFlags3 +// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/773a62b6-ee89-4c02-9e5e-344882630aac +const ( + fExtension = 0x10 +) + type login struct { TDSVersion uint32 PacketSize uint32 @@ -276,6 +277,89 @@ type login struct { SSPI []byte AtchDBFile string ChangePassword string + FeatureExt featureExts +} + +type featureExts struct { + features map[byte]featureExt +} + +type featureExt interface { + featureID() byte + toBytes() []byte +} + +func (e *featureExts) Add(f featureExt) error { + if f == nil { + return nil + } + id := f.featureID() + if _, exists := e.features[id]; exists { + f := "Login error: Feature with ID '%v' is already present in FeatureExt block." + return fmt.Errorf(f, id) + } + if e.features == nil { + e.features = make(map[byte]featureExt) + } + e.features[id] = f + return nil +} + +func (e featureExts) toBytes() []byte { + if len(e.features) == 0 { + return nil + } + var d []byte + for featureID, f := range e.features { + featureData := f.toBytes() + + hdr := make([]byte, 5) + hdr[0] = featureID // FedAuth feature extension BYTE + binary.LittleEndian.PutUint32(hdr[1:], uint32(len(featureData))) // FeatureDataLen DWORD + d = append(d, hdr...) + + d = append(d, featureData...) // FeatureData *BYTE + } + if d != nil { + d = append(d, 0xff) // Terminator + } + return d +} + +type featureExtFedAuthSTS struct { + FedAuthEcho bool + FedAuthToken string + Nonce []byte +} + +func (e *featureExtFedAuthSTS) featureID() byte { + return 0x02 +} + +func (e *featureExtFedAuthSTS) toBytes() []byte { + if e == nil { + return nil + } + + options := byte(0x01) << 1 // 0x01 => STS bFedAuthLibrary 7BIT + if e.FedAuthEcho { + options |= 1 // fFedAuthEcho + } + + d := make([]byte, 5) + d[0] = options + + // looks like string in + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/f88b63bb-b479-49e1-a87b-deda521da508 + tokenBytes := str2ucs2(e.FedAuthToken) + binary.LittleEndian.PutUint32(d[1:], uint32(len(tokenBytes))) // Should be a signed int32, but since the length is relatively small, this should work + d = append(d, tokenBytes...) + + if len(e.Nonce) == 32 { + d = append(d, e.Nonce...) + } + + return d } type loginHeader struct { @@ -302,7 +386,7 @@ type loginHeader struct { ServerNameOffset uint16 ServerNameLength uint16 ExtensionOffset uint16 - ExtensionLenght uint16 + ExtensionLength uint16 CtlIntNameOffset uint16 CtlIntNameLength uint16 LanguageOffset uint16 @@ -364,6 +448,8 @@ func sendLogin(w *tdsBuffer, login login) error { database := str2ucs2(login.Database) atchdbfile := str2ucs2(login.AtchDBFile) changepassword := str2ucs2(login.ChangePassword) + featureExt := login.FeatureExt.toBytes() + hdr := loginHeader{ TDSVersion: login.TDSVersion, PacketSize: login.PacketSize, @@ -412,7 +498,18 @@ func sendLogin(w *tdsBuffer, login login) error { offset += uint16(len(atchdbfile)) hdr.ChangePasswordOffset = offset offset += uint16(len(changepassword)) - hdr.Length = uint32(offset) + + featureExtOffset := uint32(0) + featureExtLen := len(featureExt) + if featureExtLen > 0 { + hdr.OptionFlags3 |= fExtension + hdr.ExtensionOffset = offset + hdr.ExtensionLength = 4 + offset += hdr.ExtensionLength // DWORD + featureExtOffset = uint32(offset) + } + hdr.Length = uint32(offset) + uint32(featureExtLen) + var err error err = binary.Write(w, binary.LittleEndian, &hdr) if err != nil { @@ -462,6 +559,16 @@ func sendLogin(w *tdsBuffer, login login) error { if err != nil { return err } + if featureExtOffset > 0 { + err = binary.Write(w, binary.LittleEndian, featureExtOffset) + if err != nil { + return err + } + _, err = w.Write(featureExt) + if err != nil { + return err + } + } return w.FinishPacket() } @@ -475,10 +582,9 @@ func readUcs2(r io.Reader, numchars int) (res string, err error) { } func readUsVarChar(r io.Reader) (res string, err error) { - var numchars uint16 - err = binary.Read(r, binary.LittleEndian, &numchars) + numchars, err := readUshort(r) if err != nil { - return "", err + return } return readUcs2(r, int(numchars)) } @@ -498,8 +604,7 @@ func writeUsVarChar(w io.Writer, s string) (err error) { } func readBVarChar(r io.Reader) (res string, err error) { - var numchars uint8 - err = binary.Read(r, binary.LittleEndian, &numchars) + numchars, err := readByte(r) if err != nil { return "", err } @@ -526,8 +631,7 @@ func writeBVarChar(w io.Writer, s string) (err error) { } func readBVarByte(r io.Reader) (res []byte, err error) { - var length uint8 - err = binary.Read(r, binary.LittleEndian, &length) + length, err := readByte(r) if err != nil { return } @@ -655,454 +759,6 @@ func sendAttention(buf *tdsBuffer) error { return buf.FinishPacket() } -type connectParams struct { - logFlags uint64 - port uint64 - host string - instance string - database string - user string - password string - dial_timeout time.Duration - conn_timeout time.Duration - keepAlive time.Duration - encrypt bool - disableEncryption bool - trustServerCertificate bool - certificate string - hostInCertificate string - serverSPN string - workstation string - appname string - typeFlags uint8 - failOverPartner string - failOverPort uint64 - packetSize uint16 -} - -func splitConnectionString(dsn string) (res map[string]string) { - res = map[string]string{} - parts := strings.Split(dsn, ";") - for _, part := range parts { - if len(part) == 0 { - continue - } - lst := strings.SplitN(part, "=", 2) - name := strings.TrimSpace(strings.ToLower(lst[0])) - if len(name) == 0 { - continue - } - var value string = "" - if len(lst) > 1 { - value = strings.TrimSpace(lst[1]) - } - res[name] = value - } - return res -} - -// Splits a URL in the ODBC format -func splitConnectionStringOdbc(dsn string) (map[string]string, error) { - res := map[string]string{} - - type parserState int - const ( - // Before the start of a key - parserStateBeforeKey parserState = iota - - // Inside a key - parserStateKey - - // Beginning of a value. May be bare or braced - parserStateBeginValue - - // Inside a bare value - parserStateBareValue - - // Inside a braced value - parserStateBracedValue - - // A closing brace inside a braced value. - // May be the end of the value or an escaped closing brace, depending on the next character - parserStateBracedValueClosingBrace - - // After a value. Next character should be a semicolon or whitespace. - parserStateEndValue - ) - - var state = parserStateBeforeKey - - var key string - var value string - - for i, c := range dsn { - switch state { - case parserStateBeforeKey: - switch { - case c == '=': - return res, fmt.Errorf("Unexpected character = at index %d. Expected start of key or semi-colon or whitespace.", i) - case !unicode.IsSpace(c) && c != ';': - state = parserStateKey - key += string(c) - } - - case parserStateKey: - switch c { - case '=': - key = normalizeOdbcKey(key) - if len(key) == 0 { - return res, fmt.Errorf("Unexpected end of key at index %d.", i) - } - - state = parserStateBeginValue - - case ';': - // Key without value - key = normalizeOdbcKey(key) - if len(key) == 0 { - return res, fmt.Errorf("Unexpected end of key at index %d.", i) - } - - res[key] = value - key = "" - value = "" - state = parserStateBeforeKey - - default: - key += string(c) - } - - case parserStateBeginValue: - switch { - case c == '{': - state = parserStateBracedValue - case c == ';': - // Empty value - res[key] = value - key = "" - state = parserStateBeforeKey - case unicode.IsSpace(c): - // Ignore whitespace - default: - state = parserStateBareValue - value += string(c) - } - - case parserStateBareValue: - if c == ';' { - res[key] = strings.TrimRightFunc(value, unicode.IsSpace) - key = "" - value = "" - state = parserStateBeforeKey - } else { - value += string(c) - } - - case parserStateBracedValue: - if c == '}' { - state = parserStateBracedValueClosingBrace - } else { - value += string(c) - } - - case parserStateBracedValueClosingBrace: - if c == '}' { - // Escaped closing brace - value += string(c) - state = parserStateBracedValue - continue - } - - // End of braced value - res[key] = value - key = "" - value = "" - - // This character is the first character past the end, - // so it needs to be parsed like the parserStateEndValue state. - state = parserStateEndValue - switch { - case c == ';': - state = parserStateBeforeKey - case unicode.IsSpace(c): - // Ignore whitespace - default: - return res, fmt.Errorf("Unexpected character %c at index %d. Expected semi-colon or whitespace.", c, i) - } - - case parserStateEndValue: - switch { - case c == ';': - state = parserStateBeforeKey - case unicode.IsSpace(c): - // Ignore whitespace - default: - return res, fmt.Errorf("Unexpected character %c at index %d. Expected semi-colon or whitespace.", c, i) - } - } - } - - switch state { - case parserStateBeforeKey: // Okay - case parserStateKey: // Unfinished key. Treat as key without value. - key = normalizeOdbcKey(key) - if len(key) == 0 { - return res, fmt.Errorf("Unexpected end of key at index %d.", len(dsn)) - } - res[key] = value - case parserStateBeginValue: // Empty value - res[key] = value - case parserStateBareValue: - res[key] = strings.TrimRightFunc(value, unicode.IsSpace) - case parserStateBracedValue: - return res, fmt.Errorf("Unexpected end of braced value at index %d.", len(dsn)) - case parserStateBracedValueClosingBrace: // End of braced value - res[key] = value - case parserStateEndValue: // Okay - } - - return res, nil -} - -// Normalizes the given string as an ODBC-format key -func normalizeOdbcKey(s string) string { - return strings.ToLower(strings.TrimRightFunc(s, unicode.IsSpace)) -} - -// Splits a URL of the form sqlserver://username:password@host/instance?param1=value¶m2=value -func splitConnectionStringURL(dsn string) (map[string]string, error) { - res := map[string]string{} - - u, err := url.Parse(dsn) - if err != nil { - return res, err - } - - if u.Scheme != "sqlserver" { - return res, fmt.Errorf("scheme %s is not recognized", u.Scheme) - } - - if u.User != nil { - res["user id"] = u.User.Username() - p, exists := u.User.Password() - if exists { - res["password"] = p - } - } - - host, port, err := net.SplitHostPort(u.Host) - if err != nil { - host = u.Host - } - - if len(u.Path) > 0 { - res["server"] = host + "\\" + u.Path[1:] - } else { - res["server"] = host - } - - if len(port) > 0 { - res["port"] = port - } - - query := u.Query() - for k, v := range query { - if len(v) > 1 { - return res, fmt.Errorf("key %s provided more than once", k) - } - res[strings.ToLower(k)] = v[0] - } - - return res, nil -} - -func parseConnectParams(dsn string) (connectParams, error) { - var p connectParams - - var params map[string]string - if strings.HasPrefix(dsn, "odbc:") { - parameters, err := splitConnectionStringOdbc(dsn[len("odbc:"):]) - if err != nil { - return p, err - } - params = parameters - } else if strings.HasPrefix(dsn, "sqlserver://") { - parameters, err := splitConnectionStringURL(dsn) - if err != nil { - return p, err - } - params = parameters - } else { - params = splitConnectionString(dsn) - } - - strlog, ok := params["log"] - if ok { - var err error - p.logFlags, err = strconv.ParseUint(strlog, 10, 64) - if err != nil { - return p, fmt.Errorf("Invalid log parameter '%s': %s", strlog, err.Error()) - } - } - server := params["server"] - parts := strings.SplitN(server, `\`, 2) - p.host = parts[0] - if p.host == "." || strings.ToUpper(p.host) == "(LOCAL)" || p.host == "" { - p.host = "localhost" - } - if len(parts) > 1 { - p.instance = parts[1] - } - p.database = params["database"] - p.user = params["user id"] - p.password = params["password"] - - p.port = 1433 - strport, ok := params["port"] - if ok { - var err error - p.port, err = strconv.ParseUint(strport, 10, 16) - if err != nil { - f := "Invalid tcp port '%v': %v" - return p, fmt.Errorf(f, strport, err.Error()) - } - } - - // https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/configure-the-network-packet-size-server-configuration-option - // Default packet size remains at 4096 bytes - p.packetSize = 4096 - strpsize, ok := params["packet size"] - if ok { - var err error - psize, err := strconv.ParseUint(strpsize, 0, 16) - if err != nil { - f := "Invalid packet size '%v': %v" - return p, fmt.Errorf(f, strpsize, err.Error()) - } - - // Ensure packet size falls within the TDS protocol range of 512 to 32767 bytes - // NOTE: Encrypted connections have a maximum size of 16383 bytes. If you request - // a higher packet size, the server will respond with an ENVCHANGE request to - // alter the packet size to 16383 bytes. - p.packetSize = uint16(psize) - if p.packetSize < 512 { - p.packetSize = 512 - } else if p.packetSize > 32767 { - p.packetSize = 32767 - } - } - - // https://msdn.microsoft.com/en-us/library/dd341108.aspx - // - // Do not set a connection timeout. Use Context to manage such things. - // Default to zero, but still allow it to be set. - if strconntimeout, ok := params["connection timeout"]; ok { - timeout, err := strconv.ParseUint(strconntimeout, 10, 64) - if err != nil { - f := "Invalid connection timeout '%v': %v" - return p, fmt.Errorf(f, strconntimeout, err.Error()) - } - p.conn_timeout = time.Duration(timeout) * time.Second - } - p.dial_timeout = 15 * time.Second - if strdialtimeout, ok := params["dial timeout"]; ok { - timeout, err := strconv.ParseUint(strdialtimeout, 10, 64) - if err != nil { - f := "Invalid dial timeout '%v': %v" - return p, fmt.Errorf(f, strdialtimeout, err.Error()) - } - p.dial_timeout = time.Duration(timeout) * time.Second - } - - // default keep alive should be 30 seconds according to spec: - // https://msdn.microsoft.com/en-us/library/dd341108.aspx - p.keepAlive = 30 * time.Second - if keepAlive, ok := params["keepalive"]; ok { - timeout, err := strconv.ParseUint(keepAlive, 10, 64) - if err != nil { - f := "Invalid keepAlive value '%s': %s" - return p, fmt.Errorf(f, keepAlive, err.Error()) - } - p.keepAlive = time.Duration(timeout) * time.Second - } - encrypt, ok := params["encrypt"] - if ok { - if strings.EqualFold(encrypt, "DISABLE") { - p.disableEncryption = true - } else { - var err error - p.encrypt, err = strconv.ParseBool(encrypt) - if err != nil { - f := "Invalid encrypt '%s': %s" - return p, fmt.Errorf(f, encrypt, err.Error()) - } - } - } else { - p.trustServerCertificate = true - } - trust, ok := params["trustservercertificate"] - if ok { - var err error - p.trustServerCertificate, err = strconv.ParseBool(trust) - if err != nil { - f := "Invalid trust server certificate '%s': %s" - return p, fmt.Errorf(f, trust, err.Error()) - } - } - p.certificate = params["certificate"] - p.hostInCertificate, ok = params["hostnameincertificate"] - if !ok { - p.hostInCertificate = p.host - } - - serverSPN, ok := params["serverspn"] - if ok { - p.serverSPN = serverSPN - } else { - p.serverSPN = fmt.Sprintf("MSSQLSvc/%s:%d", p.host, p.port) - } - - workstation, ok := params["workstation id"] - if ok { - p.workstation = workstation - } else { - workstation, err := os.Hostname() - if err == nil { - p.workstation = workstation - } - } - - appname, ok := params["app name"] - if !ok { - appname = "go-mssqldb" - } - p.appname = appname - - appintent, ok := params["applicationintent"] - if ok { - if appintent == "ReadOnly" { - p.typeFlags |= fReadOnlyIntent - } - } - - failOverPartner, ok := params["failoverpartner"] - if ok { - p.failOverPartner = failOverPartner - } - - failOverPort, ok := params["failoverport"] - if ok { - var err error - p.failOverPort, err = strconv.ParseUint(failOverPort, 0, 16) - if err != nil { - f := "Invalid tcp port '%v': %v" - return p, fmt.Errorf(f, failOverPort, err.Error()) - } - } - - return p, nil -} - type auth interface { InitialBytes() ([]byte, error) NextBytes([]byte) ([]byte, error) @@ -1112,7 +768,7 @@ type auth interface { // SQL Server AlwaysOn Availability Group Listeners are bound by DNS to a // list of IP addresses. So if there is more than one, try them all and // use the first one that allows a connection. -func dialConnection(ctx context.Context, p connectParams) (conn net.Conn, err error) { +func dialConnection(ctx context.Context, c *Connector, p connectParams) (conn net.Conn, err error) { var ips []net.IP ips, err = net.LookupIP(p.host) if err != nil { @@ -1123,20 +779,20 @@ func dialConnection(ctx context.Context, p connectParams) (conn net.Conn, err er ips = []net.IP{ip} } if len(ips) == 1 { - d := createDialer(&p) - addr := net.JoinHostPort(ips[0].String(), strconv.Itoa(int(p.port))) - conn, err = d.Dial(ctx, addr) + d := c.getDialer(&p) + addr := net.JoinHostPort(ips[0].String(), strconv.Itoa(int(resolveServerPort(p.port)))) + conn, err = d.DialContext(ctx, "tcp", addr) } else { //Try Dials in parallel to avoid waiting for timeouts. connChan := make(chan net.Conn, len(ips)) errChan := make(chan error, len(ips)) - portStr := strconv.Itoa(int(p.port)) + portStr := strconv.Itoa(int(resolveServerPort(p.port))) for _, ip := range ips { go func(ip net.IP) { - d := createDialer(&p) + d := c.getDialer(&p) addr := net.JoinHostPort(ip.String(), portStr) - conn, err := d.Dial(ctx, addr) + conn, err := d.DialContext(ctx, "tcp", addr) if err == nil { connChan <- conn } else { @@ -1169,12 +825,12 @@ func dialConnection(ctx context.Context, p connectParams) (conn net.Conn, err er // Can't do the usual err != nil check, as it is possible to have gotten an error before a successful connection if conn == nil { f := "Unable to open tcp connection with host '%v:%v': %v" - return nil, fmt.Errorf(f, p.host, p.port, err.Error()) + return nil, fmt.Errorf(f, p.host, resolveServerPort(p.port), err.Error()) } return conn, err } -func connect(ctx context.Context, log optionalLogger, p connectParams) (res *tdsSession, err error) { +func connect(ctx context.Context, c *Connector, log optionalLogger, p connectParams) (res *tdsSession, err error) { dialCtx := ctx if p.dial_timeout > 0 { var cancel func() @@ -1182,9 +838,10 @@ func connect(ctx context.Context, log optionalLogger, p connectParams) (res *tds defer cancel() } // if instance is specified use instance resolution service - if p.instance != "" { + if p.instance != "" && p.port == 0 { p.instance = strings.ToUpper(p.instance) - instances, err := getInstances(dialCtx, p.host) + d := c.getDialer(&p) + instances, err := getInstances(dialCtx, d, p.host) if err != nil { f := "Unable to get instances from Sql Server Browser on host %v: %v" return nil, fmt.Errorf(f, p.host, err.Error()) @@ -1194,15 +851,16 @@ func connect(ctx context.Context, log optionalLogger, p connectParams) (res *tds f := "No instance matching '%v' returned from host '%v'" return nil, fmt.Errorf(f, p.instance, p.host) } - p.port, err = strconv.ParseUint(strport, 0, 16) + port, err := strconv.ParseUint(strport, 0, 16) if err != nil { f := "Invalid tcp port returned from Sql Server Browser '%v': %v" return nil, fmt.Errorf(f, strport, err.Error()) } + p.port = port } initiate_connection: - conn, err := dialConnection(dialCtx, p) + conn, err := dialConnection(dialCtx, c, p) if err != nil { return nil, err } @@ -1273,12 +931,12 @@ initiate_connection: // while SQL Server seems to expect one TCP segment per encrypted TDS package. // Setting DynamicRecordSizingDisabled to true disables that algorithm and uses 16384 bytes per TLS package config.DynamicRecordSizingDisabled = true - outbuf.transport = conn - toconn.buf = outbuf - tlsConn := tls.Client(toconn, &config) + // setting up connection handler which will allow wrapping of TLS handshake packets inside TDS stream + handshakeConn := tlsHandshakeConn{buf: outbuf} + passthrough := passthroughConn{c: &handshakeConn} + tlsConn := tls.Client(&passthrough, &config) err = tlsConn.Handshake() - - toconn.buf = nil + passthrough.c = toconn outbuf.transport = tlsConn if err != nil { return nil, fmt.Errorf("TLS Handshake failed: %v", err) @@ -1300,15 +958,23 @@ initiate_connection: AppName: p.appname, TypeFlags: p.typeFlags, } - auth, auth_ok := getAuth(p.user, p.password, p.serverSPN, p.workstation) - if auth_ok { + auth, authOk := getAuth(p.user, p.password, p.serverSPN, p.workstation) + switch { + case p.fedAuthAccessToken != "": // accesstoken ignores user/password + featurext := &featureExtFedAuthSTS{ + FedAuthEcho: len(fields[preloginFEDAUTHREQUIRED]) > 0 && fields[preloginFEDAUTHREQUIRED][0] == 1, + FedAuthToken: p.fedAuthAccessToken, + Nonce: fields[preloginNONCEOPT], + } + login.FeatureExt.Add(featurext) + case authOk: login.SSPI, err = auth.InitialBytes() if err != nil { return nil, err } login.OptionFlags2 |= fIntSecurity defer auth.Free() - } else { + default: login.UserName = p.user login.Password = p.password } @@ -1318,42 +984,43 @@ initiate_connection: } // processing login response - var sspi_msg []byte -continue_login: - tokchan := make(chan tokenStruct, 5) - go processResponse(context.Background(), &sess, tokchan, nil) success := false - for tok := range tokchan { - switch token := tok.(type) { - case sspiMsg: - sspi_msg, err = auth.NextBytes(token) - if err != nil { - return nil, err - } - case loginAckStruct: - success = true - sess.loginAck = token - case error: - return nil, fmt.Errorf("Login error: %s", token.Error()) - case doneStruct: - if token.isError() { - return nil, fmt.Errorf("Login error: %s", token.getError()) + for { + tokchan := make(chan tokenStruct, 5) + go processResponse(context.Background(), &sess, tokchan, nil) + for tok := range tokchan { + switch token := tok.(type) { + case sspiMsg: + sspi_msg, err := auth.NextBytes(token) + if err != nil { + return nil, err + } + if sspi_msg != nil && len(sspi_msg) > 0 { + outbuf.BeginPacket(packSSPIMessage, false) + _, err = outbuf.Write(sspi_msg) + if err != nil { + return nil, err + } + err = outbuf.FinishPacket() + if err != nil { + return nil, err + } + sspi_msg = nil + } + case loginAckStruct: + success = true + sess.loginAck = token + case error: + return nil, fmt.Errorf("Login error: %s", token.Error()) + case doneStruct: + if token.isError() { + return nil, fmt.Errorf("Login error: %s", token.getError()) + } + goto loginEnd } } } - if sspi_msg != nil { - outbuf.BeginPacket(packSSPIMessage, false) - _, err = outbuf.Write(sspi_msg) - if err != nil { - return nil, err - } - err = outbuf.FinishPacket() - if err != nil { - return nil, err - } - sspi_msg = nil - goto continue_login - } +loginEnd: if !success { return nil, fmt.Errorf("Login failed") } @@ -1361,6 +1028,9 @@ continue_login: toconn.Close() p.host = sess.routedServer p.port = uint64(sess.routedPort) + if !p.hostInCertificateProvided { + p.hostInCertificate = sess.routedServer + } goto initiate_connection } return &sess, nil diff --git a/vendor/github.com/denisenkom/go-mssqldb/token.go b/vendor/github.com/denisenkom/go-mssqldb/token.go index 1acac8a5d2b..25385e89dcb 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/token.go +++ b/vendor/github.com/denisenkom/go-mssqldb/token.go @@ -17,20 +17,21 @@ type token byte // token ids const ( - tokenReturnStatus token = 121 // 0x79 - tokenColMetadata token = 129 // 0x81 - tokenOrder token = 169 // 0xA9 - tokenError token = 170 // 0xAA - tokenInfo token = 171 // 0xAB - tokenReturnValue token = 0xAC - tokenLoginAck token = 173 // 0xad - tokenRow token = 209 // 0xd1 - tokenNbcRow token = 210 // 0xd2 - tokenEnvChange token = 227 // 0xE3 - tokenSSPI token = 237 // 0xED - tokenDone token = 253 // 0xFD - tokenDoneProc token = 254 - tokenDoneInProc token = 255 + tokenReturnStatus token = 121 // 0x79 + tokenColMetadata token = 129 // 0x81 + tokenOrder token = 169 // 0xA9 + tokenError token = 170 // 0xAA + tokenInfo token = 171 // 0xAB + tokenReturnValue token = 0xAC + tokenLoginAck token = 173 // 0xad + tokenFeatureExtAck token = 174 // 0xae + tokenRow token = 209 // 0xd1 + tokenNbcRow token = 210 // 0xd2 + tokenEnvChange token = 227 // 0xE3 + tokenSSPI token = 237 // 0xED + tokenDone token = 253 // 0xFD + tokenDoneProc token = 254 + tokenDoneInProc token = 255 ) // done flags @@ -447,6 +448,22 @@ func parseLoginAck(r *tdsBuffer) loginAckStruct { return res } +// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/2eb82f8e-11f0-46dc-b42d-27302fa4701a +func parseFeatureExtAck(r *tdsBuffer) { + // at most 1 featureAck per feature in featureExt + // go-mssqldb will add at most 1 feature, the spec defines 7 different features + for i := 0; i < 8; i++ { + featureID := r.byte() // FeatureID + if featureID == 0xff { + return + } + size := r.uint32() // FeatureAckDataLen + d := make([]byte, size) + r.ReadFull(d) + } + panic("parsed more than 7 featureAck's, protocol implementation error?") +} + // http://msdn.microsoft.com/en-us/library/dd357363.aspx func parseColMetadata72(r *tdsBuffer) (columns []columnStruct) { count := r.uint16() @@ -577,6 +594,8 @@ func processSingleResponse(sess *tdsSession, ch chan tokenStruct, outs map[strin case tokenLoginAck: loginAck := parseLoginAck(sess.buf) ch <- loginAck + case tokenFeatureExtAck: + parseFeatureExtAck(sess.buf) case tokenOrder: order := parseOrder(sess.buf) ch <- order diff --git a/vendor/github.com/denisenkom/go-mssqldb/tvp_go19.go b/vendor/github.com/denisenkom/go-mssqldb/tvp_go19.go new file mode 100644 index 00000000000..64e5e21fbd8 --- /dev/null +++ b/vendor/github.com/denisenkom/go-mssqldb/tvp_go19.go @@ -0,0 +1,231 @@ +// +build go1.9 + +package mssql + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "reflect" + "strings" + "time" +) + +const ( + jsonTag = "json" + tvpTag = "tvp" + skipTagValue = "-" + sqlSeparator = "." +) + +var ( + ErrorEmptyTVPTypeName = errors.New("TypeName must not be empty") + ErrorTypeSlice = errors.New("TVP must be slice type") + ErrorTypeSliceIsEmpty = errors.New("TVP mustn't be null value") + ErrorSkip = errors.New("all fields mustn't skip") + ErrorObjectName = errors.New("wrong tvp name") + ErrorWrongTyping = errors.New("the number of elements in columnStr and tvpFieldIndexes do not align") +) + +//TVP is driver type, which allows supporting Table Valued Parameters (TVP) in SQL Server +type TVP struct { + //TypeName mustn't be default value + TypeName string + //Value must be the slice, mustn't be nil + Value interface{} +} + +func (tvp TVP) check() error { + if len(tvp.TypeName) == 0 { + return ErrorEmptyTVPTypeName + } + if !isProc(tvp.TypeName) { + return ErrorEmptyTVPTypeName + } + if sepCount := getCountSQLSeparators(tvp.TypeName); sepCount > 1 { + return ErrorObjectName + } + valueOf := reflect.ValueOf(tvp.Value) + if valueOf.Kind() != reflect.Slice { + return ErrorTypeSlice + } + if valueOf.IsNil() { + return ErrorTypeSliceIsEmpty + } + if reflect.TypeOf(tvp.Value).Elem().Kind() != reflect.Struct { + return ErrorTypeSlice + } + return nil +} + +func (tvp TVP) encode(schema, name string, columnStr []columnStruct, tvpFieldIndexes []int) ([]byte, error) { + if len(columnStr) != len(tvpFieldIndexes) { + return nil, ErrorWrongTyping + } + preparedBuffer := make([]byte, 0, 20+(10*len(columnStr))) + buf := bytes.NewBuffer(preparedBuffer) + err := writeBVarChar(buf, "") + if err != nil { + return nil, err + } + + writeBVarChar(buf, schema) + writeBVarChar(buf, name) + binary.Write(buf, binary.LittleEndian, uint16(len(columnStr))) + + for i, column := range columnStr { + binary.Write(buf, binary.LittleEndian, uint32(column.UserType)) + binary.Write(buf, binary.LittleEndian, uint16(column.Flags)) + writeTypeInfo(buf, &columnStr[i].ti) + writeBVarChar(buf, "") + } + // The returned error is always nil + buf.WriteByte(_TVP_END_TOKEN) + + conn := new(Conn) + conn.sess = new(tdsSession) + conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73} + stmt := &Stmt{ + c: conn, + } + + val := reflect.ValueOf(tvp.Value) + for i := 0; i < val.Len(); i++ { + refStr := reflect.ValueOf(val.Index(i).Interface()) + buf.WriteByte(_TVP_ROW_TOKEN) + for columnStrIdx, fieldIdx := range tvpFieldIndexes { + field := refStr.Field(fieldIdx) + tvpVal := field.Interface() + valOf := reflect.ValueOf(tvpVal) + elemKind := field.Kind() + if elemKind == reflect.Ptr && valOf.IsNil() { + switch tvpVal.(type) { + case *bool, *time.Time, *int8, *int16, *int32, *int64, *float32, *float64, *int: + binary.Write(buf, binary.LittleEndian, uint8(0)) + continue + default: + binary.Write(buf, binary.LittleEndian, uint64(_PLP_NULL)) + continue + } + } + if elemKind == reflect.Slice && valOf.IsNil() { + binary.Write(buf, binary.LittleEndian, uint64(_PLP_NULL)) + continue + } + + cval, err := convertInputParameter(tvpVal) + if err != nil { + return nil, fmt.Errorf("failed to convert tvp parameter row col: %s", err) + } + param, err := stmt.makeParam(cval) + if err != nil { + return nil, fmt.Errorf("failed to make tvp parameter row col: %s", err) + } + columnStr[columnStrIdx].ti.Writer(buf, param.ti, param.buffer) + } + } + buf.WriteByte(_TVP_END_TOKEN) + return buf.Bytes(), nil +} + +func (tvp TVP) columnTypes() ([]columnStruct, []int, error) { + val := reflect.ValueOf(tvp.Value) + var firstRow interface{} + if val.Len() != 0 { + firstRow = val.Index(0).Interface() + } else { + firstRow = reflect.New(reflect.TypeOf(tvp.Value).Elem()).Elem().Interface() + } + + tvpRow := reflect.TypeOf(firstRow) + columnCount := tvpRow.NumField() + defaultValues := make([]interface{}, 0, columnCount) + tvpFieldIndexes := make([]int, 0, columnCount) + for i := 0; i < columnCount; i++ { + field := tvpRow.Field(i) + tvpTagValue, isTvpTag := field.Tag.Lookup(tvpTag) + jsonTagValue, isJsonTag := field.Tag.Lookup(jsonTag) + if IsSkipField(tvpTagValue, isTvpTag, jsonTagValue, isJsonTag) { + continue + } + tvpFieldIndexes = append(tvpFieldIndexes, i) + if field.Type.Kind() == reflect.Ptr { + v := reflect.New(field.Type.Elem()) + defaultValues = append(defaultValues, v.Interface()) + continue + } + defaultValues = append(defaultValues, reflect.Zero(field.Type).Interface()) + } + + if columnCount-len(tvpFieldIndexes) == columnCount { + return nil, nil, ErrorSkip + } + + conn := new(Conn) + conn.sess = new(tdsSession) + conn.sess.loginAck = loginAckStruct{TDSVersion: verTDS73} + stmt := &Stmt{ + c: conn, + } + + columnConfiguration := make([]columnStruct, 0, columnCount) + for index, val := range defaultValues { + cval, err := convertInputParameter(val) + if err != nil { + return nil, nil, fmt.Errorf("failed to convert tvp parameter row %d col %d: %s", index, val, err) + } + param, err := stmt.makeParam(cval) + if err != nil { + return nil, nil, err + } + column := columnStruct{ + ti: param.ti, + } + switch param.ti.TypeId { + case typeNVarChar, typeBigVarBin: + column.ti.Size = 0 + } + columnConfiguration = append(columnConfiguration, column) + } + + return columnConfiguration, tvpFieldIndexes, nil +} + +func IsSkipField(tvpTagValue string, isTvpValue bool, jsonTagValue string, isJsonTagValue bool) bool { + if !isTvpValue && !isJsonTagValue { + return false + } else if isTvpValue && tvpTagValue != skipTagValue { + return false + } else if !isTvpValue && isJsonTagValue && jsonTagValue != skipTagValue { + return false + } + return true +} + +func getSchemeAndName(tvpName string) (string, string, error) { + if len(tvpName) == 0 { + return "", "", ErrorEmptyTVPTypeName + } + splitVal := strings.Split(tvpName, ".") + if len(splitVal) > 2 { + return "", "", errors.New("wrong tvp name") + } + if len(splitVal) == 2 { + res := make([]string, 2) + for key, value := range splitVal { + tmp := strings.Replace(value, "[", "", -1) + tmp = strings.Replace(tmp, "]", "", -1) + res[key] = tmp + } + return res[0], res[1], nil + } + tmp := strings.Replace(splitVal[0], "[", "", -1) + tmp = strings.Replace(tmp, "]", "", -1) + + return "", tmp, nil +} + +func getCountSQLSeparators(str string) int { + return strings.Count(str, sqlSeparator) +} diff --git a/vendor/github.com/denisenkom/go-mssqldb/types.go b/vendor/github.com/denisenkom/go-mssqldb/types.go index 3bad788b923..b6e7fb2b526 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/types.go +++ b/vendor/github.com/denisenkom/go-mssqldb/types.go @@ -11,6 +11,7 @@ import ( "time" "github.com/denisenkom/go-mssqldb/internal/cp" + "github.com/denisenkom/go-mssqldb/internal/decimal" ) // fixed-length data types @@ -62,6 +63,7 @@ const ( typeNChar = 0xef typeXml = 0xf1 typeUdt = 0xf0 + typeTvp = 0xf3 // long length types typeText = 0x23 @@ -73,6 +75,10 @@ const _PLP_NULL = 0xFFFFFFFFFFFFFFFF const _UNKNOWN_PLP_LEN = 0xFFFFFFFFFFFFFFFE const _PLP_TERMINATOR = 0x00000000 +// TVP COLUMN FLAGS +const _TVP_END_TOKEN = 0x00 +const _TVP_ROW_TOKEN = 0x01 + // TYPE_INFO rule // http://msdn.microsoft.com/en-us/library/dd358284.aspx type typeInfo struct { @@ -145,6 +151,8 @@ func writeTypeInfo(w io.Writer, ti *typeInfo) (err error) { // those are fixed length // https://msdn.microsoft.com/en-us/library/dd341171.aspx ti.Writer = writeFixedType + case typeTvp: + ti.Writer = writeFixedType default: // all others are VARLENTYPE err = writeVarLen(w, ti) if err != nil { @@ -162,6 +170,7 @@ func writeFixedType(w io.Writer, ti typeInfo, buf []byte) (err error) { // https://msdn.microsoft.com/en-us/library/dd358341.aspx func writeVarLen(w io.Writer, ti *typeInfo) (err error) { switch ti.TypeId { + case typeDateN: ti.Writer = writeByteLenType case typeTimeN, typeDateTime2N, typeDateTimeOffsetN: @@ -203,6 +212,7 @@ func writeVarLen(w io.Writer, ti *typeInfo) (err error) { ti.Writer = writeByteLenType case typeBigVarBin, typeBigVarChar, typeBigBinary, typeBigChar, typeNVarChar, typeNChar, typeXml, typeUdt: + // short len types if ti.Size > 8000 || ti.Size == 0 { if err = binary.Write(w, binary.LittleEndian, uint16(0xffff)); err != nil { @@ -809,12 +819,12 @@ func decodeMoney(buf []byte) []byte { uint64(buf[1])<<40 | uint64(buf[2])<<48 | uint64(buf[3])<<56) - return scaleBytes(strconv.FormatInt(money, 10), 4) + return decimal.ScaleBytes(strconv.FormatInt(money, 10), 4) } func decodeMoney4(buf []byte) []byte { money := int32(binary.LittleEndian.Uint32(buf[0:4])) - return scaleBytes(strconv.FormatInt(int64(money), 10), 4) + return decimal.ScaleBytes(strconv.FormatInt(int64(money), 10), 4) } func decodeGuid(buf []byte) []byte { @@ -826,15 +836,14 @@ func decodeGuid(buf []byte) []byte { func decodeDecimal(prec uint8, scale uint8, buf []byte) []byte { var sign uint8 sign = buf[0] - dec := Decimal{ - positive: sign != 0, - prec: prec, - scale: scale, - } + var dec decimal.Decimal + dec.SetPositive(sign != 0) + dec.SetPrec(prec) + dec.SetScale(scale) buf = buf[1:] l := len(buf) / 4 for i := 0; i < l; i++ { - dec.integer[i] = binary.LittleEndian.Uint32(buf[0:4]) + dec.SetInteger(binary.LittleEndian.Uint32(buf[0:4]), uint8(i)) buf = buf[4:] } return dec.Bytes() @@ -1177,7 +1186,7 @@ func makeDecl(ti typeInfo) string { case typeBigChar, typeChar: return fmt.Sprintf("char(%d)", ti.Size) case typeBigVarChar, typeVarChar: - if ti.Size > 4000 || ti.Size == 0 { + if ti.Size > 8000 || ti.Size == 0 { return fmt.Sprintf("varchar(max)") } else { return fmt.Sprintf("varchar(%d)", ti.Size) @@ -1219,6 +1228,11 @@ func makeDecl(ti typeInfo) string { return ti.UdtInfo.TypeName case typeGuid: return "uniqueidentifier" + case typeTvp: + if ti.UdtInfo.SchemaName != "" { + return fmt.Sprintf("%s.%s READONLY", ti.UdtInfo.SchemaName, ti.UdtInfo.TypeName) + } + return fmt.Sprintf("%s READONLY", ti.UdtInfo.TypeName) default: panic(fmt.Sprintf("not implemented makeDecl for type %#x", ti.TypeId)) } diff --git a/vendor/github.com/denisenkom/go-mssqldb/uniqueidentifier.go b/vendor/github.com/denisenkom/go-mssqldb/uniqueidentifier.go index c8ef3149b19..3ad6f8634a2 100644 --- a/vendor/github.com/denisenkom/go-mssqldb/uniqueidentifier.go +++ b/vendor/github.com/denisenkom/go-mssqldb/uniqueidentifier.go @@ -72,3 +72,9 @@ func (u UniqueIdentifier) Value() (driver.Value, error) { func (u UniqueIdentifier) String() string { return fmt.Sprintf("%X-%X-%X-%X-%X", u[0:4], u[4:6], u[6:8], u[8:10], u[10:]) } + +// MarshalText converts Uniqueidentifier to bytes corresponding to the stringified hexadecimal representation of the Uniqueidentifier +// e.g., "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA" -> [65 65 65 65 65 65 65 65 45 65 65 65 65 45 65 65 65 65 45 65 65 65 65 65 65 65 65 65 65 65 65] +func (u UniqueIdentifier) MarshalText() []byte { + return []byte(u.String()) +} diff --git a/vendor/github.com/docker/spdystream/CONTRIBUTING.md b/vendor/github.com/docker/spdystream/CONTRIBUTING.md new file mode 100644 index 00000000000..d4eddcc5396 --- /dev/null +++ b/vendor/github.com/docker/spdystream/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing to SpdyStream + +Want to hack on spdystream? Awesome! Here are instructions to get you +started. + +SpdyStream is a part of the [Docker](https://docker.io) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read +[Docker's contributions guidelines](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md). + +Happy hacking! diff --git a/vendor/github.com/docker/spdystream/LICENSE b/vendor/github.com/docker/spdystream/LICENSE new file mode 100644 index 00000000000..9e4bd4dbee9 --- /dev/null +++ b/vendor/github.com/docker/spdystream/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014-2015 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/docker/spdystream/LICENSE.docs b/vendor/github.com/docker/spdystream/LICENSE.docs new file mode 100644 index 00000000000..e26cd4fc8ed --- /dev/null +++ b/vendor/github.com/docker/spdystream/LICENSE.docs @@ -0,0 +1,425 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public licenses. +Notwithstanding, Creative Commons may elect to apply one of its public +licenses to material it publishes and in those instances will be +considered the "Licensor." Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the public +licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/vendor/github.com/docker/spdystream/MAINTAINERS b/vendor/github.com/docker/spdystream/MAINTAINERS new file mode 100644 index 00000000000..14e263325c7 --- /dev/null +++ b/vendor/github.com/docker/spdystream/MAINTAINERS @@ -0,0 +1,28 @@ +# Spdystream maintainers file +# +# This file describes who runs the docker/spdystream project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "dmcgowan", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + + [people.dmcgowan] + Name = "Derek McGowan" + Email = "derek@docker.com" + GitHub = "dmcgowan" diff --git a/vendor/github.com/docker/spdystream/README.md b/vendor/github.com/docker/spdystream/README.md new file mode 100644 index 00000000000..11cccd0a09e --- /dev/null +++ b/vendor/github.com/docker/spdystream/README.md @@ -0,0 +1,77 @@ +# SpdyStream + +A multiplexed stream library using spdy + +## Usage + +Client example (connecting to mirroring server without auth) + +```go +package main + +import ( + "fmt" + "github.com/docker/spdystream" + "net" + "net/http" +) + +func main() { + conn, err := net.Dial("tcp", "localhost:8080") + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, false) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.NoOpStreamHandler) + stream, err := spdyConn.CreateStream(http.Header{}, nil, false) + if err != nil { + panic(err) + } + + stream.Wait() + + fmt.Fprint(stream, "Writing to stream") + + buf := make([]byte, 25) + stream.Read(buf) + fmt.Println(string(buf)) + + stream.Close() +} +``` + +Server example (mirroring server without auth) + +```go +package main + +import ( + "github.com/docker/spdystream" + "net" +) + +func main() { + listener, err := net.Listen("tcp", "localhost:8080") + if err != nil { + panic(err) + } + for { + conn, err := listener.Accept() + if err != nil { + panic(err) + } + spdyConn, err := spdystream.NewConnection(conn, true) + if err != nil { + panic(err) + } + go spdyConn.Serve(spdystream.MirrorStreamHandler) + } +} +``` + +## Copyright and license + +Copyright © 2014-2015 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. diff --git a/vendor/github.com/docker/spdystream/connection.go b/vendor/github.com/docker/spdystream/connection.go new file mode 100644 index 00000000000..6031a0db1ab --- /dev/null +++ b/vendor/github.com/docker/spdystream/connection.go @@ -0,0 +1,958 @@ +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/docker/spdystream/spdy" +) + +var ( + ErrInvalidStreamId = errors.New("Invalid stream id") + ErrTimeout = errors.New("Timeout occured") + ErrReset = errors.New("Stream reset") + ErrWriteClosedStream = errors.New("Write on closed stream") +) + +const ( + FRAME_WORKERS = 5 + QUEUE_SIZE = 50 +) + +type StreamHandler func(stream *Stream) + +type AuthHandler func(header http.Header, slot uint8, parent uint32) bool + +type idleAwareFramer struct { + f *spdy.Framer + conn *Connection + writeLock sync.Mutex + resetChan chan struct{} + setTimeoutLock sync.Mutex + setTimeoutChan chan time.Duration + timeout time.Duration +} + +func newIdleAwareFramer(framer *spdy.Framer) *idleAwareFramer { + iaf := &idleAwareFramer{ + f: framer, + resetChan: make(chan struct{}, 2), + // setTimeoutChan needs to be buffered to avoid deadlocks when calling setIdleTimeout at about + // the same time the connection is being closed + setTimeoutChan: make(chan time.Duration, 1), + } + return iaf +} + +func (i *idleAwareFramer) monitor() { + var ( + timer *time.Timer + expired <-chan time.Time + resetChan = i.resetChan + setTimeoutChan = i.setTimeoutChan + ) +Loop: + for { + select { + case timeout := <-i.setTimeoutChan: + i.timeout = timeout + if timeout == 0 { + if timer != nil { + timer.Stop() + } + } else { + if timer == nil { + timer = time.NewTimer(timeout) + expired = timer.C + } else { + timer.Reset(timeout) + } + } + case <-resetChan: + if timer != nil && i.timeout > 0 { + timer.Reset(i.timeout) + } + case <-expired: + i.conn.streamCond.L.Lock() + streams := i.conn.streams + i.conn.streams = make(map[spdy.StreamId]*Stream) + i.conn.streamCond.Broadcast() + i.conn.streamCond.L.Unlock() + go func() { + for _, stream := range streams { + stream.resetStream() + } + i.conn.Close() + }() + case <-i.conn.closeChan: + if timer != nil { + timer.Stop() + } + + // Start a goroutine to drain resetChan. This is needed because we've seen + // some unit tests with large numbers of goroutines get into a situation + // where resetChan fills up, at least 1 call to Write() is still trying to + // send to resetChan, the connection gets closed, and this case statement + // attempts to grab the write lock that Write() already has, causing a + // deadlock. + // + // See https://github.com/docker/spdystream/issues/49 for more details. + go func() { + for _ = range resetChan { + } + }() + + go func() { + for _ = range setTimeoutChan { + } + }() + + i.writeLock.Lock() + close(resetChan) + i.resetChan = nil + i.writeLock.Unlock() + + i.setTimeoutLock.Lock() + close(i.setTimeoutChan) + i.setTimeoutChan = nil + i.setTimeoutLock.Unlock() + + break Loop + } + } + + // Drain resetChan + for _ = range resetChan { + } +} + +func (i *idleAwareFramer) WriteFrame(frame spdy.Frame) error { + i.writeLock.Lock() + defer i.writeLock.Unlock() + if i.resetChan == nil { + return io.EOF + } + err := i.f.WriteFrame(frame) + if err != nil { + return err + } + + i.resetChan <- struct{}{} + + return nil +} + +func (i *idleAwareFramer) ReadFrame() (spdy.Frame, error) { + frame, err := i.f.ReadFrame() + if err != nil { + return nil, err + } + + // resetChan should never be closed since it is only closed + // when the connection has closed its closeChan. This closure + // only occurs after all Reads have finished + // TODO (dmcgowan): refactor relationship into connection + i.resetChan <- struct{}{} + + return frame, nil +} + +func (i *idleAwareFramer) setIdleTimeout(timeout time.Duration) { + i.setTimeoutLock.Lock() + defer i.setTimeoutLock.Unlock() + + if i.setTimeoutChan == nil { + return + } + + i.setTimeoutChan <- timeout +} + +type Connection struct { + conn net.Conn + framer *idleAwareFramer + + closeChan chan bool + goneAway bool + lastStreamChan chan<- *Stream + goAwayTimeout time.Duration + closeTimeout time.Duration + + streamLock *sync.RWMutex + streamCond *sync.Cond + streams map[spdy.StreamId]*Stream + + nextIdLock sync.Mutex + receiveIdLock sync.Mutex + nextStreamId spdy.StreamId + receivedStreamId spdy.StreamId + + pingIdLock sync.Mutex + pingId uint32 + pingChans map[uint32]chan error + + shutdownLock sync.Mutex + shutdownChan chan error + hasShutdown bool + + // for testing https://github.com/docker/spdystream/pull/56 + dataFrameHandler func(*spdy.DataFrame) error +} + +// NewConnection creates a new spdy connection from an existing +// network connection. +func NewConnection(conn net.Conn, server bool) (*Connection, error) { + framer, framerErr := spdy.NewFramer(conn, conn) + if framerErr != nil { + return nil, framerErr + } + idleAwareFramer := newIdleAwareFramer(framer) + var sid spdy.StreamId + var rid spdy.StreamId + var pid uint32 + if server { + sid = 2 + rid = 1 + pid = 2 + } else { + sid = 1 + rid = 2 + pid = 1 + } + + streamLock := new(sync.RWMutex) + streamCond := sync.NewCond(streamLock) + + session := &Connection{ + conn: conn, + framer: idleAwareFramer, + + closeChan: make(chan bool), + goAwayTimeout: time.Duration(0), + closeTimeout: time.Duration(0), + + streamLock: streamLock, + streamCond: streamCond, + streams: make(map[spdy.StreamId]*Stream), + nextStreamId: sid, + receivedStreamId: rid, + + pingId: pid, + pingChans: make(map[uint32]chan error), + + shutdownChan: make(chan error), + } + session.dataFrameHandler = session.handleDataFrame + idleAwareFramer.conn = session + go idleAwareFramer.monitor() + + return session, nil +} + +// Ping sends a ping frame across the connection and +// returns the response time +func (s *Connection) Ping() (time.Duration, error) { + pid := s.pingId + s.pingIdLock.Lock() + if s.pingId > 0x7ffffffe { + s.pingId = s.pingId - 0x7ffffffe + } else { + s.pingId = s.pingId + 2 + } + s.pingIdLock.Unlock() + pingChan := make(chan error) + s.pingChans[pid] = pingChan + defer delete(s.pingChans, pid) + + frame := &spdy.PingFrame{Id: pid} + startTime := time.Now() + writeErr := s.framer.WriteFrame(frame) + if writeErr != nil { + return time.Duration(0), writeErr + } + select { + case <-s.closeChan: + return time.Duration(0), errors.New("connection closed") + case err, ok := <-pingChan: + if ok && err != nil { + return time.Duration(0), err + } + break + } + return time.Now().Sub(startTime), nil +} + +// Serve handles frames sent from the server, including reply frames +// which are needed to fully initiate connections. Both clients and servers +// should call Serve in a separate goroutine before creating streams. +func (s *Connection) Serve(newHandler StreamHandler) { + // use a WaitGroup to wait for all frames to be drained after receiving + // go-away. + var wg sync.WaitGroup + + // Parition queues to ensure stream frames are handled + // by the same worker, ensuring order is maintained + frameQueues := make([]*PriorityFrameQueue, FRAME_WORKERS) + for i := 0; i < FRAME_WORKERS; i++ { + frameQueues[i] = NewPriorityFrameQueue(QUEUE_SIZE) + + // Ensure frame queue is drained when connection is closed + go func(frameQueue *PriorityFrameQueue) { + <-s.closeChan + frameQueue.Drain() + }(frameQueues[i]) + + wg.Add(1) + go func(frameQueue *PriorityFrameQueue) { + // let the WaitGroup know this worker is done + defer wg.Done() + + s.frameHandler(frameQueue, newHandler) + }(frameQueues[i]) + } + + var ( + partitionRoundRobin int + goAwayFrame *spdy.GoAwayFrame + ) +Loop: + for { + readFrame, err := s.framer.ReadFrame() + if err != nil { + if err != io.EOF { + fmt.Errorf("frame read error: %s", err) + } else { + debugMessage("(%p) EOF received", s) + } + break + } + var priority uint8 + var partition int + switch frame := readFrame.(type) { + case *spdy.SynStreamFrame: + if s.checkStreamFrame(frame) { + priority = frame.Priority + partition = int(frame.StreamId % FRAME_WORKERS) + debugMessage("(%p) Add stream frame: %d ", s, frame.StreamId) + s.addStreamFrame(frame) + } else { + debugMessage("(%p) Rejected stream frame: %d ", s, frame.StreamId) + continue + } + case *spdy.SynReplyFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.DataFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.RstStreamFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.HeadersFrame: + priority = s.getStreamPriority(frame.StreamId) + partition = int(frame.StreamId % FRAME_WORKERS) + case *spdy.PingFrame: + priority = 0 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + case *spdy.GoAwayFrame: + // hold on to the go away frame and exit the loop + goAwayFrame = frame + break Loop + default: + priority = 7 + partition = partitionRoundRobin + partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS + } + frameQueues[partition].Push(readFrame, priority) + } + close(s.closeChan) + + // wait for all frame handler workers to indicate they've drained their queues + // before handling the go away frame + wg.Wait() + + if goAwayFrame != nil { + s.handleGoAwayFrame(goAwayFrame) + } + + // now it's safe to close remote channels and empty s.streams + s.streamCond.L.Lock() + // notify streams that they're now closed, which will + // unblock any stream Read() calls + for _, stream := range s.streams { + stream.closeRemoteChannels() + } + s.streams = make(map[spdy.StreamId]*Stream) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) frameHandler(frameQueue *PriorityFrameQueue, newHandler StreamHandler) { + for { + popFrame := frameQueue.Pop() + if popFrame == nil { + return + } + + var frameErr error + switch frame := popFrame.(type) { + case *spdy.SynStreamFrame: + frameErr = s.handleStreamFrame(frame, newHandler) + case *spdy.SynReplyFrame: + frameErr = s.handleReplyFrame(frame) + case *spdy.DataFrame: + frameErr = s.dataFrameHandler(frame) + case *spdy.RstStreamFrame: + frameErr = s.handleResetFrame(frame) + case *spdy.HeadersFrame: + frameErr = s.handleHeaderFrame(frame) + case *spdy.PingFrame: + frameErr = s.handlePingFrame(frame) + case *spdy.GoAwayFrame: + frameErr = s.handleGoAwayFrame(frame) + default: + frameErr = fmt.Errorf("unhandled frame type: %T", frame) + } + + if frameErr != nil { + fmt.Errorf("frame handling error: %s", frameErr) + } + } +} + +func (s *Connection) getStreamPriority(streamId spdy.StreamId) uint8 { + stream, streamOk := s.getStream(streamId) + if !streamOk { + return 7 + } + return stream.priority +} + +func (s *Connection) addStreamFrame(frame *spdy.SynStreamFrame) { + var parent *Stream + if frame.AssociatedToStreamId != spdy.StreamId(0) { + parent, _ = s.getStream(frame.AssociatedToStreamId) + } + + stream := &Stream{ + streamId: frame.StreamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: frame.Headers, + finished: (frame.CFHeader.Flags & spdy.ControlFlagUnidirectional) != 0x00, + replyCond: sync.NewCond(new(sync.Mutex)), + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + } + if frame.CFHeader.Flags&spdy.ControlFlagFin != 0x00 { + stream.closeRemoteChannels() + } + + s.addStream(stream) +} + +// checkStreamFrame checks to see if a stream frame is allowed. +// If the stream is invalid, then a reset frame with protocol error +// will be returned. +func (s *Connection) checkStreamFrame(frame *spdy.SynStreamFrame) bool { + s.receiveIdLock.Lock() + defer s.receiveIdLock.Unlock() + if s.goneAway { + return false + } + validationErr := s.validateStreamId(frame.StreamId) + if validationErr != nil { + go func() { + resetErr := s.sendResetFrame(spdy.ProtocolError, frame.StreamId) + if resetErr != nil { + fmt.Errorf("reset error: %s", resetErr) + } + }() + return false + } + return true +} + +func (s *Connection) handleStreamFrame(frame *spdy.SynStreamFrame, newHandler StreamHandler) error { + stream, ok := s.getStream(frame.StreamId) + if !ok { + return fmt.Errorf("Missing stream: %d", frame.StreamId) + } + + newHandler(stream) + + return nil +} + +func (s *Connection) handleReplyFrame(frame *spdy.SynReplyFrame) error { + debugMessage("(%p) Reply frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("Reply frame gone away for %d", frame.StreamId) + // Stream has already gone away + return nil + } + if stream.replied { + // Stream has already received reply + return nil + } + stream.replied = true + + // TODO Check for error + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + close(stream.startChan) + + return nil +} + +func (s *Connection) handleResetFrame(frame *spdy.RstStreamFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already been removed + return nil + } + s.removeStream(stream) + stream.closeRemoteChannels() + + if !stream.replied { + stream.replied = true + stream.startChan <- ErrReset + close(stream.startChan) + } + + stream.finishLock.Lock() + stream.finished = true + stream.finishLock.Unlock() + + return nil +} + +func (s *Connection) handleHeaderFrame(frame *spdy.HeadersFrame) error { + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + // Stream has already gone away + return nil + } + if !stream.replied { + // No reply received...Protocol error? + return nil + } + + // TODO limit headers while not blocking (use buffered chan or goroutine?) + select { + case <-stream.closeChan: + return nil + case stream.headerChan <- frame.Headers: + } + + if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + + return nil +} + +func (s *Connection) handleDataFrame(frame *spdy.DataFrame) error { + debugMessage("(%p) Data frame received for %d", s, frame.StreamId) + stream, streamOk := s.getStream(frame.StreamId) + if !streamOk { + debugMessage("(%p) Data frame gone away for %d", s, frame.StreamId) + // Stream has already gone away + return nil + } + if !stream.replied { + debugMessage("(%p) Data frame not replied %d", s, frame.StreamId) + // No reply received...Protocol error? + return nil + } + + debugMessage("(%p) (%d) Data frame handling", stream, stream.streamId) + if len(frame.Data) > 0 { + stream.dataLock.RLock() + select { + case <-stream.closeChan: + debugMessage("(%p) (%d) Data frame not sent (stream shut down)", stream, stream.streamId) + case stream.dataChan <- frame.Data: + debugMessage("(%p) (%d) Data frame sent", stream, stream.streamId) + } + stream.dataLock.RUnlock() + } + if (frame.Flags & spdy.DataFlagFin) != 0x00 { + s.remoteStreamFinish(stream) + } + return nil +} + +func (s *Connection) handlePingFrame(frame *spdy.PingFrame) error { + if s.pingId&0x01 != frame.Id&0x01 { + return s.framer.WriteFrame(frame) + } + pingChan, pingOk := s.pingChans[frame.Id] + if pingOk { + close(pingChan) + } + return nil +} + +func (s *Connection) handleGoAwayFrame(frame *spdy.GoAwayFrame) error { + debugMessage("(%p) Go away received", s) + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + if s.lastStreamChan != nil { + stream, _ := s.getStream(frame.LastGoodStreamId) + go func() { + s.lastStreamChan <- stream + }() + } + + // Do not block frame handler waiting for closure + go s.shutdown(s.goAwayTimeout) + + return nil +} + +func (s *Connection) remoteStreamFinish(stream *Stream) { + stream.closeRemoteChannels() + + stream.finishLock.Lock() + if stream.finished { + // Stream is fully closed, cleanup + s.removeStream(stream) + } + stream.finishLock.Unlock() +} + +// CreateStream creates a new spdy stream using the parameters for +// creating the stream frame. The stream frame will be sent upon +// calling this function, however this function does not wait for +// the reply frame. If waiting for the reply is desired, use +// the stream Wait or WaitTimeout function on the stream returned +// by this function. +func (s *Connection) CreateStream(headers http.Header, parent *Stream, fin bool) (*Stream, error) { + // MUST synchronize stream creation (all the way to writing the frame) + // as stream IDs **MUST** increase monotonically. + s.nextIdLock.Lock() + defer s.nextIdLock.Unlock() + + streamId := s.getNextStreamId() + if streamId == 0 { + return nil, fmt.Errorf("Unable to get new stream id") + } + + stream := &Stream{ + streamId: streamId, + parent: parent, + conn: s, + startChan: make(chan error), + headers: headers, + dataChan: make(chan []byte), + headerChan: make(chan http.Header), + closeChan: make(chan bool), + } + + debugMessage("(%p) (%p) Create stream", s, stream) + + s.addStream(stream) + + return stream, s.sendStream(stream, fin) +} + +func (s *Connection) shutdown(closeTimeout time.Duration) { + // TODO Ensure this isn't called multiple times + s.shutdownLock.Lock() + if s.hasShutdown { + s.shutdownLock.Unlock() + return + } + s.hasShutdown = true + s.shutdownLock.Unlock() + + var timeout <-chan time.Time + if closeTimeout > time.Duration(0) { + timeout = time.After(closeTimeout) + } + streamsClosed := make(chan bool) + + go func() { + s.streamCond.L.Lock() + for len(s.streams) > 0 { + debugMessage("Streams opened: %d, %#v", len(s.streams), s.streams) + s.streamCond.Wait() + } + s.streamCond.L.Unlock() + close(streamsClosed) + }() + + var err error + select { + case <-streamsClosed: + // No active streams, close should be safe + err = s.conn.Close() + case <-timeout: + // Force ungraceful close + err = s.conn.Close() + // Wait for cleanup to clear active streams + <-streamsClosed + } + + if err != nil { + duration := 10 * time.Minute + time.AfterFunc(duration, func() { + select { + case err, ok := <-s.shutdownChan: + if ok { + fmt.Errorf("Unhandled close error after %s: %s", duration, err) + } + default: + } + }) + s.shutdownChan <- err + } + close(s.shutdownChan) + + return +} + +// Closes spdy connection by sending GoAway frame and initiating shutdown +func (s *Connection) Close() error { + s.receiveIdLock.Lock() + if s.goneAway { + s.receiveIdLock.Unlock() + return nil + } + s.goneAway = true + s.receiveIdLock.Unlock() + + var lastStreamId spdy.StreamId + if s.receivedStreamId > 2 { + lastStreamId = s.receivedStreamId - 2 + } + + goAwayFrame := &spdy.GoAwayFrame{ + LastGoodStreamId: lastStreamId, + Status: spdy.GoAwayOK, + } + + err := s.framer.WriteFrame(goAwayFrame) + if err != nil { + return err + } + + go s.shutdown(s.closeTimeout) + + return nil +} + +// CloseWait closes the connection and waits for shutdown +// to finish. Note the underlying network Connection +// is not closed until the end of shutdown. +func (s *Connection) CloseWait() error { + closeErr := s.Close() + if closeErr != nil { + return closeErr + } + shutdownErr, ok := <-s.shutdownChan + if ok { + return shutdownErr + } + return nil +} + +// Wait waits for the connection to finish shutdown or for +// the wait timeout duration to expire. This needs to be +// called either after Close has been called or the GOAWAYFRAME +// has been received. If the wait timeout is 0, this function +// will block until shutdown finishes. If wait is never called +// and a shutdown error occurs, that error will be logged as an +// unhandled error. +func (s *Connection) Wait(waitTimeout time.Duration) error { + var timeout <-chan time.Time + if waitTimeout > time.Duration(0) { + timeout = time.After(waitTimeout) + } + + select { + case err, ok := <-s.shutdownChan: + if ok { + return err + } + case <-timeout: + return ErrTimeout + } + return nil +} + +// NotifyClose registers a channel to be called when the remote +// peer inidicates connection closure. The last stream to be +// received by the remote will be sent on the channel. The notify +// timeout will determine the duration between go away received +// and the connection being closed. +func (s *Connection) NotifyClose(c chan<- *Stream, timeout time.Duration) { + s.goAwayTimeout = timeout + s.lastStreamChan = c +} + +// SetCloseTimeout sets the amount of time close will wait for +// streams to finish before terminating the underlying network +// connection. Setting the timeout to 0 will cause close to +// wait forever, which is the default. +func (s *Connection) SetCloseTimeout(timeout time.Duration) { + s.closeTimeout = timeout +} + +// SetIdleTimeout sets the amount of time the connection may sit idle before +// it is forcefully terminated. +func (s *Connection) SetIdleTimeout(timeout time.Duration) { + s.framer.setIdleTimeout(timeout) +} + +func (s *Connection) sendHeaders(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + headerFrame := &spdy.HeadersFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(headerFrame) +} + +func (s *Connection) sendReply(headers http.Header, stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + } + + replyFrame := &spdy.SynReplyFrame{ + StreamId: stream.streamId, + Headers: headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(replyFrame) +} + +func (s *Connection) sendResetFrame(status spdy.RstStreamStatus, streamId spdy.StreamId) error { + resetFrame := &spdy.RstStreamFrame{ + StreamId: streamId, + Status: status, + } + + return s.framer.WriteFrame(resetFrame) +} + +func (s *Connection) sendReset(status spdy.RstStreamStatus, stream *Stream) error { + return s.sendResetFrame(status, stream.streamId) +} + +func (s *Connection) sendStream(stream *Stream, fin bool) error { + var flags spdy.ControlFlags + if fin { + flags = spdy.ControlFlagFin + stream.finished = true + } + + var parentId spdy.StreamId + if stream.parent != nil { + parentId = stream.parent.streamId + } + + streamFrame := &spdy.SynStreamFrame{ + StreamId: spdy.StreamId(stream.streamId), + AssociatedToStreamId: spdy.StreamId(parentId), + Headers: stream.headers, + CFHeader: spdy.ControlFrameHeader{Flags: flags}, + } + + return s.framer.WriteFrame(streamFrame) +} + +// getNextStreamId returns the next sequential id +// every call should produce a unique value or an error +func (s *Connection) getNextStreamId() spdy.StreamId { + sid := s.nextStreamId + if sid > 0x7fffffff { + return 0 + } + s.nextStreamId = s.nextStreamId + 2 + return sid +} + +// PeekNextStreamId returns the next sequential id and keeps the next id untouched +func (s *Connection) PeekNextStreamId() spdy.StreamId { + sid := s.nextStreamId + return sid +} + +func (s *Connection) validateStreamId(rid spdy.StreamId) error { + if rid > 0x7fffffff || rid < s.receivedStreamId { + return ErrInvalidStreamId + } + s.receivedStreamId = rid + 2 + return nil +} + +func (s *Connection) addStream(stream *Stream) { + s.streamCond.L.Lock() + s.streams[stream.streamId] = stream + debugMessage("(%p) (%p) Stream added, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) removeStream(stream *Stream) { + s.streamCond.L.Lock() + delete(s.streams, stream.streamId) + debugMessage("(%p) (%p) Stream removed, broadcasting: %d", s, stream, stream.streamId) + s.streamCond.Broadcast() + s.streamCond.L.Unlock() +} + +func (s *Connection) getStream(streamId spdy.StreamId) (stream *Stream, ok bool) { + s.streamLock.RLock() + stream, ok = s.streams[streamId] + s.streamLock.RUnlock() + return +} + +// FindStream looks up the given stream id and either waits for the +// stream to be found or returns nil if the stream id is no longer +// valid. +func (s *Connection) FindStream(streamId uint32) *Stream { + var stream *Stream + var ok bool + s.streamCond.L.Lock() + stream, ok = s.streams[spdy.StreamId(streamId)] + debugMessage("(%p) Found stream %d? %t", s, spdy.StreamId(streamId), ok) + for !ok && streamId >= uint32(s.receivedStreamId) { + s.streamCond.Wait() + stream, ok = s.streams[spdy.StreamId(streamId)] + } + s.streamCond.L.Unlock() + return stream +} + +func (s *Connection) CloseChan() <-chan bool { + return s.closeChan +} diff --git a/vendor/github.com/docker/spdystream/handlers.go b/vendor/github.com/docker/spdystream/handlers.go new file mode 100644 index 00000000000..b59fa5fdcd0 --- /dev/null +++ b/vendor/github.com/docker/spdystream/handlers.go @@ -0,0 +1,38 @@ +package spdystream + +import ( + "io" + "net/http" +) + +// MirrorStreamHandler mirrors all streams. +func MirrorStreamHandler(stream *Stream) { + replyErr := stream.SendReply(http.Header{}, false) + if replyErr != nil { + return + } + + go func() { + io.Copy(stream, stream) + stream.Close() + }() + go func() { + for { + header, receiveErr := stream.ReceiveHeader() + if receiveErr != nil { + return + } + sendErr := stream.SendHeader(header, false) + if sendErr != nil { + return + } + } + }() +} + +// NoopStreamHandler does nothing when stream connects, most +// likely used with RejectAuthHandler which will not allow any +// streams to make it to the stream handler. +func NoOpStreamHandler(stream *Stream) { + stream.SendReply(http.Header{}, false) +} diff --git a/vendor/github.com/docker/spdystream/priority.go b/vendor/github.com/docker/spdystream/priority.go new file mode 100644 index 00000000000..fc8582b5c6f --- /dev/null +++ b/vendor/github.com/docker/spdystream/priority.go @@ -0,0 +1,98 @@ +package spdystream + +import ( + "container/heap" + "sync" + + "github.com/docker/spdystream/spdy" +) + +type prioritizedFrame struct { + frame spdy.Frame + priority uint8 + insertId uint64 +} + +type frameQueue []*prioritizedFrame + +func (fq frameQueue) Len() int { + return len(fq) +} + +func (fq frameQueue) Less(i, j int) bool { + if fq[i].priority == fq[j].priority { + return fq[i].insertId < fq[j].insertId + } + return fq[i].priority < fq[j].priority +} + +func (fq frameQueue) Swap(i, j int) { + fq[i], fq[j] = fq[j], fq[i] +} + +func (fq *frameQueue) Push(x interface{}) { + *fq = append(*fq, x.(*prioritizedFrame)) +} + +func (fq *frameQueue) Pop() interface{} { + old := *fq + n := len(old) + *fq = old[0 : n-1] + return old[n-1] +} + +type PriorityFrameQueue struct { + queue *frameQueue + c *sync.Cond + size int + nextInsertId uint64 + drain bool +} + +func NewPriorityFrameQueue(size int) *PriorityFrameQueue { + queue := make(frameQueue, 0, size) + heap.Init(&queue) + + return &PriorityFrameQueue{ + queue: &queue, + size: size, + c: sync.NewCond(&sync.Mutex{}), + } +} + +func (q *PriorityFrameQueue) Push(frame spdy.Frame, priority uint8) { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() >= q.size { + q.c.Wait() + } + pFrame := &prioritizedFrame{ + frame: frame, + priority: priority, + insertId: q.nextInsertId, + } + q.nextInsertId = q.nextInsertId + 1 + heap.Push(q.queue, pFrame) + q.c.Signal() +} + +func (q *PriorityFrameQueue) Pop() spdy.Frame { + q.c.L.Lock() + defer q.c.L.Unlock() + for q.queue.Len() == 0 { + if q.drain { + return nil + } + q.c.Wait() + } + frame := heap.Pop(q.queue).(*prioritizedFrame).frame + q.c.Signal() + return frame +} + +func (q *PriorityFrameQueue) Drain() { + q.c.L.Lock() + defer q.c.L.Unlock() + q.drain = true + q.c.Broadcast() +} diff --git a/vendor/github.com/docker/spdystream/spdy/dictionary.go b/vendor/github.com/docker/spdystream/spdy/dictionary.go new file mode 100644 index 00000000000..5a5ff0e14cd --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/dictionary.go @@ -0,0 +1,187 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +// headerDictionary is the dictionary sent to the zlib compressor/decompressor. +var headerDictionary = []byte{ + 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, + 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, + 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, + 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, + 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, + 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, + 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, + 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, + 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, + 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, + 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, + 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, + 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, + 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, + 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, + 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, + 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, + 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, + 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, + 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, + 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, + 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, + 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, + 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, + 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, + 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, + 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, + 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, + 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, + 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, + 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, + 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, + 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, + 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, + 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, + 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, + 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, + 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, + 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, + 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, + 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, + 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, + 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, + 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, + 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, + 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, + 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, + 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, + 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, + 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, + 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, + 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, + 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, + 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, + 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, + 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, + 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, + 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, + 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, + 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, + 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, + 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, + 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, + 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, + 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, + 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, + 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, + 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, + 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, + 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, + 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, + 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, + 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, + 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, + 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, + 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, + 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, + 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, + 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, + 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, + 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, + 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, + 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, + 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, + 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, + 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, + 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e, +} diff --git a/vendor/github.com/docker/spdystream/spdy/read.go b/vendor/github.com/docker/spdystream/spdy/read.go new file mode 100644 index 00000000000..9359a95015c --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/read.go @@ -0,0 +1,348 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "compress/zlib" + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynStreamFrame(h, frame) +} + +func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readSynReplyFrame(h, frame) +} + +func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + var numSettings uint32 + if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { + return err + } + frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) + for i := uint32(0); i < numSettings; i++ { + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { + return err + } + frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) + frame.FlagIdValues[i].Id &= 0xffffff + if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { + return err + } + } + return nil +} + +func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { + return err + } + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, StreamId(frame.Id)} + } + return nil +} + +func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.LastGoodStreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { + return err + } + return nil +} + +func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { + return f.readHeadersFrame(h, frame) +} + +func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error { + frame.CFHeader = h + if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if frame.CFHeader.Flags != 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if frame.CFHeader.length != 8 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil { + return err + } + return nil +} + +func newControlFrame(frameType ControlFrameType) (controlFrame, error) { + ctor, ok := cframeCtor[frameType] + if !ok { + return nil, &Error{Err: InvalidControlFrame} + } + return ctor(), nil +} + +var cframeCtor = map[ControlFrameType]func() controlFrame{ + TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, + TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, + TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, + TypeSettings: func() controlFrame { return new(SettingsFrame) }, + TypePing: func() controlFrame { return new(PingFrame) }, + TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, + TypeHeaders: func() controlFrame { return new(HeadersFrame) }, + TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) }, +} + +func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { + if f.headerDecompressor != nil { + f.headerReader.N = payloadSize + return nil + } + f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} + decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary)) + if err != nil { + return err + } + f.headerDecompressor = decompressor + return nil +} + +// ReadFrame reads SPDY encoded data and returns a decompressed Frame. +func (f *Framer) ReadFrame() (Frame, error) { + var firstWord uint32 + if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { + return nil, err + } + if firstWord&0x80000000 != 0 { + frameType := ControlFrameType(firstWord & 0xffff) + version := uint16(firstWord >> 16 & 0x7fff) + return f.parseControlFrame(version, frameType) + } + return f.parseDataFrame(StreamId(firstWord & 0x7fffffff)) +} + +func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + flags := ControlFlags((length & 0xff000000) >> 24) + length &= 0xffffff + header := ControlFrameHeader{version, frameType, flags, length} + cframe, err := newControlFrame(frameType) + if err != nil { + return nil, err + } + if err = cframe.read(header, f); err != nil { + return nil, err + } + return cframe, nil +} + +func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { + var numHeaders uint32 + if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { + return nil, err + } + var e error + h := make(http.Header, int(numHeaders)) + for i := 0; i < int(numHeaders); i++ { + var length uint32 + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + nameBytes := make([]byte, length) + if _, err := io.ReadFull(r, nameBytes); err != nil { + return nil, err + } + name := string(nameBytes) + if name != strings.ToLower(name) { + e = &Error{UnlowercasedHeaderName, streamId} + name = strings.ToLower(name) + } + if h[name] != nil { + e = &Error{DuplicateHeaders, streamId} + } + if err := binary.Read(r, binary.BigEndian, &length); err != nil { + return nil, err + } + value := make([]byte, length) + if _, err := io.ReadFull(r, value); err != nil { + return nil, err + } + valueList := strings.Split(string(value), headerValueSeparator) + for _, v := range valueList { + h.Add(name, v) + } + } + if e != nil { + return h, e + } + return h, nil +} + +func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { + return err + } + frame.Priority >>= 5 + if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 10)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidReqHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + for h := range frame.Headers { + if invalidRespHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { + frame.CFHeader = h + var err error + if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { + return err + } + reader := f.r + if !f.headerCompressionDisabled { + err := f.uncorkHeaderDecompressor(int64(h.length - 4)) + if err != nil { + return err + } + reader = f.headerDecompressor + } + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } + if err != nil { + return err + } + var invalidHeaders map[string]bool + if frame.StreamId%2 == 0 { + invalidHeaders = invalidReqHeaders + } else { + invalidHeaders = invalidRespHeaders + } + for h := range frame.Headers { + if invalidHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + return nil +} + +func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) { + var length uint32 + if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { + return nil, err + } + var frame DataFrame + frame.StreamId = streamId + frame.Flags = DataFlags(length >> 24) + length &= 0xffffff + frame.Data = make([]byte, length) + if _, err := io.ReadFull(f.r, frame.Data); err != nil { + return nil, err + } + if frame.StreamId == 0 { + return nil, &Error{ZeroStreamId, 0} + } + return &frame, nil +} diff --git a/vendor/github.com/docker/spdystream/spdy/types.go b/vendor/github.com/docker/spdystream/spdy/types.go new file mode 100644 index 00000000000..7b6ee9c6f2b --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/types.go @@ -0,0 +1,275 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package spdy implements the SPDY protocol (currently SPDY/3), described in +// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. +package spdy + +import ( + "bytes" + "compress/zlib" + "io" + "net/http" +) + +// Version is the protocol version number that this package implements. +const Version = 3 + +// ControlFrameType stores the type field in a control frame header. +type ControlFrameType uint16 + +const ( + TypeSynStream ControlFrameType = 0x0001 + TypeSynReply = 0x0002 + TypeRstStream = 0x0003 + TypeSettings = 0x0004 + TypePing = 0x0006 + TypeGoAway = 0x0007 + TypeHeaders = 0x0008 + TypeWindowUpdate = 0x0009 +) + +// ControlFlags are the flags that can be set on a control frame. +type ControlFlags uint8 + +const ( + ControlFlagFin ControlFlags = 0x01 + ControlFlagUnidirectional = 0x02 + ControlFlagSettingsClearSettings = 0x01 +) + +// DataFlags are the flags that can be set on a data frame. +type DataFlags uint8 + +const ( + DataFlagFin DataFlags = 0x01 +) + +// MaxDataLength is the maximum number of bytes that can be stored in one frame. +const MaxDataLength = 1<<24 - 1 + +// headerValueSepator separates multiple header values. +const headerValueSeparator = "\x00" + +// Frame is a single SPDY frame in its unpacked in-memory representation. Use +// Framer to read and write it. +type Frame interface { + write(f *Framer) error +} + +// ControlFrameHeader contains all the fields in a control frame header, +// in its unpacked in-memory representation. +type ControlFrameHeader struct { + // Note, high bit is the "Control" bit. + version uint16 // spdy version number + frameType ControlFrameType + Flags ControlFlags + length uint32 // length of data field +} + +type controlFrame interface { + Frame + read(h ControlFrameHeader, f *Framer) error +} + +// StreamId represents a 31-bit value identifying the stream. +type StreamId uint32 + +// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM +// frame. +type SynStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to + Priority uint8 // priority of this frame (3-bit) + Slot uint8 // index in the server's credential vector of the client certificate + Headers http.Header +} + +// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. +type SynReplyFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// RstStreamStatus represents the status that led to a RST_STREAM. +type RstStreamStatus uint32 + +const ( + ProtocolError RstStreamStatus = iota + 1 + InvalidStream + RefusedStream + UnsupportedVersion + Cancel + InternalError + FlowControlError + StreamInUse + StreamAlreadyClosed + InvalidCredentials + FrameTooLarge +) + +// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM +// frame. +type RstStreamFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Status RstStreamStatus +} + +// SettingsFlag represents a flag in a SETTINGS frame. +type SettingsFlag uint8 + +const ( + FlagSettingsPersistValue SettingsFlag = 0x1 + FlagSettingsPersisted = 0x2 +) + +// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. +type SettingsId uint32 + +const ( + SettingsUploadBandwidth SettingsId = iota + 1 + SettingsDownloadBandwidth + SettingsRoundTripTime + SettingsMaxConcurrentStreams + SettingsCurrentCwnd + SettingsDownloadRetransRate + SettingsInitialWindowSize + SettingsClientCretificateVectorSize +) + +// SettingsFlagIdValue is the unpacked, in-memory representation of the +// combined flag/id/value for a setting in a SETTINGS frame. +type SettingsFlagIdValue struct { + Flag SettingsFlag + Id SettingsId + Value uint32 +} + +// SettingsFrame is the unpacked, in-memory representation of a SPDY +// SETTINGS frame. +type SettingsFrame struct { + CFHeader ControlFrameHeader + FlagIdValues []SettingsFlagIdValue +} + +// PingFrame is the unpacked, in-memory representation of a PING frame. +type PingFrame struct { + CFHeader ControlFrameHeader + Id uint32 // unique id for this ping, from server is even, from client is odd. +} + +// GoAwayStatus represents the status in a GoAwayFrame. +type GoAwayStatus uint32 + +const ( + GoAwayOK GoAwayStatus = iota + GoAwayProtocolError + GoAwayInternalError +) + +// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. +type GoAwayFrame struct { + CFHeader ControlFrameHeader + LastGoodStreamId StreamId // last stream id which was accepted by sender + Status GoAwayStatus +} + +// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. +type HeadersFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + Headers http.Header +} + +// WindowUpdateFrame is the unpacked, in-memory representation of a +// WINDOW_UPDATE frame. +type WindowUpdateFrame struct { + CFHeader ControlFrameHeader + StreamId StreamId + DeltaWindowSize uint32 // additional number of bytes to existing window size +} + +// TODO: Implement credential frame and related methods. + +// DataFrame is the unpacked, in-memory representation of a DATA frame. +type DataFrame struct { + // Note, high bit is the "Control" bit. Should be 0 for data frames. + StreamId StreamId + Flags DataFlags + Data []byte // payload data of this frame +} + +// A SPDY specific error. +type ErrorCode string + +const ( + UnlowercasedHeaderName ErrorCode = "header was not lowercased" + DuplicateHeaders = "multiple headers with same name" + WrongCompressedPayloadSize = "compressed payload size was incorrect" + UnknownFrameType = "unknown frame type" + InvalidControlFrame = "invalid control frame" + InvalidDataFrame = "invalid data frame" + InvalidHeaderPresent = "frame contained invalid header" + ZeroStreamId = "stream id zero is disallowed" +) + +// Error contains both the type of error and additional values. StreamId is 0 +// if Error is not associated with a stream. +type Error struct { + Err ErrorCode + StreamId StreamId +} + +func (e *Error) Error() string { + return string(e.Err) +} + +var invalidReqHeaders = map[string]bool{ + "Connection": true, + "Host": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +var invalidRespHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +// Framer handles serializing/deserializing SPDY frames, including compressing/ +// decompressing payloads. +type Framer struct { + headerCompressionDisabled bool + w io.Writer + headerBuf *bytes.Buffer + headerCompressor *zlib.Writer + r io.Reader + headerReader io.LimitedReader + headerDecompressor io.ReadCloser +} + +// NewFramer allocates a new Framer for a given SPDY connection, represented by +// a io.Writer and io.Reader. Note that Framer will read and write individual fields +// from/to the Reader and Writer, so the caller should pass in an appropriately +// buffered implementation to optimize performance. +func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { + compressBuf := new(bytes.Buffer) + compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) + if err != nil { + return nil, err + } + framer := &Framer{ + w: w, + headerBuf: compressBuf, + headerCompressor: compressor, + r: r, + } + return framer, nil +} diff --git a/vendor/github.com/docker/spdystream/spdy/write.go b/vendor/github.com/docker/spdystream/spdy/write.go new file mode 100644 index 00000000000..b212f66a235 --- /dev/null +++ b/vendor/github.com/docker/spdystream/spdy/write.go @@ -0,0 +1,318 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package spdy + +import ( + "encoding/binary" + "io" + "net/http" + "strings" +) + +func (frame *SynStreamFrame) write(f *Framer) error { + return f.writeSynStreamFrame(frame) +} + +func (frame *SynReplyFrame) write(f *Framer) error { + return f.writeSynReplyFrame(frame) +} + +func (frame *RstStreamFrame) write(f *Framer) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeRstStream + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if frame.Status == 0 { + return &Error{InvalidControlFrame, frame.StreamId} + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return +} + +func (frame *SettingsFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSettings + frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { + return + } + for _, flagIdValue := range frame.FlagIdValues { + flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id) + if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { + return + } + } + return +} + +func (frame *PingFrame) write(f *Framer) (err error) { + if frame.Id == 0 { + return &Error{ZeroStreamId, 0} + } + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypePing + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 4 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { + return + } + return +} + +func (frame *GoAwayFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeGoAway + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { + return + } + return nil +} + +func (frame *HeadersFrame) write(f *Framer) error { + return f.writeHeadersFrame(frame) +} + +func (frame *WindowUpdateFrame) write(f *Framer) (err error) { + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeWindowUpdate + frame.CFHeader.Flags = 0 + frame.CFHeader.length = 8 + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil { + return + } + return nil +} + +func (frame *DataFrame) write(f *Framer) error { + return f.writeDataFrame(frame) +} + +// WriteFrame writes a frame. +func (f *Framer) WriteFrame(frame Frame) error { + return frame.write(f) +} + +func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { + if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { + return err + } + flagsAndLength := uint32(h.Flags)<<24 | h.length + if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { + return err + } + return nil +} + +func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { + n = 0 + if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { + return + } + n += 2 + for name, values := range h { + if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { + return + } + n += 2 + name = strings.ToLower(name) + if _, err = io.WriteString(w, name); err != nil { + return + } + n += len(name) + v := strings.Join(values, headerValueSeparator) + if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { + return + } + n += 2 + if _, err = io.WriteString(w, v); err != nil { + return + } + n += len(v) + } + return +} + +func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynStream + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil { + return err + } + if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil { + return err + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return err + } + f.headerBuf.Reset() + return nil +} + +func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeSynReply + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + // Marshal the headers. + var writer io.Writer = f.headerBuf + if !f.headerCompressionDisabled { + writer = f.headerCompressor + } + if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { + return + } + if !f.headerCompressionDisabled { + f.headerCompressor.Flush() + } + + // Set ControlFrameHeader. + frame.CFHeader.version = Version + frame.CFHeader.frameType = TypeHeaders + frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) + + // Serialize frame to Writer. + if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { + return + } + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { + return + } + f.headerBuf.Reset() + return +} + +func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { + if frame.StreamId == 0 { + return &Error{ZeroStreamId, 0} + } + if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength { + return &Error{InvalidDataFrame, frame.StreamId} + } + + // Serialize frame to Writer. + if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { + return + } + flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) + if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { + return + } + if _, err = f.w.Write(frame.Data); err != nil { + return + } + return nil +} diff --git a/vendor/github.com/docker/spdystream/stream.go b/vendor/github.com/docker/spdystream/stream.go new file mode 100644 index 00000000000..f9e9ee267f8 --- /dev/null +++ b/vendor/github.com/docker/spdystream/stream.go @@ -0,0 +1,327 @@ +package spdystream + +import ( + "errors" + "fmt" + "io" + "net" + "net/http" + "sync" + "time" + + "github.com/docker/spdystream/spdy" +) + +var ( + ErrUnreadPartialData = errors.New("unread partial data") +) + +type Stream struct { + streamId spdy.StreamId + parent *Stream + conn *Connection + startChan chan error + + dataLock sync.RWMutex + dataChan chan []byte + unread []byte + + priority uint8 + headers http.Header + headerChan chan http.Header + finishLock sync.Mutex + finished bool + replyCond *sync.Cond + replied bool + closeLock sync.Mutex + closeChan chan bool +} + +// WriteData writes data to stream, sending a dataframe per call +func (s *Stream) WriteData(data []byte, fin bool) error { + s.waitWriteReply() + var flags spdy.DataFlags + + if fin { + flags = spdy.DataFlagFin + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return ErrWriteClosedStream + } + s.finished = true + s.finishLock.Unlock() + } + + dataFrame := &spdy.DataFrame{ + StreamId: s.streamId, + Flags: flags, + Data: data, + } + + debugMessage("(%p) (%d) Writing data frame", s, s.streamId) + return s.conn.framer.WriteFrame(dataFrame) +} + +// Write writes bytes to a stream, calling write data for each call. +func (s *Stream) Write(data []byte) (n int, err error) { + err = s.WriteData(data, false) + if err == nil { + n = len(data) + } + return +} + +// Read reads bytes from a stream, a single read will never get more +// than what is sent on a single data frame, but a multiple calls to +// read may get data from the same data frame. +func (s *Stream) Read(p []byte) (n int, err error) { + if s.unread == nil { + select { + case <-s.closeChan: + return 0, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return 0, io.EOF + } + s.unread = read + } + } + n = copy(p, s.unread) + if n < len(s.unread) { + s.unread = s.unread[n:] + } else { + s.unread = nil + } + return +} + +// ReadData reads an entire data frame and returns the byte array +// from the data frame. If there is unread data from the result +// of a Read call, this function will return an ErrUnreadPartialData. +func (s *Stream) ReadData() ([]byte, error) { + debugMessage("(%p) Reading data from %d", s, s.streamId) + if s.unread != nil { + return nil, ErrUnreadPartialData + } + select { + case <-s.closeChan: + return nil, io.EOF + case read, ok := <-s.dataChan: + if !ok { + return nil, io.EOF + } + return read, nil + } +} + +func (s *Stream) waitWriteReply() { + if s.replyCond != nil { + s.replyCond.L.Lock() + for !s.replied { + s.replyCond.Wait() + } + s.replyCond.L.Unlock() + } +} + +// Wait waits for the stream to receive a reply. +func (s *Stream) Wait() error { + return s.WaitTimeout(time.Duration(0)) +} + +// WaitTimeout waits for the stream to receive a reply or for timeout. +// When the timeout is reached, ErrTimeout will be returned. +func (s *Stream) WaitTimeout(timeout time.Duration) error { + var timeoutChan <-chan time.Time + if timeout > time.Duration(0) { + timeoutChan = time.After(timeout) + } + + select { + case err := <-s.startChan: + if err != nil { + return err + } + break + case <-timeoutChan: + return ErrTimeout + } + return nil +} + +// Close closes the stream by sending an empty data frame with the +// finish flag set, indicating this side is finished with the stream. +func (s *Stream) Close() error { + select { + case <-s.closeChan: + // Stream is now fully closed + s.conn.removeStream(s) + default: + break + } + return s.WriteData([]byte{}, true) +} + +// Reset sends a reset frame, putting the stream into the fully closed state. +func (s *Stream) Reset() error { + s.conn.removeStream(s) + return s.resetStream() +} + +func (s *Stream) resetStream() error { + // Always call closeRemoteChannels, even if s.finished is already true. + // This makes it so that stream.Close() followed by stream.Reset() allows + // stream.Read() to unblock. + s.closeRemoteChannels() + + s.finishLock.Lock() + if s.finished { + s.finishLock.Unlock() + return nil + } + s.finished = true + s.finishLock.Unlock() + + resetFrame := &spdy.RstStreamFrame{ + StreamId: s.streamId, + Status: spdy.Cancel, + } + return s.conn.framer.WriteFrame(resetFrame) +} + +// CreateSubStream creates a stream using the current as the parent +func (s *Stream) CreateSubStream(headers http.Header, fin bool) (*Stream, error) { + return s.conn.CreateStream(headers, s, fin) +} + +// SetPriority sets the stream priority, does not affect the +// remote priority of this stream after Open has been called. +// Valid values are 0 through 7, 0 being the highest priority +// and 7 the lowest. +func (s *Stream) SetPriority(priority uint8) { + s.priority = priority +} + +// SendHeader sends a header frame across the stream +func (s *Stream) SendHeader(headers http.Header, fin bool) error { + return s.conn.sendHeaders(headers, s, fin) +} + +// SendReply sends a reply on a stream, only valid to be called once +// when handling a new stream +func (s *Stream) SendReply(headers http.Header, fin bool) error { + if s.replyCond == nil { + return errors.New("cannot reply on initiated stream") + } + s.replyCond.L.Lock() + defer s.replyCond.L.Unlock() + if s.replied { + return nil + } + + err := s.conn.sendReply(headers, s, fin) + if err != nil { + return err + } + + s.replied = true + s.replyCond.Broadcast() + return nil +} + +// Refuse sends a reset frame with the status refuse, only +// valid to be called once when handling a new stream. This +// may be used to indicate that a stream is not allowed +// when http status codes are not being used. +func (s *Stream) Refuse() error { + if s.replied { + return nil + } + s.replied = true + return s.conn.sendReset(spdy.RefusedStream, s) +} + +// Cancel sends a reset frame with the status canceled. This +// can be used at any time by the creator of the Stream to +// indicate the stream is no longer needed. +func (s *Stream) Cancel() error { + return s.conn.sendReset(spdy.Cancel, s) +} + +// ReceiveHeader receives a header sent on the other side +// of the stream. This function will block until a header +// is received or stream is closed. +func (s *Stream) ReceiveHeader() (http.Header, error) { + select { + case <-s.closeChan: + break + case header, ok := <-s.headerChan: + if !ok { + return nil, fmt.Errorf("header chan closed") + } + return header, nil + } + return nil, fmt.Errorf("stream closed") +} + +// Parent returns the parent stream +func (s *Stream) Parent() *Stream { + return s.parent +} + +// Headers returns the headers used to create the stream +func (s *Stream) Headers() http.Header { + return s.headers +} + +// String returns the string version of stream using the +// streamId to uniquely identify the stream +func (s *Stream) String() string { + return fmt.Sprintf("stream:%d", s.streamId) +} + +// Identifier returns a 32 bit identifier for the stream +func (s *Stream) Identifier() uint32 { + return uint32(s.streamId) +} + +// IsFinished returns whether the stream has finished +// sending data +func (s *Stream) IsFinished() bool { + return s.finished +} + +// Implement net.Conn interface + +func (s *Stream) LocalAddr() net.Addr { + return s.conn.conn.LocalAddr() +} + +func (s *Stream) RemoteAddr() net.Addr { + return s.conn.conn.RemoteAddr() +} + +// TODO set per stream values instead of connection-wide + +func (s *Stream) SetDeadline(t time.Time) error { + return s.conn.conn.SetDeadline(t) +} + +func (s *Stream) SetReadDeadline(t time.Time) error { + return s.conn.conn.SetReadDeadline(t) +} + +func (s *Stream) SetWriteDeadline(t time.Time) error { + return s.conn.conn.SetWriteDeadline(t) +} + +func (s *Stream) closeRemoteChannels() { + s.closeLock.Lock() + defer s.closeLock.Unlock() + select { + case <-s.closeChan: + default: + close(s.closeChan) + } +} diff --git a/vendor/github.com/docker/spdystream/utils.go b/vendor/github.com/docker/spdystream/utils.go new file mode 100644 index 00000000000..1b2c199a402 --- /dev/null +++ b/vendor/github.com/docker/spdystream/utils.go @@ -0,0 +1,16 @@ +package spdystream + +import ( + "log" + "os" +) + +var ( + DEBUG = os.Getenv("DEBUG") +) + +func debugMessage(fmt string, args ...interface{}) { + if DEBUG != "" { + log.Printf(fmt, args...) + } +} diff --git a/vendor/github.com/golang-sql/civil/CONTRIBUTING.md b/vendor/github.com/golang-sql/civil/CONTRIBUTING.md new file mode 100644 index 00000000000..d0635c3ab56 --- /dev/null +++ b/vendor/github.com/golang-sql/civil/CONTRIBUTING.md @@ -0,0 +1,73 @@ +# Contributing + +1. Sign one of the contributor license agreements below. + +#### Running + +Once you've done the necessary setup, you can run the integration tests by +running: + +``` sh +$ go test -v github.com/golang-sql/civil +``` + +## Contributor License Agreements + +Before we can accept your pull requests you'll need to sign a Contributor +License Agreement (CLA): + +- **If you are an individual writing original source code** and **you own the +intellectual property**, then you'll need to sign an [individual CLA][indvcla]. +- **If you work for a company that wants to allow you to contribute your +work**, then you'll need to sign a [corporate CLA][corpcla]. + +You can sign these electronically (just scroll to the bottom). After that, +we'll be able to accept your pull requests. + +## Contributor Code of Conduct + +As contributors and maintainers of this project, +and in the interest of fostering an open and welcoming community, +we pledge to respect all people who contribute through reporting issues, +posting feature requests, updating documentation, +submitting pull requests or patches, and other activities. + +We are committed to making participation in this project +a harassment-free experience for everyone, +regardless of level of experience, gender, gender identity and expression, +sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, +such as physical or electronic +addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. +By adopting this Code of Conduct, +project maintainers commit themselves to fairly and consistently +applying these principles to every aspect of managing this project. +Project maintainers who do not follow or enforce the Code of Conduct +may be permanently removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior +may be reported by opening an issue +or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) + +[gcloudcli]: https://developers.google.com/cloud/sdk/gcloud/ +[indvcla]: https://developers.google.com/open-source/cla/individual +[corpcla]: https://developers.google.com/open-source/cla/corporate \ No newline at end of file diff --git a/vendor/github.com/golang-sql/civil/LICENSE b/vendor/github.com/golang-sql/civil/LICENSE new file mode 100644 index 00000000000..7a4a3ea2424 --- /dev/null +++ b/vendor/github.com/golang-sql/civil/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/vendor/github.com/golang-sql/civil/README.md b/vendor/github.com/golang-sql/civil/README.md new file mode 100644 index 00000000000..3a7956ecda4 --- /dev/null +++ b/vendor/github.com/golang-sql/civil/README.md @@ -0,0 +1,15 @@ +# Civil Date and Time + +[![GoDoc](https://godoc.org/github.com/golang-sql/civil?status.svg)](https://godoc.org/github.com/golang-sql/civil) + +Civil provides Date, Time of Day, and DateTime data types. + +While there are many uses, using specific types when working +with databases make is conceptually eaiser to understand what value +is set in the remote system. + +## Source + +This civil package was extracted and forked from `cloud.google.com/go/civil`. +As such the license and contributing requirements remain the same as that +module. diff --git a/vendor/cloud.google.com/go/civil/civil.go b/vendor/github.com/golang-sql/civil/civil.go similarity index 100% rename from vendor/cloud.google.com/go/civil/civil.go rename to vendor/github.com/golang-sql/civil/civil.go diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/.travis.yml b/vendor/github.com/santhosh-tekuri/jsonschema/.travis.yml new file mode 100644 index 00000000000..1ab35ab1b1f --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/.travis.yml @@ -0,0 +1,10 @@ +language: go + +go: + - 1.8.1 + +script: + - ./go.test.sh + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/LICENSE b/vendor/github.com/santhosh-tekuri/jsonschema/LICENSE new file mode 100644 index 00000000000..65cd403ab0e --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017 Santhosh Kumar Tekuri. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/README.md b/vendor/github.com/santhosh-tekuri/jsonschema/README.md new file mode 100644 index 00000000000..3d369d71d74 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/README.md @@ -0,0 +1,148 @@ +# jsonschema + +[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +[![GoDoc](https://godoc.org/github.com/santhosh-tekuri/jsonschema?status.svg)](https://godoc.org/github.com/santhosh-tekuri/jsonschema) +[![Go Report Card](https://goreportcard.com/badge/github.com/santhosh-tekuri/jsonschema)](https://goreportcard.com/report/github.com/santhosh-tekuri/jsonschema) +[![Build Status](https://travis-ci.org/santhosh-tekuri/jsonschema.svg?branch=master)](https://travis-ci.org/santhosh-tekuri/jsonschema) +[![codecov.io](https://codecov.io/github/santhosh-tekuri/jsonschema/coverage.svg?branch=master)](https://codecov.io/github/santhosh-tekuri/jsonschema?branch=master) + +Package jsonschema provides json-schema compilation and validation. + +This implementation of JSON Schema, supports draft4, draft6 and draft7. + +Passes all tests(including optional) in https://github.com/json-schema/JSON-Schema-Test-Suite + +An example of using this package: + +```go +schema, err := jsonschema.Compile("schemas/purchaseOrder.json") +if err != nil { + return err +} +f, err := os.Open("purchaseOrder.json") +if err != nil { + return err +} +defer f.Close() +if err = schema.Validate(f); err != nil { + return err +} +``` + +The schema is compiled against the version specified in `$schema` property. +If `$schema` property is missing, it uses latest draft which currently is draft7. +You can force to use draft4 when `$schema` is missing, as follows: + +```go +compiler := jsonschema.NewCompiler() +compler.Draft = jsonschema.Draft4 +``` + +you can also validate go value using `schema.ValidateInterface(interface{})` method. +but the argument should not be user-defined struct. + + +This package supports loading json-schema from filePath and fileURL. + +To load json-schema from HTTPURL, add following import: + +```go +import _ "github.com/santhosh-tekuri/jsonschema/httploader" +``` + +Loading from urls for other schemes (such as ftp), can be plugged in. see package jsonschema/httploader +for an example + +To load json-schema from in-memory: + +```go +data := `{"type": "string"}` +url := "sch.json" +compiler := jsonschema.NewCompiler() +if err := compiler.AddResource(url, strings.NewReader(data)); err != nil { + return err +} +schema, err := compiler.Compile(url) +if err != nil { + return err +} +f, err := os.Open("doc.json") +if err != nil { + return err +} +defer f.Close() +if err = schema.Validate(f); err != nil { + return err +} +``` + +This package supports json string formats: +- date-time +- date +- time +- hostname +- email +- ip-address +- ipv4 +- ipv6 +- uri +- uriref/uri-reference +- regex +- format +- json-pointer +- relative-json-pointer +- uri-template (limited validation) + +Developers can register their own formats using package "github.com/santhosh-tekuri/jsonschema/formats". + +"base64" contentEncoding is supported. Custom decoders can be registered using package "github.com/santhosh-tekuri/jsonschema/decoders". + +"application/json" contentMediaType is supported. Custom mediatypes can be registered using package "github.com/santhosh-tekuri/jsonschema/mediatypes". + +## ValidationError + +The ValidationError returned by Validate method contains detailed context to understand why and where the error is. + +schema.json: +```json +{ + "$ref": "t.json#/definitions/employee" +} +``` + +t.json: +```json +{ + "definitions": { + "employee": { + "type": "string" + } + } +} +``` + +doc.json: +```json +1 +``` + +Validating `doc.json` with `schema.json`, gives following ValidationError: +``` +I[#] S[#] doesn't validate with "schema.json#" + I[#] S[#/$ref] doesn't valide with "t.json#/definitions/employee" + I[#] S[#/definitions/employee/type] expected string, but got number +``` + +Here `I` stands for instance document and `S` stands for schema document. +The json-fragments that caused error in instance and schema documents are represented using json-pointer notation. +Nested causes are printed with indent. + +## CLI + +```bash +jv []... +``` + +if no `` arguments are passed, it simply validates the ``. + +exit-code is 1, if there are any validation errors diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/compiler.go b/vendor/github.com/santhosh-tekuri/jsonschema/compiler.go new file mode 100644 index 00000000000..9ffb715ab05 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/compiler.go @@ -0,0 +1,534 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonschema + +import ( + "encoding/json" + "fmt" + "io" + "math/big" + "regexp" + "strings" + + "github.com/santhosh-tekuri/jsonschema/decoders" + "github.com/santhosh-tekuri/jsonschema/formats" + "github.com/santhosh-tekuri/jsonschema/loader" + "github.com/santhosh-tekuri/jsonschema/mediatypes" +) + +func init() { + formats.Register("encoding", func(s string) bool { + _, ok := decoders.Get(s) + return ok + }) + formats.Register("mediatype", func(s string) bool { + _, ok := mediatypes.Get(s) + return ok + }) +} + +// A Draft represents json-schema draft +type Draft struct { + meta *Schema + id string // property name used to represent schema id. + version int +} + +var latest = Draft7 + +func (draft *Draft) validateSchema(url, ptr string, v interface{}) error { + if meta := draft.meta; meta != nil { + if err := meta.validate(v); err != nil { + addContext(ptr, "", err) + finishSchemaContext(err, meta) + finishInstanceContext(err) + var instancePtr string + if ptr == "" { + instancePtr = "#" + } else { + instancePtr = "#/" + ptr + } + return &SchemaError{ + url, + &ValidationError{ + Message: fmt.Sprintf("doesn't validate with %q", meta.URL+meta.Ptr), + InstancePtr: instancePtr, + SchemaURL: meta.URL, + SchemaPtr: "#", + Causes: []*ValidationError{err.(*ValidationError)}, + }, + } + } + } + return nil +} + +// A Compiler represents a json-schema compiler. +// +// Currently draft4, draft6 and draft7 are supported +type Compiler struct { + // Draft represents the draft used when '$schema' attribute is missing. + // + // This defaults to latest draft (currently draft7). + Draft *Draft + resources map[string]*resource + + // ExtractAnnotations tells whether schema annotations has to be extracted + // in compiled Schema or not. + ExtractAnnotations bool +} + +// NewCompiler returns a draft7 json-schema Compiler object. +func NewCompiler() *Compiler { + return &Compiler{Draft: latest, resources: make(map[string]*resource)} +} + +// AddResource adds in-memory resource to the compiler. +// +// Note that url must not have fragment +func (c *Compiler) AddResource(url string, r io.Reader) error { + res, err := newResource(url, r) + if err != nil { + return err + } + c.resources[res.url] = res + return nil +} + +// MustCompile is like Compile but panics if the url cannot be compiled to *Schema. +// It simplifies safe initialization of global variables holding compiled Schemas. +func (c *Compiler) MustCompile(url string) *Schema { + s, err := c.Compile(url) + if err != nil { + panic(fmt.Sprintf("jsonschema: Compile(%q): %s", url, err)) + } + return s +} + +// Compile parses json-schema at given url returns, if successful, +// a Schema object that can be used to match against json. +func (c *Compiler) Compile(url string) (*Schema, error) { + base, fragment := split(url) + if _, ok := c.resources[base]; !ok { + r, err := loader.Load(base) + if err != nil { + return nil, err + } + defer r.Close() + if err := c.AddResource(base, r); err != nil { + return nil, err + } + } + r := c.resources[base] + if r.draft == nil { + if m, ok := r.doc.(map[string]interface{}); ok { + if url, ok := m["$schema"]; ok { + switch url { + case "http://json-schema.org/schema#": + r.draft = latest + case "http://json-schema.org/draft-07/schema#": + r.draft = Draft7 + case "http://json-schema.org/draft-06/schema#": + r.draft = Draft6 + case "http://json-schema.org/draft-04/schema#": + r.draft = Draft4 + default: + return nil, fmt.Errorf("unknown $schema %q", url) + } + } + } + if r.draft == nil { + r.draft = c.Draft + } + } + return c.compileRef(r, r.url, fragment) +} + +func (c Compiler) compileRef(r *resource, base, ref string) (*Schema, error) { + var err error + if rootFragment(ref) { + if _, ok := r.schemas["#"]; !ok { + if err := r.draft.validateSchema(r.url, "", r.doc); err != nil { + return nil, err + } + s := &Schema{URL: r.url, Ptr: "#"} + r.schemas["#"] = s + if m, ok := r.doc.(map[string]interface{}); ok { + if _, err := c.compile(r, s, base, m); err != nil { + return nil, err + } + } else { + if _, err := c.compile(r, s, base, r.doc); err != nil { + return nil, err + } + } + } + return r.schemas["#"], nil + } + + if strings.HasPrefix(ref, "#/") { + if _, ok := r.schemas[ref]; !ok { + ptrBase, doc, err := r.resolvePtr(ref) + if err != nil { + return nil, err + } + if err := r.draft.validateSchema(r.url, strings.TrimPrefix(ref, "#/"), doc); err != nil { + return nil, err + } + r.schemas[ref] = &Schema{URL: base, Ptr: ref} + if _, err := c.compile(r, r.schemas[ref], ptrBase, doc); err != nil { + return nil, err + } + } + return r.schemas[ref], nil + } + + refURL, err := resolveURL(base, ref) + if err != nil { + return nil, err + } + if rs, ok := r.schemas[refURL]; ok { + return rs, nil + } + + ids := make(map[string]map[string]interface{}) + if err := resolveIDs(r.draft, r.url, r.doc, ids); err != nil { + return nil, err + } + if v, ok := ids[refURL]; ok { + if err := r.draft.validateSchema(r.url, "", v); err != nil { + return nil, err + } + u, f := split(refURL) + s := &Schema{URL: u, Ptr: f} + r.schemas[refURL] = s + if err := c.compileMap(r, s, refURL, v); err != nil { + return nil, err + } + return s, nil + } + + base, _ = split(refURL) + if base == r.url { + return nil, fmt.Errorf("invalid ref: %q", refURL) + } + return c.Compile(refURL) +} + +func (c Compiler) compile(r *resource, s *Schema, base string, m interface{}) (*Schema, error) { + if s == nil { + s = new(Schema) + s.URL, _ = split(base) + } + switch m := m.(type) { + case bool: + s.Always = &m + return s, nil + default: + return s, c.compileMap(r, s, base, m.(map[string]interface{})) + } +} + +func (c Compiler) compileMap(r *resource, s *Schema, base string, m map[string]interface{}) error { + var err error + + if id, ok := m[r.draft.id]; ok { + if base, err = resolveURL(base, id.(string)); err != nil { + return err + } + } + + if ref, ok := m["$ref"]; ok { + b, _ := split(base) + s.Ref, err = c.compileRef(r, b, ref.(string)) + if err != nil { + return err + } + // All other properties in a "$ref" object MUST be ignored + return nil + } + + if t, ok := m["type"]; ok { + switch t := t.(type) { + case string: + s.Types = []string{t} + case []interface{}: + s.Types = toStrings(t) + } + } + + if e, ok := m["enum"]; ok { + s.Enum = e.([]interface{}) + allPrimitives := true + for _, item := range s.Enum { + switch jsonType(item) { + case "object", "array": + allPrimitives = false + break + } + } + s.enumError = "enum failed" + if allPrimitives { + if len(s.Enum) == 1 { + s.enumError = fmt.Sprintf("value must be %#v", s.Enum[0]) + } else { + strEnum := make([]string, len(s.Enum)) + for i, item := range s.Enum { + strEnum[i] = fmt.Sprintf("%#v", item) + } + s.enumError = fmt.Sprintf("value must be one of %s", strings.Join(strEnum, ", ")) + } + } + } + + loadSchema := func(pname string) (*Schema, error) { + if pvalue, ok := m[pname]; ok { + return c.compile(r, nil, base, pvalue) + } + return nil, nil + } + + if s.Not, err = loadSchema("not"); err != nil { + return err + } + + loadSchemas := func(pname string) ([]*Schema, error) { + if pvalue, ok := m[pname]; ok { + pvalue := pvalue.([]interface{}) + schemas := make([]*Schema, len(pvalue)) + for i, v := range pvalue { + sch, err := c.compile(r, nil, base, v) + if err != nil { + return nil, err + } + schemas[i] = sch + } + return schemas, nil + } + return nil, nil + } + if s.AllOf, err = loadSchemas("allOf"); err != nil { + return err + } + if s.AnyOf, err = loadSchemas("anyOf"); err != nil { + return err + } + if s.OneOf, err = loadSchemas("oneOf"); err != nil { + return err + } + + loadInt := func(pname string) int { + if num, ok := m[pname]; ok { + i, _ := num.(json.Number).Int64() + return int(i) + } + return -1 + } + s.MinProperties, s.MaxProperties = loadInt("minProperties"), loadInt("maxProperties") + + if req, ok := m["required"]; ok { + s.Required = toStrings(req.([]interface{})) + } + + if props, ok := m["properties"]; ok { + props := props.(map[string]interface{}) + s.Properties = make(map[string]*Schema, len(props)) + for pname, pmap := range props { + s.Properties[pname], err = c.compile(r, nil, base, pmap) + if err != nil { + return err + } + } + } + + if regexProps, ok := m["regexProperties"]; ok { + s.RegexProperties = regexProps.(bool) + } + + if patternProps, ok := m["patternProperties"]; ok { + patternProps := patternProps.(map[string]interface{}) + s.PatternProperties = make(map[*regexp.Regexp]*Schema, len(patternProps)) + for pattern, pmap := range patternProps { + s.PatternProperties[regexp.MustCompile(pattern)], err = c.compile(r, nil, base, pmap) + if err != nil { + return err + } + } + } + + if additionalProps, ok := m["additionalProperties"]; ok { + switch additionalProps := additionalProps.(type) { + case bool: + if !additionalProps { + s.AdditionalProperties = false + } + case map[string]interface{}: + s.AdditionalProperties, err = c.compile(r, nil, base, additionalProps) + if err != nil { + return err + } + } + } + + if deps, ok := m["dependencies"]; ok { + deps := deps.(map[string]interface{}) + s.Dependencies = make(map[string]interface{}, len(deps)) + for pname, pvalue := range deps { + switch pvalue := pvalue.(type) { + case []interface{}: + s.Dependencies[pname] = toStrings(pvalue) + default: + s.Dependencies[pname], err = c.compile(r, nil, base, pvalue) + if err != nil { + return err + } + } + } + } + + s.MinItems, s.MaxItems = loadInt("minItems"), loadInt("maxItems") + + if unique, ok := m["uniqueItems"]; ok { + s.UniqueItems = unique.(bool) + } + + if items, ok := m["items"]; ok { + switch items := items.(type) { + case []interface{}: + s.Items, err = loadSchemas("items") + if err != nil { + return err + } + if additionalItems, ok := m["additionalItems"]; ok { + switch additionalItems := additionalItems.(type) { + case bool: + s.AdditionalItems = additionalItems + case map[string]interface{}: + s.AdditionalItems, err = c.compile(r, nil, base, additionalItems) + if err != nil { + return err + } + } + } else { + s.AdditionalItems = true + } + default: + s.Items, err = c.compile(r, nil, base, items) + if err != nil { + return err + } + } + } + + s.MinLength, s.MaxLength = loadInt("minLength"), loadInt("maxLength") + + if pattern, ok := m["pattern"]; ok { + s.Pattern = regexp.MustCompile(pattern.(string)) + } + + if format, ok := m["format"]; ok { + s.FormatName = format.(string) + s.Format, _ = formats.Get(s.FormatName) + } + + loadFloat := func(pname string) *big.Float { + if num, ok := m[pname]; ok { + r, _ := new(big.Float).SetString(string(num.(json.Number))) + return r + } + return nil + } + + s.Minimum = loadFloat("minimum") + if exclusive, ok := m["exclusiveMinimum"]; ok { + if exclusive, ok := exclusive.(bool); ok { + if exclusive { + s.Minimum, s.ExclusiveMinimum = nil, s.Minimum + } + } else { + s.ExclusiveMinimum = loadFloat("exclusiveMinimum") + } + } + + s.Maximum = loadFloat("maximum") + if exclusive, ok := m["exclusiveMaximum"]; ok { + if exclusive, ok := exclusive.(bool); ok { + if exclusive { + s.Maximum, s.ExclusiveMaximum = nil, s.Maximum + } + } else { + s.ExclusiveMaximum = loadFloat("exclusiveMaximum") + } + } + + s.MultipleOf = loadFloat("multipleOf") + + if c.ExtractAnnotations { + if title, ok := m["title"]; ok { + s.Title = title.(string) + } + if description, ok := m["description"]; ok { + s.Description = description.(string) + } + s.Default = m["default"] + } + + if r.draft.version >= 6 { + if c, ok := m["const"]; ok { + s.Constant = []interface{}{c} + } + if s.PropertyNames, err = loadSchema("propertyNames"); err != nil { + return err + } + if s.Contains, err = loadSchema("contains"); err != nil { + return err + } + } + + if r.draft.version >= 7 { + if m["if"] != nil && (m["then"] != nil || m["else"] != nil) { + if s.If, err = loadSchema("if"); err != nil { + return err + } + if s.Then, err = loadSchema("then"); err != nil { + return err + } + if s.Else, err = loadSchema("else"); err != nil { + return err + } + + if c.ExtractAnnotations { + if readOnly, ok := m["readOnly"]; ok { + s.ReadOnly = readOnly.(bool) + } + if writeOnly, ok := m["writeOnly"]; ok { + s.WriteOnly = writeOnly.(bool) + } + if examples, ok := m["examples"]; ok { + s.Examples = examples.([]interface{}) + } + } + } + + if encoding, ok := m["contentEncoding"]; ok { + s.ContentEncoding = encoding.(string) + s.Decoder, _ = decoders.Get(s.ContentEncoding) + } + if mediaType, ok := m["contentMediaType"]; ok { + s.ContentMediaType = mediaType.(string) + s.MediaType, _ = mediatypes.Get(s.ContentMediaType) + } + } + + return nil +} + +func toStrings(arr []interface{}) []string { + s := make([]string, len(arr)) + for i, v := range arr { + s[i] = v.(string) + } + return s +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/decoders/decoders.go b/vendor/github.com/santhosh-tekuri/jsonschema/decoders/decoders.go new file mode 100644 index 00000000000..4a1edc43c57 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/decoders/decoders.go @@ -0,0 +1,32 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package decoders provides functions to decode encoded-string. +// +// It allows developers to register custom encodings, that can be used +// in json-schema for validation. +package decoders + +import ( + "encoding/base64" +) + +// The Decoder type is a function, that returns +// the bytes represented by encoded string. +type Decoder func(string) ([]byte, error) + +var decoders = map[string]Decoder{ + "base64": base64.StdEncoding.DecodeString, +} + +// Register registers Decoder object for given encoding. +func Register(name string, d Decoder) { + decoders[name] = d +} + +// Get returns Decoder object for given encoding, if found. +func Get(name string) (Decoder, bool) { + d, ok := decoders[name] + return d, ok +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/doc.go b/vendor/github.com/santhosh-tekuri/jsonschema/doc.go new file mode 100644 index 00000000000..ea444425313 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/doc.go @@ -0,0 +1,77 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package jsonschema provides json-schema compilation and validation. + +This implementation of JSON Schema, supports draft4, draft6 and draft7. +Passes all tests(including optional) in https://github.com/json-schema/JSON-Schema-Test-Suite + +An example of using this package: + + schema, err := jsonschema.Compile("schemas/purchaseOrder.json") + if err != nil { + return err + } + f, err := os.Open("purchaseOrder.json") + if err != nil { + return err + } + defer f.Close() + if err = schema.Validate(f); err != nil { + return err + } + +The schema is compiled against the version specified in `$schema` property. +If `$schema` property is missing, it uses latest draft which currently is draft7. +You can force to use draft4 when `$schema` is missing, as follows: + + compiler := jsonschema.NewCompiler() + compler.Draft = jsonschema.Draft4 + +you can also validate go value using schema.ValidateInterface(interface{}) method. +but the argument should not be user-defined struct. + +This package supports loading json-schema from filePath and fileURL. + +To load json-schema from HTTPURL, add following import: + + import _ "github.com/santhosh-tekuri/jsonschema/httploader" + +Loading from urls for other schemes (such as ftp), can be plugged in. see package jsonschema/httploader +for an example + +To load json-schema from in-memory: + + data := `{"type": "string"}` + url := "sch.json" + compiler := jsonschema.NewCompiler() + if err := compiler.AddResource(url, strings.NewReader(data)); err != nil { + return err + } + schema, err := compiler.Compile(url) + if err != nil { + return err + } + f, err := os.Open("doc.json") + if err != nil { + return err + } + defer f.Close() + if err = schema.Validate(f); err != nil { + return err + } + +This package supports json string formats: date-time, date, time, hostname, email, ip-address, ipv4, ipv6, uri, uriref, regex, +format, json-pointer, relative-json-pointer, uri-template (limited validation). Developers can register their own formats using +package "github.com/santhosh-tekuri/jsonschema/formats". + +"base64" contentEncoding is supported. Custom decoders can be registered using package "github.com/santhosh-tekuri/jsonschema/decoders". + +"application/json" contentMediaType is supported. Custom mediatypes can be registered using package "github.com/santhosh-tekuri/jsonschema/mediatypes". + +The ValidationError returned by Validate method contains detailed context to understand why and where the error is. + +*/ +package jsonschema diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/draft4.go b/vendor/github.com/santhosh-tekuri/jsonschema/draft4.go new file mode 100644 index 00000000000..6c787bc1a76 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/draft4.go @@ -0,0 +1,172 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonschema + +import "strings" + +// Draft4 respresents http://json-schema.org/specification-links.html#draft-4 +var Draft4 = &Draft{id: "id", version: 4} + +func init() { + c := NewCompiler() + url := "http://json-schema.org/draft-04/schema" + err := c.AddResource(url, strings.NewReader(`{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "positiveInteger": { + "type": "integer", + "minimum": 0 + }, + "positiveIntegerDefault0": { + "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] + }, + "simpleTypes": { + "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "uniqueItems": true + } + }, + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uriref" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "multipleOf": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "boolean", + "default": false + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "boolean", + "default": false + }, + "maxLength": { "$ref": "#/definitions/positiveInteger" }, + "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/positiveInteger" }, + "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxProperties": { "$ref": "#/definitions/positiveInteger" }, + "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "regexProperties": true, + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "regexProperties": { "type": "boolean" }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" }, + "format": { "type": "string", "format": "format" }, + "$ref": { "type": "string" } + }, + "dependencies": { + "exclusiveMaximum": [ "maximum" ], + "exclusiveMinimum": [ "minimum" ] + }, + "default": {} + }`)) + if err != nil { + panic(err) + } + Draft4.meta = c.MustCompile(url) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/draft6.go b/vendor/github.com/santhosh-tekuri/jsonschema/draft6.go new file mode 100644 index 00000000000..310245d1974 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/draft6.go @@ -0,0 +1,170 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonschema + +import "strings" + +// Draft6 respresents http://json-schema.org/specification-links.html#draft-6 +var Draft6 = &Draft{id: "$id", version: 6} + +func init() { + c := NewCompiler() + url := "http://json-schema.org/draft-06/schema" + err := c.AddResource(url, strings.NewReader(`{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "regexProperties": true, + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string", "format": "format" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": {} + }`)) + if err != nil { + panic(err) + } + Draft6.meta = c.MustCompile(url) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/draft7.go b/vendor/github.com/santhosh-tekuri/jsonschema/draft7.go new file mode 100644 index 00000000000..68c88f0a08f --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/draft7.go @@ -0,0 +1,196 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonschema + +import "strings" + +// Draft7 respresents http://json-schema.org/specification-links.html#draft-7 +var Draft7 = &Draft{id: "$id", version: 7} + +func init() { + c := NewCompiler() + url := "http://json-schema.org/draft-07/schema" + err := c.AddResource(url, strings.NewReader(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { + "type": "string", + "format": "format" + }, + "contentMediaType": { + "type": "string", + "format": "mediatype" + }, + "contentEncoding": { + "type": "string", + "format": "encoding" + }, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": true + }`)) + if err != nil { + panic(err) + } + Draft7.meta = c.MustCompile(url) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/errors.go b/vendor/github.com/santhosh-tekuri/jsonschema/errors.go new file mode 100644 index 00000000000..4bb61a925a5 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/errors.go @@ -0,0 +1,122 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonschema + +import ( + "fmt" + "strings" +) + +// InvalidJSONTypeError is the error type returned by ValidateInteface. +// this tells that specified go object is not valid jsonType. +type InvalidJSONTypeError string + +func (e InvalidJSONTypeError) Error() string { + return fmt.Sprintf("invalid jsonType: %s", string(e)) +} + +// SchemaError is the error type returned by Compile. +type SchemaError struct { + // SchemaURL is the url to json-schema that filed to compile. + // This is helpful, if your schema refers to external schemas + SchemaURL string + + // Err is the error that occurred during compilation. + // It could be ValidationError, because compilation validates + // given schema against the json meta-schema + Err error +} + +func (se *SchemaError) Error() string { + return fmt.Sprintf("json-schema %q compilation failed. Reason:\n%s", se.SchemaURL, se.Err) +} + +// ValidationError is the error type returned by Validate. +type ValidationError struct { + // Message describes error + Message string + + // InstancePtr is json-pointer which refers to json-fragment in json instance + // that is not valid + InstancePtr string + + // SchemaURL is the url to json-schema against which validation failed. + // This is helpful, if your schema refers to external schemas + SchemaURL string + + // SchemaPtr is json-pointer which refers to json-fragment in json schema + // that failed to satisfy + SchemaPtr string + + // Causes details the nested validation errors + Causes []*ValidationError +} + +func (ve *ValidationError) add(causes ...error) error { + for _, cause := range causes { + addContext(ve.InstancePtr, ve.SchemaPtr, cause) + ve.Causes = append(ve.Causes, cause.(*ValidationError)) + } + return ve +} + +func (ve *ValidationError) Error() string { + msg := fmt.Sprintf("I[%s] S[%s] %s", ve.InstancePtr, ve.SchemaPtr, ve.Message) + for _, c := range ve.Causes { + for _, line := range strings.Split(c.Error(), "\n") { + msg += "\n " + line + } + } + return msg +} + +func validationError(schemaPtr string, format string, a ...interface{}) *ValidationError { + return &ValidationError{fmt.Sprintf(format, a...), "", "", schemaPtr, nil} +} + +func addContext(instancePtr, schemaPtr string, err error) error { + ve := err.(*ValidationError) + ve.InstancePtr = joinPtr(instancePtr, ve.InstancePtr) + if len(ve.SchemaURL) == 0 { + ve.SchemaPtr = joinPtr(schemaPtr, ve.SchemaPtr) + } + for _, cause := range ve.Causes { + addContext(instancePtr, schemaPtr, cause) + } + return ve +} + +func finishSchemaContext(err error, s *Schema) { + ve := err.(*ValidationError) + if len(ve.SchemaURL) == 0 { + ve.SchemaURL = s.URL + ve.SchemaPtr = s.Ptr + "/" + ve.SchemaPtr + for _, cause := range ve.Causes { + finishSchemaContext(cause, s) + } + } +} + +func finishInstanceContext(err error) { + ve := err.(*ValidationError) + if len(ve.InstancePtr) == 0 { + ve.InstancePtr = "#" + } else { + ve.InstancePtr = "#/" + ve.InstancePtr + } + for _, cause := range ve.Causes { + finishInstanceContext(cause) + } +} + +func joinPtr(ptr1, ptr2 string) string { + if len(ptr1) == 0 { + return ptr2 + } + if len(ptr2) == 0 { + return ptr1 + } + return ptr1 + "/" + ptr2 +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/formats/formats.go b/vendor/github.com/santhosh-tekuri/jsonschema/formats/formats.go new file mode 100644 index 00000000000..03efa2bc46f --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/formats/formats.go @@ -0,0 +1,295 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package formats provides functions to check string against format. +// +// It allows developers to register custom formats, that can be used +// in json-schema for validation. +package formats + +import ( + "net" + "net/mail" + "net/url" + "regexp" + "strconv" + "strings" + "time" +) + +// The Format type is a function, to check +// whether given string is in valid format. +type Format func(string) bool + +var formats = map[string]Format{ + "date-time": IsDateTime, + "date": IsDate, + "time": IsTime, + "hostname": IsHostname, + "email": IsEmail, + "ip-address": IsIPV4, + "ipv4": IsIPV4, + "ipv6": IsIPV6, + "uri": IsURI, + "iri": IsURI, + "uri-reference": IsURIReference, + "uriref": IsURIReference, + "iri-reference": IsURIReference, + "uri-template": IsURITemplate, + "regex": IsRegex, + "json-pointer": IsJSONPointer, + "relative-json-pointer": IsRelativeJSONPointer, +} + +func init() { + formats["format"] = IsFormat +} + +// Register registers Format object for given format name. +func Register(name string, f Format) { + formats[name] = f +} + +// Get returns Format object for given format name, if found. +func Get(name string) (Format, bool) { + f, ok := formats[name] + return f, ok +} + +// IsFormat tells whether given string is a valid format that is registered. +func IsFormat(s string) bool { + _, ok := formats[s] + return ok +} + +// IsDateTime tells whether given string is a valid date representation +// as defined by RFC 3339, section 5.6. +// +// Note: this is unable to parse UTC leap seconds. See https://github.com/golang/go/issues/8728. +func IsDateTime(s string) bool { + if _, err := time.Parse(time.RFC3339, s); err == nil { + return true + } + if _, err := time.Parse(time.RFC3339Nano, s); err == nil { + return true + } + return false +} + +// IsDate tells whether given string is a valid full-date production +// as defined by RFC 3339, section 5.6. +func IsDate(s string) bool { + _, err := time.Parse("2006-01-02", s) + return err == nil +} + +// IsTime tells whether given string is a valid full-time production +// as defined by RFC 3339, section 5.6. +func IsTime(s string) bool { + if _, err := time.Parse("15:04:05Z07:00", s); err == nil { + return true + } + if _, err := time.Parse("15:04:05.999999999Z07:00", s); err == nil { + return true + } + return false +} + +// IsHostname tells whether given string is a valid representation +// for an Internet host name, as defined by RFC 1034, section 3.1. +// +// See https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names, for details. +func IsHostname(s string) bool { + // entire hostname (including the delimiting dots but not a trailing dot) has a maximum of 253 ASCII characters + s = strings.TrimSuffix(s, ".") + if len(s) > 253 { + return false + } + + // Hostnames are composed of series of labels concatenated with dots, as are all domain names + for _, label := range strings.Split(s, ".") { + // Each label must be from 1 to 63 characters long + if labelLen := len(label); labelLen < 1 || labelLen > 63 { + return false + } + + // labels could not start with a digit or with a hyphen + if first := s[0]; (first >= '0' && first <= '9') || (first == '-') { + return false + } + + // must not end with a hyphen + if label[len(label)-1] == '-' { + return false + } + + // labels may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner), + // the digits '0' through '9', and the hyphen ('-') + for _, c := range label { + if valid := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '-'); !valid { + return false + } + } + } + + return true +} + +// IsEmail tells whether given string is a valid Internet email address +// as defined by RFC 5322, section 3.4.1. +// +// See https://en.wikipedia.org/wiki/Email_address, for details. +func IsEmail(s string) bool { + // entire email address to be no more than 254 characters long + if len(s) > 254 { + return false + } + + // email address is generally recognized as having two parts joined with an at-sign + at := strings.LastIndexByte(s, '@') + if at == -1 { + return false + } + local := s[0:at] + domain := s[at+1:] + + // local part may be up to 64 characters long + if len(local) > 64 { + return false + } + + // domain must match the requirements for a hostname + if !IsHostname(domain) { + return false + } + + _, err := mail.ParseAddress(s) + return err == nil +} + +// IsIPV4 tells whether given string is a valid representation of an IPv4 address +// according to the "dotted-quad" ABNF syntax as defined in RFC 2673, section 3.2. +func IsIPV4(s string) bool { + groups := strings.Split(s, ".") + if len(groups) != 4 { + return false + } + for _, group := range groups { + n, err := strconv.Atoi(group) + if err != nil { + return false + } + if n < 0 || n > 255 { + return false + } + } + return true +} + +// IsIPV6 tells whether given string is a valid representation of an IPv6 address +// as defined in RFC 2373, section 2.2. +func IsIPV6(s string) bool { + if !strings.Contains(s, ":") { + return false + } + return net.ParseIP(s) != nil +} + +// IsURI tells whether given string is valid URI, according to RFC 3986. +func IsURI(s string) bool { + u, err := url.Parse(s) + return err == nil && u.IsAbs() +} + +// IsURIReference tells whether given string is a valid URI Reference +// (either a URI or a relative-reference), according to RFC 3986. +func IsURIReference(s string) bool { + _, err := url.Parse(s) + return err == nil +} + +// IsURITemplate tells whether given string is a valid URI Template +// according to RFC6570. +// +// Current implementation does minimal validation. +func IsURITemplate(s string) bool { + u, err := url.Parse(s) + if err != nil { + return false + } + for _, item := range strings.Split(u.RawPath, "/") { + depth := 0 + for _, ch := range item { + switch ch { + case '{': + depth++ + if depth != 1 { + return false + } + case '}': + depth-- + if depth != 0 { + return false + } + } + } + if depth != 0 { + return false + } + } + return true +} + +// IsRegex tells whether given string is a valid regular expression, +// according to the ECMA 262 regular expression dialect. +// +// The implementation uses go-lang regexp package. +func IsRegex(s string) bool { + _, err := regexp.Compile(s) + return err == nil +} + +// IsJSONPointer tells whether given string is a valid JSON Pointer. +// +// Note: It returns false for JSON Pointer URI fragments. +func IsJSONPointer(s string) bool { + if s != "" && !strings.HasPrefix(s, "/") { + return false + } + for _, item := range strings.Split(s, "/") { + for i := 0; i < len(item); i++ { + if item[i] == '~' { + if i == len(item)-1 { + return false + } + switch item[i+1] { + case '~', '0', '1': + // valid + default: + return false + } + } + } + } + return true +} + +// IsRelativeJSONPointer tells whether given string is a valid Relative JSON Pointer. +// +// see https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01#section-3 +func IsRelativeJSONPointer(s string) bool { + if s == "" { + return false + } + if s[0] == '0' { + s = s[1:] + } else if s[0] >= '0' && s[0] <= '9' { + for s != "" && s[0] >= '0' && s[0] <= '9' { + s = s[1:] + } + } else { + return false + } + return s == "#" || IsJSONPointer(s) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/go.mod b/vendor/github.com/santhosh-tekuri/jsonschema/go.mod new file mode 100644 index 00000000000..89a74866100 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/go.mod @@ -0,0 +1 @@ +module github.com/santhosh-tekuri/jsonschema diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/go.test.sh b/vendor/github.com/santhosh-tekuri/jsonschema/go.test.sh new file mode 100644 index 00000000000..88c4e8b6e7d --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -v -race -coverprofile=profile.out -covermode=atomic $d + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/loader/loader.go b/vendor/github.com/santhosh-tekuri/jsonschema/loader/loader.go new file mode 100644 index 00000000000..6ae19fb13b9 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/loader/loader.go @@ -0,0 +1,105 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package loader abstracts the reading document at given url. +// +// It allows developers to register loaders for different uri +// schemes. +package loader + +import ( + "fmt" + "io" + "net/url" + "os" + "path/filepath" + "runtime" + "strings" + "sync" +) + +// Loader is the interface that wraps the basic Load method. +// +// Load loads the document at given url and returns []byte, +// if successful. +type Loader interface { + Load(url string) (io.ReadCloser, error) +} + +type filePathLoader struct{} + +func (filePathLoader) Load(path string) (io.ReadCloser, error) { + return os.Open(path) +} + +type fileURLLoader struct{} + +func (fileURLLoader) Load(s string) (io.ReadCloser, error) { + u, err := url.Parse(s) + if err != nil { + return nil, err + } + f := u.Path + if runtime.GOOS == "windows" { + f = strings.TrimPrefix(f, "/") + f = filepath.FromSlash(f) + } + return os.Open(f) +} + +var registry = make(map[string]Loader) +var mutex = sync.RWMutex{} + +// SchemeNotRegisteredError is the error type returned by Load function. +// It tells that no Loader is registered for that URL Scheme. +type SchemeNotRegisteredError string + +func (s SchemeNotRegisteredError) Error() string { + return fmt.Sprintf("no Loader registered for scheme %s", string(s)) +} + +// Register registers given Loader for given URI Scheme. +func Register(scheme string, loader Loader) { + mutex.Lock() + defer mutex.Unlock() + registry[scheme] = loader +} + +// UnRegister unregisters the registered loader(if any) for given URI Scheme. +func UnRegister(scheme string) { + mutex.Lock() + defer mutex.Unlock() + delete(registry, scheme) +} + +func get(s string) (Loader, error) { + mutex.RLock() + defer mutex.RUnlock() + u, err := url.Parse(s) + if err != nil { + return nil, err + } + if loader, ok := registry[u.Scheme]; ok { + return loader, nil + } + return nil, SchemeNotRegisteredError(u.Scheme) +} + +// Load loads the document at given url and returns []byte, +// if successful. +// +// If no Loader is registered against the URI Scheme, then it +// returns *SchemeNotRegisteredError +var Load = func(url string) (io.ReadCloser, error) { + loader, err := get(url) + if err != nil { + return nil, err + } + return loader.Load(url) +} + +func init() { + Register("", filePathLoader{}) + Register("file", fileURLLoader{}) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/mediatypes/mediatypes.go b/vendor/github.com/santhosh-tekuri/jsonschema/mediatypes/mediatypes.go new file mode 100644 index 00000000000..3c4ec3f53bf --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/mediatypes/mediatypes.go @@ -0,0 +1,39 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package mediatypes provides functions to validate data against mediatype. +// +// It allows developers to register custom mediatypes, that can be used +// in json-schema for validation. +package mediatypes + +import ( + "bytes" + "encoding/json" +) + +// The MediaType type is a function, that validates +// whether the bytes represent data of given mediaType. +type MediaType func([]byte) error + +var mediaTypes = map[string]MediaType{ + "application/json": validateJSON, +} + +// Register registers MediaType object for given mediaType. +func Register(name string, mt MediaType) { + mediaTypes[name] = mt +} + +// Get returns MediaType object for given mediaType, if found. +func Get(name string) (MediaType, bool) { + mt, ok := mediaTypes[name] + return mt, ok +} + +func validateJSON(b []byte) error { + decoder := json.NewDecoder(bytes.NewReader(b)) + var v interface{} + return decoder.Decode(&v) +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/resource.go b/vendor/github.com/santhosh-tekuri/jsonschema/resource.go new file mode 100644 index 00000000000..9f52cf3acd9 --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/resource.go @@ -0,0 +1,236 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonschema + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/url" + "path/filepath" + "strconv" + "strings" +) + +type resource struct { + url string + doc interface{} + draft *Draft + schemas map[string]*Schema +} + +// DecodeJSON decodes json document from r. +// +// Note that number is decoded into json.Number instead of as a float64 +func DecodeJSON(r io.Reader) (interface{}, error) { + decoder := json.NewDecoder(r) + decoder.UseNumber() + var doc interface{} + if err := decoder.Decode(&doc); err != nil { + return nil, err + } + if t, _ := decoder.Token(); t != nil { + return nil, fmt.Errorf("invalid character %v after top-level value", t) + } + return doc, nil +} + +func newResource(base string, r io.Reader) (*resource, error) { + if strings.IndexByte(base, '#') != -1 { + panic(fmt.Sprintf("BUG: newResource(%q)", base)) + } + doc, err := DecodeJSON(r) + if err != nil { + return nil, fmt.Errorf("parsing %q failed. Reason: %v", base, err) + } + return &resource{ + url: base, + doc: doc, + schemas: make(map[string]*Schema)}, nil +} + +func resolveURL(base, ref string) (string, error) { + if ref == "" { + return base, nil + } + + refURL, err := url.Parse(ref) + if err != nil { + return "", err + } + if refURL.IsAbs() { + return normalize(ref), nil + } + + baseURL, err := url.Parse(base) + if err != nil { + return "", err + } + if baseURL.IsAbs() { + return normalize(baseURL.ResolveReference(refURL).String()), nil + } + + // filepath resolving + base, _ = split(base) + ref, fragment := split(ref) + if ref == "" { + return base + fragment, nil + } + dir, _ := filepath.Split(base) + return filepath.Join(dir, ref) + fragment, nil +} + +func (r *resource) resolvePtr(ptr string) (string, interface{}, error) { + if !strings.HasPrefix(ptr, "#/") { + panic(fmt.Sprintf("BUG: resolvePtr(%q)", ptr)) + } + base := r.url + p := strings.TrimPrefix(ptr, "#/") + doc := r.doc + for _, item := range strings.Split(p, "/") { + item = strings.Replace(item, "~1", "/", -1) + item = strings.Replace(item, "~0", "~", -1) + item, err := url.PathUnescape(item) + if err != nil { + return "", nil, errors.New("unable to url unscape: " + item) + } + switch d := doc.(type) { + case map[string]interface{}: + if id, ok := d[r.draft.id]; ok { + if id, ok := id.(string); ok { + if base, err = resolveURL(base, id); err != nil { + return "", nil, err + } + } + } + doc = d[item] + case []interface{}: + index, err := strconv.Atoi(item) + if err != nil { + return "", nil, fmt.Errorf("invalid $ref %q, reason: %s", ptr, err) + } + if index < 0 || index >= len(d) { + return "", nil, fmt.Errorf("invalid $ref %q, reason: array index outofrange", ptr) + } + doc = d[index] + default: + return "", nil, errors.New("invalid $ref " + ptr) + } + } + return base, doc, nil +} + +func split(uri string) (string, string) { + hash := strings.IndexByte(uri, '#') + if hash == -1 { + return uri, "#" + } + return uri[0:hash], uri[hash:] +} + +func normalize(url string) string { + base, fragment := split(url) + if rootFragment(fragment) { + fragment = "#" + } + return base + fragment +} + +func rootFragment(fragment string) bool { + return fragment == "" || fragment == "#" || fragment == "#/" +} + +func resolveIDs(draft *Draft, base string, v interface{}, ids map[string]map[string]interface{}) error { + m, ok := v.(map[string]interface{}) + if !ok { + return nil + } + if id, ok := m[draft.id]; ok { + b, err := resolveURL(base, id.(string)) + if err != nil { + return err + } + base = b + ids[base] = m + } + + for _, pname := range []string{"not", "additionalProperties"} { + if m, ok := m[pname]; ok { + if err := resolveIDs(draft, base, m, ids); err != nil { + return err + } + } + } + + for _, pname := range []string{"allOf", "anyOf", "oneOf"} { + if arr, ok := m[pname]; ok { + for _, m := range arr.([]interface{}) { + if err := resolveIDs(draft, base, m, ids); err != nil { + return err + } + } + } + } + + for _, pname := range []string{"definitions", "properties", "patternProperties", "dependencies"} { + if props, ok := m[pname]; ok { + for _, m := range props.(map[string]interface{}) { + if err := resolveIDs(draft, base, m, ids); err != nil { + return err + } + } + } + } + + if items, ok := m["items"]; ok { + switch items := items.(type) { + case map[string]interface{}: + if err := resolveIDs(draft, base, items, ids); err != nil { + return err + } + case []interface{}: + for _, item := range items { + if err := resolveIDs(draft, base, item, ids); err != nil { + return err + } + } + } + if additionalItems, ok := m["additionalItems"]; ok { + if additionalItems, ok := additionalItems.(map[string]interface{}); ok { + if err := resolveIDs(draft, base, additionalItems, ids); err != nil { + return err + } + } + } + } + + if draft.version >= 6 { + for _, pname := range []string{"propertyNames", "contains"} { + if m, ok := m[pname]; ok { + if err := resolveIDs(draft, base, m, ids); err != nil { + return err + } + } + } + } + + if draft.version >= 7 { + if iff, ok := m["if"]; ok { + if err := resolveIDs(draft, base, iff, ids); err != nil { + return err + } + for _, pname := range []string{"then", "else"} { + if m, ok := m[pname]; ok { + if err := resolveIDs(draft, base, m, ids); err != nil { + return err + } + } + } + } + } + + return nil +} diff --git a/vendor/github.com/santhosh-tekuri/jsonschema/schema.go b/vendor/github.com/santhosh-tekuri/jsonschema/schema.go new file mode 100644 index 00000000000..8ee1638547f --- /dev/null +++ b/vendor/github.com/santhosh-tekuri/jsonschema/schema.go @@ -0,0 +1,558 @@ +// Copyright 2017 Santhosh Kumar Tekuri. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jsonschema + +import ( + "encoding/json" + "fmt" + "io" + "math/big" + "net/url" + "regexp" + "strconv" + "strings" + "unicode/utf8" + + "github.com/santhosh-tekuri/jsonschema/decoders" + "github.com/santhosh-tekuri/jsonschema/formats" + "github.com/santhosh-tekuri/jsonschema/mediatypes" +) + +// A Schema represents compiled version of json-schema. +type Schema struct { + URL string // absolute url of the resource. + Ptr string // json-pointer to schema. always starts with `#`. + + // type agnostic validations + Always *bool // always pass/fail. used when booleans are used as schemas in draft-07. + Ref *Schema // reference to actual schema. if not nil, all the remaining fields are ignored. + Types []string // allowed types. + Constant []interface{} // first element in slice is constant value. note: slice is used to capture nil constant. + Enum []interface{} // allowed values. + enumError string // error message for enum fail. captured here to avoid constructing error message every time. + Not *Schema + AllOf []*Schema + AnyOf []*Schema + OneOf []*Schema + If *Schema + Then *Schema // nil, when If is nil. + Else *Schema // nil, when If is nil. + + // object validations + MinProperties int // -1 if not specified. + MaxProperties int // -1 if not specified. + Required []string // list of required properties. + Properties map[string]*Schema + PropertyNames *Schema + RegexProperties bool // property names must be valid regex. used only in draft4 as workaround in metaschema. + PatternProperties map[*regexp.Regexp]*Schema + AdditionalProperties interface{} // nil or false or *Schema. + Dependencies map[string]interface{} // value is *Schema or []string. + + // array validations + MinItems int // -1 if not specified. + MaxItems int // -1 if not specified. + UniqueItems bool + Items interface{} // nil or *Schema or []*Schema + AdditionalItems interface{} // nil or bool or *Schema. + Contains *Schema + + // string validations + MinLength int // -1 if not specified. + MaxLength int // -1 if not specified. + Pattern *regexp.Regexp + Format formats.Format + FormatName string + ContentEncoding string + Decoder decoders.Decoder + ContentMediaType string + MediaType mediatypes.MediaType + + // number validators + Minimum *big.Float + ExclusiveMinimum *big.Float + Maximum *big.Float + ExclusiveMaximum *big.Float + MultipleOf *big.Float + + // annotations. captured only when Compiler.ExtractAnnotations is true. + Title string + Description string + Default interface{} + ReadOnly bool + WriteOnly bool + Examples []interface{} +} + +// Compile parses json-schema at given url returns, if successful, +// a Schema object that can be used to match against json. +// +// The json-schema is validated with draft4 specification. +// Returned error can be *SchemaError +func Compile(url string) (*Schema, error) { + return NewCompiler().Compile(url) +} + +// MustCompile is like Compile but panics if the url cannot be compiled to *Schema. +// It simplifies safe initialization of global variables holding compiled Schemas. +func MustCompile(url string) *Schema { + return NewCompiler().MustCompile(url) +} + +// Validate validates the given json data, against the json-schema. +// +// Returned error can be *ValidationError. +func (s *Schema) Validate(r io.Reader) error { + doc, err := DecodeJSON(r) + if err != nil { + return err + } + return s.ValidateInterface(doc) +} + +// ValidateInterface validates given doc, against the json-schema. +// +// the doc must be the value decoded by json package using interface{} type. +// we recommend to use jsonschema.DecodeJSON(io.Reader) to decode JSON. +func (s *Schema) ValidateInterface(doc interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(InvalidJSONTypeError); ok { + err = r.(InvalidJSONTypeError) + } else { + panic(r) + } + } + }() + if err := s.validate(doc); err != nil { + finishSchemaContext(err, s) + finishInstanceContext(err) + return &ValidationError{ + Message: fmt.Sprintf("doesn't validate with %q", s.URL+s.Ptr), + InstancePtr: "#", + SchemaURL: s.URL, + SchemaPtr: s.Ptr, + Causes: []*ValidationError{err.(*ValidationError)}, + } + } + return nil +} + +// validate validates given value v with this schema. +func (s *Schema) validate(v interface{}) error { + if s.Always != nil { + if !*s.Always { + return validationError("", "always fail") + } + return nil + } + + if s.Ref != nil { + if err := s.Ref.validate(v); err != nil { + finishSchemaContext(err, s.Ref) + var refURL string + if s.URL == s.Ref.URL { + refURL = s.Ref.Ptr + } else { + refURL = s.Ref.URL + s.Ref.Ptr + } + return validationError("$ref", "doesn't validate with %q", refURL).add(err) + } + + // All other properties in a "$ref" object MUST be ignored + return nil + } + + if len(s.Types) > 0 { + vType := jsonType(v) + matched := false + for _, t := range s.Types { + if vType == t { + matched = true + break + } else if t == "integer" && vType == "number" { + if _, ok := new(big.Int).SetString(fmt.Sprint(v), 10); ok { + matched = true + break + } + } + } + if !matched { + return validationError("type", "expected %s, but got %s", strings.Join(s.Types, " or "), vType) + } + } + + if len(s.Constant) > 0 { + if !equals(v, s.Constant[0]) { + switch jsonType(s.Constant[0]) { + case "object", "array": + return validationError("const", "const failed") + default: + return validationError("const", "value must be %#v", s.Constant[0]) + } + } + } + + if len(s.Enum) > 0 { + matched := false + for _, item := range s.Enum { + if equals(v, item) { + matched = true + break + } + } + if !matched { + return validationError("enum", s.enumError) + } + } + + if s.Not != nil && s.Not.validate(v) == nil { + return validationError("not", "not failed") + } + + for i, sch := range s.AllOf { + if err := sch.validate(v); err != nil { + return validationError("allOf/"+strconv.Itoa(i), "allOf failed").add(err) + } + } + + if len(s.AnyOf) > 0 { + matched := false + var causes []error + for i, sch := range s.AnyOf { + if err := sch.validate(v); err == nil { + matched = true + break + } else { + causes = append(causes, addContext("", strconv.Itoa(i), err)) + } + } + if !matched { + return validationError("anyOf", "anyOf failed").add(causes...) + } + } + + if len(s.OneOf) > 0 { + matched := -1 + var causes []error + for i, sch := range s.OneOf { + if err := sch.validate(v); err == nil { + if matched == -1 { + matched = i + } else { + return validationError("oneOf", "valid against schemas at indexes %d and %d", matched, i) + } + } else { + causes = append(causes, addContext("", strconv.Itoa(i), err)) + } + } + if matched == -1 { + return validationError("oneOf", "oneOf failed").add(causes...) + } + } + + if s.If != nil { + if s.If.validate(v) == nil { + if s.Then != nil { + if err := s.Then.validate(v); err != nil { + return validationError("then", "if-then failed").add(err) + } + } + } else { + if s.Else != nil { + if err := s.Else.validate(v); err != nil { + return validationError("else", "if-else failed").add(err) + } + } + } + } + + switch v := v.(type) { + case map[string]interface{}: + if s.MinProperties != -1 && len(v) < s.MinProperties { + return validationError("minProperties", "minimum %d properties allowed, but found %d properties", s.MinProperties, len(v)) + } + if s.MaxProperties != -1 && len(v) > s.MaxProperties { + return validationError("maxProperties", "maximum %d properties allowed, but found %d properties", s.MaxProperties, len(v)) + } + if len(s.Required) > 0 { + var missing []string + for _, pname := range s.Required { + if _, ok := v[pname]; !ok { + missing = append(missing, strconv.Quote(pname)) + } + } + if len(missing) > 0 { + return validationError("required", "missing properties: %s", strings.Join(missing, ", ")) + } + } + + var additionalProps map[string]struct{} + if s.AdditionalProperties != nil { + additionalProps = make(map[string]struct{}, len(v)) + for pname := range v { + additionalProps[pname] = struct{}{} + } + } + + if len(s.Properties) > 0 { + for pname, pschema := range s.Properties { + if pvalue, ok := v[pname]; ok { + delete(additionalProps, pname) + if err := pschema.validate(pvalue); err != nil { + return addContext(escape(pname), "properties/"+escape(pname), err) + } + } + } + } + + if s.PropertyNames != nil { + for pname := range v { + if err := s.PropertyNames.validate(pname); err != nil { + return addContext(escape(pname), "propertyNames", err) + } + } + } + + if s.RegexProperties { + for pname := range v { + if !formats.IsRegex(pname) { + return validationError("", "patternProperty %q is not valid regex", pname) + } + } + } + for pattern, pschema := range s.PatternProperties { + for pname, pvalue := range v { + if pattern.MatchString(pname) { + delete(additionalProps, pname) + if err := pschema.validate(pvalue); err != nil { + return addContext(escape(pname), "patternProperties/"+escape(pattern.String()), err) + } + } + } + } + if s.AdditionalProperties != nil { + if _, ok := s.AdditionalProperties.(bool); ok { + if len(additionalProps) != 0 { + pnames := make([]string, 0, len(additionalProps)) + for pname := range additionalProps { + pnames = append(pnames, strconv.Quote(pname)) + } + return validationError("additionalProperties", "additionalProperties %s not allowed", strings.Join(pnames, ", ")) + } + } else { + schema := s.AdditionalProperties.(*Schema) + for pname := range additionalProps { + if pvalue, ok := v[pname]; ok { + if err := schema.validate(pvalue); err != nil { + return addContext(escape(pname), "additionalProperties", err) + } + } + } + } + } + for dname, dvalue := range s.Dependencies { + if _, ok := v[dname]; ok { + switch dvalue := dvalue.(type) { + case *Schema: + if err := dvalue.validate(v); err != nil { + return addContext("", "dependencies/"+escape(dname), err) + } + case []string: + for i, pname := range dvalue { + if _, ok := v[pname]; !ok { + return validationError("dependencies/"+escape(dname)+"/"+strconv.Itoa(i), "property %q is required, if %q property exists", pname, dname) + } + } + } + } + } + + case []interface{}: + if s.MinItems != -1 && len(v) < s.MinItems { + return validationError("minItems", "minimum %d items allowed, but found %d items", s.MinItems, len(v)) + } + if s.MaxItems != -1 && len(v) > s.MaxItems { + return validationError("maxItems", "maximum %d items allowed, but found %d items", s.MaxItems, len(v)) + } + if s.UniqueItems { + for i := 1; i < len(v); i++ { + for j := 0; j < i; j++ { + if equals(v[i], v[j]) { + return validationError("uniqueItems", "items at index %d and %d are equal", j, i) + } + } + } + } + switch items := s.Items.(type) { + case *Schema: + for i, item := range v { + if err := items.validate(item); err != nil { + return addContext(strconv.Itoa(i), "items", err) + } + } + case []*Schema: + if additionalItems, ok := s.AdditionalItems.(bool); ok { + if !additionalItems && len(v) > len(items) { + return validationError("additionalItems", "only %d items are allowed, but found %d items", len(items), len(v)) + } + } + for i, item := range v { + if i < len(items) { + if err := items[i].validate(item); err != nil { + return addContext(strconv.Itoa(i), "items/"+strconv.Itoa(i), err) + } + } else if sch, ok := s.AdditionalItems.(*Schema); ok { + if err := sch.validate(item); err != nil { + return addContext(strconv.Itoa(i), "additionalItems", err) + } + } else { + break + } + } + } + if s.Contains != nil { + matched := false + var causes []error + for i, item := range v { + if err := s.Contains.validate(item); err != nil { + causes = append(causes, addContext(strconv.Itoa(i), "", err)) + } else { + matched = true + break + } + } + if !matched { + return validationError("contains", "contains failed").add(causes...) + } + } + + case string: + if s.MinLength != -1 || s.MaxLength != -1 { + length := utf8.RuneCount([]byte(v)) + if s.MinLength != -1 && length < s.MinLength { + return validationError("minLength", "length must be >= %d, but got %d", s.MinLength, length) + } + if s.MaxLength != -1 && length > s.MaxLength { + return validationError("maxLength", "length must be <= %d, but got %d", s.MaxLength, length) + } + } + if s.Pattern != nil && !s.Pattern.MatchString(v) { + return validationError("pattern", "does not match pattern %q", s.Pattern) + } + if s.Format != nil && !s.Format(v) { + return validationError("format", "%q is not valid %q", v, s.FormatName) + } + + var content []byte + if s.Decoder != nil { + b, err := s.Decoder(v) + if err != nil { + return validationError("contentEncoding", "%q is not %s encoded", v, s.ContentEncoding) + } + content = b + } + if s.MediaType != nil { + if s.Decoder == nil { + content = []byte(v) + } + if err := s.MediaType(content); err != nil { + return validationError("contentMediaType", "value is not of mediatype %q", s.ContentMediaType) + } + } + + case json.Number, float64, int, int32, int64: + num, _ := new(big.Float).SetString(fmt.Sprint(v)) + if s.Minimum != nil && num.Cmp(s.Minimum) < 0 { + return validationError("minimum", "must be >= %v but found %v", s.Minimum, v) + } + if s.ExclusiveMinimum != nil && num.Cmp(s.ExclusiveMinimum) <= 0 { + return validationError("exclusiveMinimum", "must be > %v but found %v", s.ExclusiveMinimum, v) + } + if s.Maximum != nil && num.Cmp(s.Maximum) > 0 { + return validationError("maximum", "must be <= %v but found %v", s.Maximum, v) + } + if s.ExclusiveMaximum != nil && num.Cmp(s.ExclusiveMaximum) >= 0 { + return validationError("exclusiveMaximum", "must be < %v but found %v", s.ExclusiveMaximum, v) + } + if s.MultipleOf != nil { + if q := new(big.Float).Quo(num, s.MultipleOf); !q.IsInt() { + return validationError("multipleOf", "%v not multipleOf %v", v, s.MultipleOf) + } + } + } + + return nil +} + +// jsonType returns the json type of given value v. +// +// It panics if the given value is not valid json value +func jsonType(v interface{}) string { + switch v.(type) { + case nil: + return "null" + case bool: + return "boolean" + case json.Number, float64, int, int32, int64: + return "number" + case string: + return "string" + case []interface{}: + return "array" + case map[string]interface{}: + return "object" + } + panic(InvalidJSONTypeError(fmt.Sprintf("%T", v))) +} + +// equals tells if given two json values are equal or not. +func equals(v1, v2 interface{}) bool { + v1Type := jsonType(v1) + if v1Type != jsonType(v2) { + return false + } + switch v1Type { + case "array": + arr1, arr2 := v1.([]interface{}), v2.([]interface{}) + if len(arr1) != len(arr2) { + return false + } + for i := range arr1 { + if !equals(arr1[i], arr2[i]) { + return false + } + } + return true + case "object": + obj1, obj2 := v1.(map[string]interface{}), v2.(map[string]interface{}) + if len(obj1) != len(obj2) { + return false + } + for k, v1 := range obj1 { + if v2, ok := obj2[k]; ok { + if !equals(v1, v2) { + return false + } + } else { + return false + } + } + return true + case "number": + num1, _ := new(big.Float).SetString(string(v1.(json.Number))) + num2, _ := new(big.Float).SetString(string(v2.(json.Number))) + return num1.Cmp(num2) == 0 + default: + return v1 == v2 + } +} + +// escape converts given token to valid json-pointer token +func escape(token string) string { + token = strings.Replace(token, "~", "~0", -1) + token = strings.Replace(token, "/", "~1", -1) + return url.PathEscape(token) +} diff --git a/vendor/github.com/stretchr/testify/suite/doc.go b/vendor/github.com/stretchr/testify/suite/doc.go new file mode 100644 index 00000000000..f91a245d3f8 --- /dev/null +++ b/vendor/github.com/stretchr/testify/suite/doc.go @@ -0,0 +1,65 @@ +// Package suite contains logic for creating testing suite structs +// and running the methods on those structs as tests. The most useful +// piece of this package is that you can create setup/teardown methods +// on your testing suites, which will run before/after the whole suite +// or individual tests (depending on which interface(s) you +// implement). +// +// A testing suite is usually built by first extending the built-in +// suite functionality from suite.Suite in testify. Alternatively, +// you could reproduce that logic on your own if you wanted (you +// just need to implement the TestingSuite interface from +// suite/interfaces.go). +// +// After that, you can implement any of the interfaces in +// suite/interfaces.go to add setup/teardown functionality to your +// suite, and add any methods that start with "Test" to add tests. +// Methods that do not match any suite interfaces and do not begin +// with "Test" will not be run by testify, and can safely be used as +// helper methods. +// +// Once you've built your testing suite, you need to run the suite +// (using suite.Run from testify) inside any function that matches the +// identity that "go test" is already looking for (i.e. +// func(*testing.T)). +// +// Regular expression to select test suites specified command-line +// argument "-run". Regular expression to select the methods +// of test suites specified command-line argument "-m". +// Suite object has assertion methods. +// +// A crude example: +// // Basic imports +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/suite" +// ) +// +// // Define the suite, and absorb the built-in basic suite +// // functionality from testify - including a T() method which +// // returns the current testing context +// type ExampleTestSuite struct { +// suite.Suite +// VariableThatShouldStartAtFive int +// } +// +// // Make sure that VariableThatShouldStartAtFive is set to five +// // before each test +// func (suite *ExampleTestSuite) SetupTest() { +// suite.VariableThatShouldStartAtFive = 5 +// } +// +// // All methods that begin with "Test" are run as tests within a +// // suite. +// func (suite *ExampleTestSuite) TestExample() { +// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) +// suite.Equal(5, suite.VariableThatShouldStartAtFive) +// } +// +// // In order for 'go test' to run this suite, we need to create +// // a normal test function and pass our suite to suite.Run +// func TestExampleTestSuite(t *testing.T) { +// suite.Run(t, new(ExampleTestSuite)) +// } +package suite diff --git a/vendor/github.com/stretchr/testify/suite/interfaces.go b/vendor/github.com/stretchr/testify/suite/interfaces.go new file mode 100644 index 00000000000..b37cb040987 --- /dev/null +++ b/vendor/github.com/stretchr/testify/suite/interfaces.go @@ -0,0 +1,46 @@ +package suite + +import "testing" + +// TestingSuite can store and return the current *testing.T context +// generated by 'go test'. +type TestingSuite interface { + T() *testing.T + SetT(*testing.T) +} + +// SetupAllSuite has a SetupSuite method, which will run before the +// tests in the suite are run. +type SetupAllSuite interface { + SetupSuite() +} + +// SetupTestSuite has a SetupTest method, which will run before each +// test in the suite. +type SetupTestSuite interface { + SetupTest() +} + +// TearDownAllSuite has a TearDownSuite method, which will run after +// all the tests in the suite have been run. +type TearDownAllSuite interface { + TearDownSuite() +} + +// TearDownTestSuite has a TearDownTest method, which will run after +// each test in the suite. +type TearDownTestSuite interface { + TearDownTest() +} + +// BeforeTest has a function to be executed right before the test +// starts and receives the suite and test names as input +type BeforeTest interface { + BeforeTest(suiteName, testName string) +} + +// AfterTest has a function to be executed right after the test +// finishes and receives the suite and test names as input +type AfterTest interface { + AfterTest(suiteName, testName string) +} diff --git a/vendor/github.com/stretchr/testify/suite/suite.go b/vendor/github.com/stretchr/testify/suite/suite.go new file mode 100644 index 00000000000..d708d7d7539 --- /dev/null +++ b/vendor/github.com/stretchr/testify/suite/suite.go @@ -0,0 +1,166 @@ +package suite + +import ( + "flag" + "fmt" + "os" + "reflect" + "regexp" + "runtime/debug" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var allTestsFilter = func(_, _ string) (bool, error) { return true, nil } +var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run") + +// Suite is a basic testing suite with methods for storing and +// retrieving the current *testing.T context. +type Suite struct { + *assert.Assertions + require *require.Assertions + t *testing.T +} + +// T retrieves the current *testing.T context. +func (suite *Suite) T() *testing.T { + return suite.t +} + +// SetT sets the current *testing.T context. +func (suite *Suite) SetT(t *testing.T) { + suite.t = t + suite.Assertions = assert.New(t) + suite.require = require.New(t) +} + +// Require returns a require context for suite. +func (suite *Suite) Require() *require.Assertions { + if suite.require == nil { + suite.require = require.New(suite.T()) + } + return suite.require +} + +// Assert returns an assert context for suite. Normally, you can call +// `suite.NoError(expected, actual)`, but for situations where the embedded +// methods are overridden (for example, you might want to override +// assert.Assertions with require.Assertions), this method is provided so you +// can call `suite.Assert().NoError()`. +func (suite *Suite) Assert() *assert.Assertions { + if suite.Assertions == nil { + suite.Assertions = assert.New(suite.T()) + } + return suite.Assertions +} + +func failOnPanic(t *testing.T) { + r := recover() + if r != nil { + t.Errorf("test panicked: %v\n%s", r, debug.Stack()) + t.FailNow() + } +} + +// Run provides suite functionality around golang subtests. It should be +// called in place of t.Run(name, func(t *testing.T)) in test suite code. +// The passed-in func will be executed as a subtest with a fresh instance of t. +// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName. +func (suite *Suite) Run(name string, subtest func()) bool { + oldT := suite.T() + defer suite.SetT(oldT) + return oldT.Run(name, func(t *testing.T) { + suite.SetT(t) + subtest() + }) +} + +// Run takes a testing suite and runs all of the tests attached +// to it. +func Run(t *testing.T, suite TestingSuite) { + suite.SetT(t) + defer failOnPanic(t) + + suiteSetupDone := false + + methodFinder := reflect.TypeOf(suite) + tests := []testing.InternalTest{} + for index := 0; index < methodFinder.NumMethod(); index++ { + method := methodFinder.Method(index) + ok, err := methodFilter(method.Name) + if err != nil { + fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err) + os.Exit(1) + } + if !ok { + continue + } + if !suiteSetupDone { + if setupAllSuite, ok := suite.(SetupAllSuite); ok { + setupAllSuite.SetupSuite() + } + defer func() { + if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok { + tearDownAllSuite.TearDownSuite() + } + }() + suiteSetupDone = true + } + test := testing.InternalTest{ + Name: method.Name, + F: func(t *testing.T) { + parentT := suite.T() + suite.SetT(t) + defer failOnPanic(t) + + if setupTestSuite, ok := suite.(SetupTestSuite); ok { + setupTestSuite.SetupTest() + } + if beforeTestSuite, ok := suite.(BeforeTest); ok { + beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name) + } + defer func() { + if afterTestSuite, ok := suite.(AfterTest); ok { + afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name) + } + if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok { + tearDownTestSuite.TearDownTest() + } + suite.SetT(parentT) + }() + method.Func.Call([]reflect.Value{reflect.ValueOf(suite)}) + }, + } + tests = append(tests, test) + } + runTests(t, tests) +} + +func runTests(t testing.TB, tests []testing.InternalTest) { + r, ok := t.(runner) + if !ok { // backwards compatibility with Go 1.6 and below + if !testing.RunTests(allTestsFilter, tests) { + t.Fail() + } + return + } + + for _, test := range tests { + r.Run(test.Name, test.F) + } +} + +// Filtering method according to set regular expression +// specified command-line argument -m +func methodFilter(name string) (bool, error) { + if ok, _ := regexp.MatchString("^Test", name); !ok { + return false, nil + } + return regexp.MatchString(*matchMethod, name) +} + +type runner interface { + Run(name string, f func(t *testing.T)) bool +} diff --git a/vendor/go.elastic.co/apm/.dockerignore b/vendor/go.elastic.co/apm/.dockerignore new file mode 100644 index 00000000000..25e074c2513 --- /dev/null +++ b/vendor/go.elastic.co/apm/.dockerignore @@ -0,0 +1,2 @@ +scripts/docker* +scripts/Docker* diff --git a/vendor/go.elastic.co/apm/.gitignore b/vendor/go.elastic.co/apm/.gitignore new file mode 100644 index 00000000000..d5a32627a04 --- /dev/null +++ b/vendor/go.elastic.co/apm/.gitignore @@ -0,0 +1,5 @@ +*.swp +*.test +*.out +docs/html +build diff --git a/vendor/go.elastic.co/apm/.jenkins-edge.yml b/vendor/go.elastic.co/apm/.jenkins-edge.yml new file mode 100644 index 00000000000..3426e876db2 --- /dev/null +++ b/vendor/go.elastic.co/apm/.jenkins-edge.yml @@ -0,0 +1,2 @@ +GO_VERSION: + - "master" diff --git a/vendor/go.elastic.co/apm/.jenkins.yml b/vendor/go.elastic.co/apm/.jenkins.yml new file mode 100644 index 00000000000..e41a8a5e942 --- /dev/null +++ b/vendor/go.elastic.co/apm/.jenkins.yml @@ -0,0 +1,8 @@ +GO_VERSION: + - stable + - "1.x" + - "1.8.x" + - "1.9.x" + - "1.10.x" + - "1.11.x" + - "1.12.x" diff --git a/vendor/go.elastic.co/apm/CHANGELOG.asciidoc b/vendor/go.elastic.co/apm/CHANGELOG.asciidoc new file mode 100644 index 00000000000..f6cd5aa4be5 --- /dev/null +++ b/vendor/go.elastic.co/apm/CHANGELOG.asciidoc @@ -0,0 +1,262 @@ +ifdef::env-github[] +NOTE: Release notes are best read in our documentation at +https://www.elastic.co/guide/en/apm/agent/go/current/release-notes.html[elastic.co] +endif::[] + +//// +[[release-notes-x.x.x]] +==== x.x.x - YYYY/MM/DD + +[float] +===== Breaking changes + +[float] +===== Features +* Cool new feature: {pull}2526[#2526] + +[float] +===== Bug fixes +//// + +[[unreleased]] +=== Unreleased + +https://github.com/elastic/apm-agent-go/compare/v1.7.2...master[View commits] + +[[release-notes-1.x]] +=== Go Agent version 1.x + +[[release-notes-1.7.2]] +==== 1.7.2 - 2020/03/19 + +- Update cucumber/godog to 0.8.1 {pull}733[(#733)] + +[[release-notes-1.7.1]] +==== 1.7.1 - 2020/03/05 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.7.1[View release] + +- Fix segfault on 32-bit architectures {pull}728[(#728)] + +[[release-notes-1.7.0]] +==== 1.7.0 - 2020/01/10 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.7.0[View release] + + - Add span.context.destination.* {pull}664[(#664)] + - transport: fix Content-Type for pprof data {pull}679[(#679)] + - Add "tracestate" propagation {pull}690[(#690)] + - Add support for API Key auth {pull}698[(#698)] + - module/apmsql: report rows affected {pull}700[(#700)] + +[[release-notes-1.6.0]] +==== 1.6.0 - 2019/11/17 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.6.0[View release] + + - module/apmhttp: add WithClientRequestName option {pull}609[(#609)] + - module/apmhttp: add WithPanicPropagation function {pull}611[(#611)] + - module/apmgoredis: add Client.RedisClient {pull}613[(#613)] + - Introduce apm.TraceFormatter, for formatting trace IDs {pull}635[(#635)] + - Report error cause(s), add support for errors.Unwrap {pull}638[(#638)] + - Setting `ELASTIC_APM_TRANSACTION_MAX_SPANS` to 0 now disables all spans {pull}640[(#640)] + - module/apmzerolog: add Writer.MinLevel {pull}641[(#641)] + - Introduce SetLabel and deprecate SetTag {pull}642[(#642)] + - Support central config for `ELASTIC_APM_CAPTURE_BODY` and `ELASTIC_APM_TRANSACTION_MAX_SPANS` {pull}648[(#648)] + - module/apmgorm: sql.ErrNoRows is no longer reported as an error {pull}645[(#645)] + - Server URL path is cleaned/canonicalizsed in order to avoid 301 redirects {pull}658[(#658)] + - `context.request.socket.remote_address` now reports the peer address {pull}662[(#662)] + - Experimental support for periodic CPU/heap profiling {pull}666[(#666)] + - module/apmnegroni: introduce tracing Negroni middleware {pull}671[(#671)] + - Unescape hyphens in k8s pod UIDs when the systemd cgroup driver is used {pull}672[(#672)] + - Read and propagate the standard W3C "traceparent" header {pull}674[(#674)] + +[[release-notes-1.5.0]] +==== 1.5.0 - 2019/07/31 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.5.0[View release] + + - Add Context.SetCustom {pull}581[(#581)] + - Add support for extracting UUID-like container IDs {pull}577[(#577)] + - Introduce transaction/span breakdown metrics {pull}564[(#564)] + - Optimised HTTP request body capture {pull}592[(#592)] + - Fixed transaction encoding to drop tags (and other context) for non-sampled transactions {pull}593[(#593)] + - Introduce central config polling {pull}591[(#591)] + - Fixed apmgrpc client interceptor, propagating trace context for non-sampled transactions {pull}602[(#602)] + +[[release-notes-1.4.0]] +==== 1.4.0 - 2019/06/20 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.4.0[View release] + + - Update opentracing-go dependency to v1.1.0 + - Update HTTP routers to return " unknown route" if route cannot be matched {pull}486[(#486)] + - module/apmchi: introduce instrumentation for go-chi/chi router {pull}495[(#495)] + - module/apmgoredis: introduce instrumentation for the go-redis/redis client {pull}505[(#505)] + - module/apmsql: exposed the QuerySignature function {pull}515[(#515)] + - module/apmgopg: introduce instrumentation for the go-pg/pg ORM {pull}516[(#516)] + - module/apmmongo: set minimum Go version to Go 1.10 {pull}522[(#522)] + - internal/sqlscanner: bug fix for multi-byte rune handling {pull}535[(#535)] + - module/apmgrpc: added WithServerRequestIgnorer server option {pull}531[(#531)] + - Introduce `ELASTIC_APM_GLOBAL_LABELS` config {pull}539[(#539)] + - module/apmgorm: register `row_query` callbacks {pull}532[(#532)] + - Introduce `ELASTIC_APM_STACK_TRACE_LIMIT` config {pull}559[(#559)] + - Include agent name/version and Go version in User-Agent {pull}560[(#560)] + - Truncate `error.culprit` at 1024 chars {pull}561[(#561)] + +[[release-notes-1.3.0]] +==== 1.3.0 - 2019/03/20 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.3.0[View release] + + - Rename "metricset.labels" to "metricset.tags" {pull}438[(#438)] + - Introduce `ELASTIC_APM_DISABLE_METRICS` to disable metrics with matching names {pull}439[(#439)] + - module/apmelasticsearch: introduce instrumentation for Elasticsearch clients {pull}445[(#445)] + - module/apmmongo: introduce instrumentation for the MongoDB Go Driver {pull}452[(#452)] + - Introduce ErrorDetailer interface {pull}453[(#453)] + - module/apmhttp: add CloseIdleConnectons and CancelRequest to RoundTripper {pull}457[(#457)] + - Allow specifying transaction (span) ID via TransactionOptions/SpanOptions {pull}463[(#463)] + - module/apmzerolog: introduce zerolog log correlation and exception-tracking writer {pull}428[(#428)] + - module/apmelasticsearch: capture body for \_msearch, template and rollup search {pull}470[(#470)] + - Ended Transactions/Spans may now be used as parents {pull}478[(#478)] + - Introduce apm.DetachedContext for async/fire-and-forget trace propagation {pull}481[(#481)] + - module/apmechov4: add a copy of apmecho supporting echo/v4 {pull}477[(#477)] + +[[release-notes-1.2.0]] +==== 1.2.0 - 2019/01/17 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.2.0[View release] + + - Add "transaction.sampled" to errors {pull}410[(#410)] + - Enforce license header in source files with go-licenser {pull}411[(#411)] + - module/apmot: ignore "follows-from" span references {pull}414[(#414)] + - module/apmot: report error log records {pull}415[(#415)] + - Introduce `ELASTIC_APM_CAPTURE_HEADERS` to control HTTP header capture {pull}418[(#418)] + - module/apmzap: introduce zap log correlation and exception-tracking hook {pull}426[(#426)] + - type Error implements error interface {pull}399[(#399)] + - Add "transaction.type" to errors {pull}433[(#433)] + - Added instrumentation-specific Go modules (i.e. one for each package under apm/module) {pull}405[(#405)] + +[[release-notes-1.1.3]] +==== 1.1.3 - 2019/01/06 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.1.3[View release] + + - Remove the `agent.*` metrics {pull}407[(#407)] + - Add support for new github.com/pkg/errors.Frame type {pull}409[(#409)] + +[[release-notes-1.1.2]] +==== 1.1.2 - 2019/01/03 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.1.2[View release] + + - Fix data race between Tracer.Active and Tracer.loop {pull}406[(#406)] + +[[release-notes-1.1.1]] +==== 1.1.1 - 2018/12/13 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.1.1[View release] + + - CPU% metrics are now correctly in the range [0,1] + +[[release-notes-1.1.0]] +==== 1.1.0 - 2018/12/12 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.1.0[View release] + + - Stop pooling Transaction/Span/Error, introduce internal pooled objects {pull}319[(#319)] + - Enable metrics collection with default interval of 30s {pull}322[(#322)] + - `ELASTIC_APM_SERVER_CERT` enables server certificate pinning {pull}325[(#325)] + - Add Docker container ID to metadata {pull}330[(#330)] + - Added distributed trace context propagation to apmgrpc {pull}335[(#335)] + - Introduce `Span.Subtype`, `Span.Action` {pull}332[(#332)] + - apm.StartSpanOptions fixed to stop ignoring options {pull}326[(#326)] + - Add Kubernetes pod info to metadata {pull}342[(#342)] + - module/apmsql: don't report driver.ErrBadConn, context.Canceled (#346, #348) + - Added ErrorLogRecord.Error field, for associating an error value with a log record {pull}380[(#380)] + - module/apmlogrus: introduce logrus exception-tracking hook, and log correlation {pull}381[(#381)] + - module/apmbeego: introduce Beego instrumentation module {pull}386[(#386)] + - module/apmhttp: report status code for client spans {pull}388[(#388)] + +[[release-notes-1.0.0]] +==== 1.0.0 - 2018/11/14 + +https://github.com/elastic/apm-agent-go/releases/tag/v1.0.0[View release] + + - Implement v2 intake protocol {pull}180[(#180)] + - Unexport Transaction.Timestamp and Span.Timestamp {pull}207[(#207)] + - Add jitter (+/-10%) to backoff on transport error {pull}212[(#212)] + - Add support for span tags {pull}213[(#213)] + - Require units for size configuration {pull}223[(#223)] + - Require units for duration configuration {pull}211[(#211)] + - Add support for multiple server URLs with failover {pull}233[(#233)] + - Add support for mixing OpenTracing spans with native transactions/spans {pull}235[(#235)] + - Drop SetHTTPResponseHeadersSent and SetHTTPResponseFinished methods from Context {pull}238[(#238)] + - Stop setting custom context (gin.handler) in apmgin {pull}238[(#238)] + - Set response context in errors reported by web modules {pull}238[(#238)] + - module/apmredigo: introduce gomodule/redigo instrumentation {pull}248[(#248)] + - Update Sampler interface to take TraceContext {pull}243[(#243)] + - Truncate SQL statements to a maximum of 10000 chars, all other strings to 1024 (#244, #276) + - Add leading slash to URLs in transaction/span context {pull}250[(#250)] + - Add `Transaction.Context` method for setting framework {pull}252[(#252)] + - Timestamps are now reported as usec since epoch, spans no longer use "start" offset {pull}257[(#257)] + - `ELASTIC_APM_SANITIZE_FIELD_NAMES` and `ELASTIC_APM_IGNORE_URLS` now use wildcard matching {pull}260[(#260)] + - Changed top-level package name to "apm", and canonical import path to "go.elastic.co/apm" {pull}202[(#202)] + - module/apmrestful: introduce emicklei/go-restful instrumentation {pull}270[(#270)] + - Fix panic handling in web instrumentations {pull}273[(#273)] + - Migrate internal/fastjson to go.elastic.co/fastjson {pull}275[(#275)] + - Report all HTTP request/response headers {pull}280[(#280)] + - Drop Context.SetCustom {pull}284[(#284)] + - Reuse memory for tags {pull}286[(#286)] + - Return a more helpful error message when /intake/v2/events 404s, to detect old servers {pull}290[(#290)] + - Implement test service for w3c/distributed-tracing test harness {pull}293[(#293)] + - End HTTP client spans on response body closure {pull}289[(#289)] + - module/apmgrpc requires Go 1.9+ {pull}300[(#300)] + - Invalid tag key characters are replaced with underscores {pull}308[(#308)] + - `ELASTIC_APM_LOG_FILE` and `ELASTIC_APM_LOG_LEVEL` introduced {pull}313[(#313)] + +[[release-notes-0.x]] +=== Go Agent version 0.x + +[[release-notes-0.5.2]] +==== 0.5.2 - 2018/09/19 + +https://github.com/elastic/apm-agent-go/releases/tag/v0.5.2[View release] + + - Fixed premature Span.End() in apmgorm callback, causing a data-race with captured errors {pull}229[(#229)] + +[[release-notes-0.5.1]] +==== 0.5.1 - 2018/09/05 + +https://github.com/elastic/apm-agent-go/releases/tag/v0.5.1[View release] + + - Fixed a bug causing error stacktraces and culprit to sometimes not be set {pull}204[(#204)] + +[[release-notes-0.5.0]] +==== 0.5.0 - 2018/08/27 + +https://github.com/elastic/apm-agent-go/releases/tag/v0.5.0[View release] + + - `ELASTIC_APM_SERVER_URL` now defaults to "http://localhost:8200" {pull}122[(#122)] + - `Transport.SetUserAgent` method added, enabling the User-Agent to be set programatically {pull}124[(#124)] + - Inlined functions are now properly reported in stacktraces {pull}127[(#127)] + - Support for the experimental metrics API added {pull}94[(#94)] + - module/apmsql: SQL is parsed to generate more useful span names {pull}129[(#129)] + - Basic vgo module added {pull}136[(#136)] + - module/apmhttprouter: added a wrapper type for `httprouter.Router` to simplify adding routes {pull}140[(#140)] + - Add `Transaction.Context` methods for setting user IDs {pull}144[(#144)] + - module/apmgocql: new instrumentation module, providing an observer for gocql {pull}148[(#148)] + - Add `ELASTIC_APM_SERVER_TIMEOUT` config {pull}157[(#157)] + - Add `ELASTIC_APM_IGNORE_URLS` config {pull}158[(#158)] + - module/apmsql: fix a bug preventing errors from being captured {pull}160[(#160)] + - Introduce `Tracer.StartTransactionOptions`, drop variadic args from `Tracer.StartTransaction` {pull}165[(#165)] + - module/apmgorm: introduce GORM instrumentation module (#169, #170) + - module/apmhttp: record outgoing request URLs in span context {pull}172[(#172)] + - module/apmot: introduce OpenTracing implementation {pull}173[(#173)] + +[[release-notes-0.4.0]] +==== 0.4.0 - 2018/06/17 + +https://github.com/elastic/apm-agent-go/releases/tag/v0.4.0[View release] + +First release of the Go agent for Elastic APM diff --git a/vendor/go.elastic.co/apm/CHANGELOG.md b/vendor/go.elastic.co/apm/CHANGELOG.md new file mode 100644 index 00000000000..e118c79191a --- /dev/null +++ b/vendor/go.elastic.co/apm/CHANGELOG.md @@ -0,0 +1 @@ +Release notes are now available in our documentation at ([elastic.co](https://www.elastic.co/guide/en/apm/agent/go/current/release-notes.html)) diff --git a/vendor/go.elastic.co/apm/CODE_OF_CONDUCT.md b/vendor/go.elastic.co/apm/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..c286a3152c4 --- /dev/null +++ b/vendor/go.elastic.co/apm/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +303 See Other + +Location: https://www.elastic.co/community/codeofconduct diff --git a/vendor/go.elastic.co/apm/CONTRIBUTING.md b/vendor/go.elastic.co/apm/CONTRIBUTING.md new file mode 100644 index 00000000000..cef6651e8db --- /dev/null +++ b/vendor/go.elastic.co/apm/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing to the Go APM Agent + +The Go APM Agent is open source and we love to receive contributions from our community — you! + +There are many ways to contribute, from writing tutorials or blog posts, improving the +documentation, submitting bug reports and feature requests or writing code. + +You can get in touch with us through [Discuss](https://discuss.elastic.co/c/apm). +Feedback and ideas are always welcome. + +## Code contributions + +If you have a bugfix or new feature that involves significant changes that you would like to +contribute, please find or open an issue to discuss the changes first. It may be that somebody +is already working on it, or that there are particular issues that you should know about before +implementing the change. + +For minor changes (e.g. fixing a typo), you can just send your changes. + +### Submitting your changes + +Generally, we require that you test any code you are adding or modifying. Once your changes are +ready to submit for review: + +1. Sign the Contributor License Agreement + + Please make sure you have signed our [Contributor License Agreement](https://www.elastic.co/contributor-agreement/). + We are not asking you to assign copyright to us, but to give us the right to distribute + your code without restriction. We ask this of all contributors in order to assure our + users of the origin and continuing existence of the code. You only need to sign the CLA once. + +2. Test your changes + + Run the test suite to make sure that nothing is broken. + See [testing](#testing) for details. + +3. Review your changes + + Before sending your changes for review, it pays to review it yourself first! + + If you're making significant changes, please familiarize yourself with [Effective Go](https://golang.org/doc/effective_go.html) + and [go/wiki/CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments). + These documents will walk you through writing idiomatic Go code, which we strive for. + + Here are a few things to check: + - format the code with [gofmt](https://golang.org/cmd/gofmt/) or [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) + - lint your code using [golint](https://github.com/golang/lint) + - check for common errors using [go vet](https://golang.org/cmd/vet/) + +4. Rebase your changes + + Update your local repository with the most recent code from the main repo, and rebase your + branch on top of the latest master branch. We prefer your initial changes to be squashed + into a single commit. Later, if we ask you to make changes, add them as separate commits. + This makes them easier to review. As a final step before merging we will either ask you to + squash all commits yourself or we'll do it for you. + +5. Submit a pull request + + Push your local changes to your forked copy of the repository and [submit a pull request](https://help.github.com/articles/using-pull-requests). + In the pull request, choose a title which sums up the changes that you have made, and in + the body provide more details about what your changes do, and the reason for making them. + Also mention the number of the issue where discussion has taken place, or issues that are + fixed/closed by the changes, e.g. "Closes #123". + +6. Be patient + + We might not be able to review your code as fast as we would like to, but we'll do our + best to dedicate it the attention it deserves. Your effort is much appreciated! + +### Testing + +The tests currently do not require any external resources, so just run `go test ./...`. +We test with all versions of Go from 1.8 onwards using [Travis CI](https://travis-ci.org). + +We track code coverage. 100% coverage is not a goal, but please do check that your tests +adequately cover the code using `go test -cover`. + +### Release procedure + +1. Update version.go and internal/apmversion/version.go, and then run "make update-modules" +1. Update [`CHANGELOG.asciidoc`](changelog.asciidoc), by adding a new version heading (`==== 1.x.x - yyyy/MM/dd`) and changing the base tag of the Unreleased comparison URL +1. For major and minor releases, update the EOL table in [`upgrading.asciidoc`](docs/upgrading.asciidoc). +1. Merge changes into github.com/elastic/apm-agent-go@master +1. Create tags: vN.N.N, and module/$MODULE/vN.N.N for each instrumentation module + + scripts/tagversion.sh + +1. Create release on GitHub + + hub release -d vN.N.N diff --git a/vendor/go.elastic.co/apm/Jenkinsfile b/vendor/go.elastic.co/apm/Jenkinsfile new file mode 100644 index 00000000000..07df4b18ce4 --- /dev/null +++ b/vendor/go.elastic.co/apm/Jenkinsfile @@ -0,0 +1,294 @@ +#!/usr/bin/env groovy + +@Library('apm@current') _ + +pipeline { + agent { label 'linux && immutable' } + environment { + REPO = 'apm-agent-go' + BASE_DIR = "src/go.elastic.co/apm" + NOTIFY_TO = credentials('notify-to') + JOB_GCS_BUCKET = credentials('gcs-bucket') + CODECOV_SECRET = 'secret/apm-team/ci/apm-agent-go-codecov' + GO111MODULE = 'on' + GOPATH = "${env.WORKSPACE}" + GOPROXY = 'https://proxy.golang.org' + HOME = "${env.WORKSPACE}" + GITHUB_CHECK_ITS_NAME = 'Integration Tests' + ITS_PIPELINE = 'apm-integration-tests-selector-mbp/master' + OPBEANS_REPO = 'opbeans-go' + } + options { + timeout(time: 1, unit: 'HOURS') + buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20', daysToKeepStr: '30')) + timestamps() + ansiColor('xterm') + disableResume() + durabilityHint('PERFORMANCE_OPTIMIZED') + rateLimitBuilds(throttle: [count: 60, durationName: 'hour', userBoost: true]) + quietPeriod(10) + } + triggers { + issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?tests(?:\\W+please)?.*') + } + parameters { + string(name: 'GO_VERSION', defaultValue: "1.12.7", description: "Go version to use.") + booleanParam(name: 'Run_As_Master_Branch', defaultValue: false, description: 'Allow to run any steps on a PR, some steps normally only run on master branch.') + booleanParam(name: 'test_ci', defaultValue: true, description: 'Enable test') + booleanParam(name: 'docker_test_ci', defaultValue: true, description: 'Enable run docker tests') + booleanParam(name: 'bench_ci', defaultValue: true, description: 'Enable benchmarks') + } + stages { + stage('Initializing'){ + options { skipDefaultCheckout() } + environment { + GO_VERSION = "${params.GO_VERSION}" + PATH = "${env.PATH}:${env.WORKSPACE}/bin" + } + stages { + /** + Checkout the code and stash it, to use it on other stages. + */ + stage('Checkout') { + options { skipDefaultCheckout() } + steps { + pipelineManager([ cancelPreviousRunningBuilds: [ when: 'PR' ] ]) + deleteDir() + gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: true, reference: '/var/lib/jenkins/.git-references/apm-agent-go.git') + stash allowEmpty: true, name: 'source', useDefaultExcludes: false + } + } + /** + Execute unit tests. + */ + stage('Tests') { + options { skipDefaultCheckout() } + when { + expression { return params.test_ci } + } + steps { + withGithubNotify(context: 'Tests', tab: 'tests') { + deleteDir() + unstash 'source' + dir("${BASE_DIR}"){ + script { + def go = readYaml(file: '.jenkins.yml') + def parallelTasks = [:] + go['GO_VERSION'].each{ version -> + parallelTasks["Go-${version}"] = generateStep(version) + } + // For the cutting edge + def edge = readYaml(file: '.jenkins-edge.yml') + edge['GO_VERSION'].each{ version -> + parallelTasks["Go-${version}"] = generateStepAndCatchError(version) + } + parallel(parallelTasks) + } + } + } + } + } + stage('Coverage') { + options { skipDefaultCheckout() } + when { + expression { return params.docker_test_ci } + } + steps { + withGithubNotify(context: 'Coverage') { + deleteDir() + unstash 'source' + dir("${BASE_DIR}"){ + sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies' + sh script: './scripts/jenkins/docker-test.sh', label: 'Docker tests' + } + } + } + post { + always { + coverageReport("${BASE_DIR}/build/coverage") + codecov(repo: env.REPO, basedir: "${BASE_DIR}", + flags: "-f build/coverage/coverage.cov -X search", + secret: "${CODECOV_SECRET}") + junit(allowEmptyResults: true, + keepLongStdio: true, + testResults: "${BASE_DIR}/build/junit-*.xml") + } + } + } + stage('Benchmark') { + agent { label 'linux && immutable' } + options { skipDefaultCheckout() } + when { + beforeAgent true + allOf { + anyOf { + branch 'master' + tag pattern: 'v\\d+\\.\\d+\\.\\d+.*', comparator: 'REGEXP' + expression { return params.Run_As_Master_Branch } + } + expression { return params.bench_ci } + } + } + steps { + withGithubNotify(context: 'Benchmark', tab: 'tests') { + deleteDir() + unstash 'source' + dir("${BASE_DIR}"){ + sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies' + sh script: './scripts/jenkins/bench.sh', label: 'Benchmarking' + sendBenchmarks(file: 'build/bench.out', index: 'benchmark-go') + } + } + } + } + } + } + stage('More OS') { + parallel { + stage('Windows') { + agent { label 'windows-2019-immutable' } + options { skipDefaultCheckout() } + environment { + GOROOT = "c:\\Go" + GOPATH = "${env.WORKSPACE}" + PATH = "${env.PATH};${env.GOROOT}\\bin;${env.GOPATH}\\bin" + GO_VERSION = "${params.GO_VERSION}" + } + steps { + withGithubNotify(context: 'Build-Test - Windows') { + cleanDir("${WORKSPACE}/${BASE_DIR}") + unstash 'source' + dir("${BASE_DIR}"){ + bat script: 'scripts/jenkins/windows/install-tools.bat', label: 'Install tools' + bat script: 'scripts/jenkins/windows/build-test.bat', label: 'Build and test' + } + } + } + post { + always { + junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/build/junit-*.xml") + } + } + } + stage('OSX') { + agent { label 'macosx' } + options { skipDefaultCheckout() } + environment { + GO_VERSION = "${params.GO_VERSION}" + PATH = "${env.PATH}:${env.WORKSPACE}/bin" + } + steps { + withGithubNotify(context: 'Build-Test - OSX') { + deleteDir() + unstash 'source' + dir("${BASE_DIR}"){ + sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies' + sh script: './scripts/jenkins/build-test.sh', label: 'Build and test' + } + } + } + post { + always { + junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/build/junit-*.xml") + deleteDir() + } + } + } + } + } + stage('Integration Tests') { + agent none + when { + beforeAgent true + allOf { + anyOf { + environment name: 'GIT_BUILD_CAUSE', value: 'pr' + expression { return !params.Run_As_Master_Branch } + } + } + } + steps { + build(job: env.ITS_PIPELINE, propagate: false, wait: false, + parameters: [string(name: 'INTEGRATION_TEST', value: 'Go'), + string(name: 'BUILD_OPTS', value: "--go-agent-version ${env.GIT_BASE_COMMIT} --opbeans-go-agent-branch ${env.GIT_BASE_COMMIT}"), + string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_ITS_NAME), + string(name: 'GITHUB_CHECK_REPO', value: env.REPO), + string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT)]) + githubNotify(context: "${env.GITHUB_CHECK_ITS_NAME}", description: "${env.GITHUB_CHECK_ITS_NAME} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${env.ITS_PIPELINE.replaceAll('/','+')}") + } + } + stage('Release') { + options { skipDefaultCheckout() } + when { + beforeAgent true + tag pattern: 'v\\d+\\.\\d+\\.\\d+', comparator: 'REGEXP' + } + stages { + stage('Opbeans') { + environment { + REPO_NAME = "${OPBEANS_REPO}" + GO_VERSION = "${params.GO_VERSION}" + } + steps { + deleteDir() + dir("${OPBEANS_REPO}"){ + git credentialsId: 'f6c7695a-671e-4f4f-a331-acdce44ff9ba', + url: "git@github.com:elastic/${OPBEANS_REPO}.git" + sh script: ".ci/bump-version.sh ${env.BRANCH_NAME}", label: 'Bump version' + // The opbeans-go pipeline will trigger a release for the master branch + gitPush() + // The opbeans-go pipeline will trigger a release for the release tag + gitCreateTag(tag: "${env.BRANCH_NAME}") + } + } + } + } + } + } + post { + cleanup { + notifyBuildResult() + } + } +} + +def generateStep(version){ + return { + node('linux && immutable'){ + try { + deleteDir() + unstash 'source' + echo "${version}" + dir("${BASE_DIR}"){ + withEnv(["GO_VERSION=${version}"]) { + // Another retry in case there are any environmental issues + // See https://issuetracker.google.com/issues/146072599 for more context + retry(2) { + sleep randomNumber(min: 2, max: 5) + sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies' + } + sh script: './scripts/jenkins/build-test.sh', label: 'Build and test' + } + } + } catch(e){ + error(e.toString()) + } finally { + junit(allowEmptyResults: true, + keepLongStdio: true, + testResults: "${BASE_DIR}/build/junit-*.xml") + } + } + } +} + +def generateStepAndCatchError(version){ + return { + catchError(buildResult: 'SUCCESS', message: 'Cutting Edge Tests', stageResult: 'UNSTABLE') { + generateStep(version) + } + } +} + +def cleanDir(path){ + powershell label: "Clean ${path}", script: "Remove-Item -Recurse -Force ${path}" +} diff --git a/vendor/go.elastic.co/apm/LICENSE b/vendor/go.elastic.co/apm/LICENSE new file mode 100644 index 00000000000..b1a731fb5a3 --- /dev/null +++ b/vendor/go.elastic.co/apm/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Elasticsearch BV + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/go.elastic.co/apm/Makefile b/vendor/go.elastic.co/apm/Makefile new file mode 100644 index 00000000000..572cd2647fc --- /dev/null +++ b/vendor/go.elastic.co/apm/Makefile @@ -0,0 +1,81 @@ +TEST_TIMEOUT?=5m +GO_LICENSER_EXCLUDE=stacktrace/testdata + +.PHONY: check +check: precheck check-modules test + +.PHONY: precheck +precheck: check-goimports check-lint check-vet check-dockerfile-testing check-licenses + +.PHONY: check-goimports +.PHONY: check-dockerfile-testing +.PHONY: check-lint +.PHONY: check-licenses +.PHONY: check-modules +ifeq ($(shell go run ./scripts/mingoversion.go -print 1.12),true) +check-goimports: + sh scripts/check_goimports.sh + +check-dockerfile-testing: + go run ./scripts/gendockerfile.go -d + +check-lint: + sh scripts/check_lint.sh + +check-licenses: + go-licenser -d $(patsubst %,-exclude %,$(GO_LICENSER_EXCLUDE)) . + +check-modules: + go run scripts/genmod/main.go -check . +else +check-goimports: +check-dockerfile-testing: +check-lint: +check-licenses: +check-modules: +endif + +.PHONY: check-vet +check-vet: + @for dir in $(shell scripts/moduledirs.sh); do (cd $$dir && go vet ./...) || exit $$?; done + +.PHONY: install +install: + go get -v -t ./... + +.PHONY: docker-test +docker-test: + scripts/docker-compose-testing run -T --rm go-agent-tests make test + +.PHONY: test +test: + @for dir in $(shell scripts/moduledirs.sh); do (cd $$dir && go test -v -timeout=$(TEST_TIMEOUT) ./...) || exit $$?; done + +.PHONY: coverage +coverage: + @bash scripts/test_coverage.sh + +.PHONY: fmt +fmt: + @GOIMPORTSFLAGS=-w sh scripts/goimports.sh + +.PHONY: clean +clean: + rm -fr docs/html + +.PHONY: update-modules +update-modules: + go run scripts/genmod/main.go . + +.PHONY: docs +docs: +ifdef ELASTIC_DOCS + $(ELASTIC_DOCS)/build_docs --direct_html --chunk=1 $(BUILD_DOCS_ARGS) --doc docs/index.asciidoc --out docs/html +else + @echo "\nELASTIC_DOCS is not defined.\n" + @exit 1 +endif + +.PHONY: update-licenses +update-licenses: + go-licenser $(patsubst %, -exclude %, $(GO_LICENSER_EXCLUDE)) . diff --git a/vendor/go.elastic.co/apm/NOTICE b/vendor/go.elastic.co/apm/NOTICE new file mode 100644 index 00000000000..147618ca4f9 --- /dev/null +++ b/vendor/go.elastic.co/apm/NOTICE @@ -0,0 +1,84 @@ +Elastic APM Go Agent +Copyright 2018-2019 Elasticsearch B.V. + +This product includes software developed at Elasticsearch, B.V. (https://www.elastic.co/). + +========================================= +Third party code included by the Go Agent +========================================= + +------------------------------------------------------------------------------------ +This project copies code from the Go standard library (https://github.com/golang/go) +------------------------------------------------------------------------------------ + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------------- +This project copies code from Gorilla Mux (https://github.com/gorilla/mux) +-------------------------------------------------------------------------- + +Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------ +This project copies code from pq (https://github.com/lib/pq) +------------------------------------------------------------ + +Copyright (c) 2011-2013, 'pq' Contributors Portions Copyright (C) 2011 Blake Mizerany + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/go.elastic.co/apm/README.md b/vendor/go.elastic.co/apm/README.md new file mode 100644 index 00000000000..f173f83d4df --- /dev/null +++ b/vendor/go.elastic.co/apm/README.md @@ -0,0 +1,41 @@ +[![Build Status](https://apm-ci.elastic.co/buildStatus/icon?job=apm-agent-go/apm-agent-go-mbp/master)](https://apm-ci.elastic.co/job/apm-agent-go/job/apm-agent-go-mbp/job/master/) +[![GoDoc](https://godoc.org/go.elastic.co/apm?status.svg)](http://godoc.org/go.elastic.co/apm) +[![Travis-CI](https://travis-ci.org/elastic/apm-agent-go.svg)](https://travis-ci.org/elastic/apm-agent-go) +[![AppVeyor](https://ci.appveyor.com/api/projects/status/28fhswvqqc7p90f7?svg=true)](https://ci.appveyor.com/project/AndrewWilkins/apm-agent-go) +[![Go Report Card](https://goreportcard.com/badge/go.elastic.co/apm)](https://goreportcard.com/report/go.elastic.co/apm) +[![codecov.io](https://codecov.io/github/elastic/apm-agent-go/coverage.svg?branch=master)](https://codecov.io/github/elastic/apm-agent-go?branch=master) + +# apm-agent-go: APM Agent for Go + +This is the official Go package for [Elastic APM](https://www.elastic.co/solutions/apm). + +The Go agent enables you to trace the execution of operations in your application, +sending performance metrics and errors to the Elastic APM server. You can find a +list of the supported frameworks and other technologies in the [documentation](https://www.elastic.co/guide/en/apm/agent/go/current/supported-tech.html). + +We'd love to hear your feedback, please take a minute to fill out our [survey](https://docs.google.com/forms/d/e/1FAIpQLScbW7D8m-otPO7cxqeg7XstWR8vMnxG6brnXLs_TFVSTHuHvg/viewform?usp=sf_link). + +## Installation + +```bash +go get -u go.elastic.co/apm +``` + +## Requirements + +Tested with Go 1.8+ on Linux, Windows and MacOS. + +Requires [APM Server](https://github.com/elastic/apm-server) v6.5 or newer. + +## License + +Apache 2.0. + +## Documentation + +[Elastic APM Go documentation](https://www.elastic.co/guide/en/apm/agent/go/current/index.html). + +## Getting Help + +If you find a bug, please [report an issue](https://github.com/elastic/apm-agent-go/issues). +For any other assistance, please open or add to a topic on the [APM discuss forum](https://discuss.elastic.co/c/apm). diff --git a/vendor/go.elastic.co/apm/apmconfig/doc.go b/vendor/go.elastic.co/apm/apmconfig/doc.go new file mode 100644 index 00000000000..dd29e1525a6 --- /dev/null +++ b/vendor/go.elastic.co/apm/apmconfig/doc.go @@ -0,0 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package apmconfig provides an API for watching agent config +// changes. +package apmconfig diff --git a/vendor/go.elastic.co/apm/apmconfig/watcher.go b/vendor/go.elastic.co/apm/apmconfig/watcher.go new file mode 100644 index 00000000000..57ea3c41240 --- /dev/null +++ b/vendor/go.elastic.co/apm/apmconfig/watcher.go @@ -0,0 +1,54 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmconfig + +import ( + "context" +) + +// Watcher provides an interface for watching config changes. +type Watcher interface { + // WatchConfig subscribes to changes to configuration for the agent, + // which must match the given ConfigSelector. + // + // If the watcher experiences an unexpected error fetching config, + // it will surface this in a Change with the Err field set. + // + // If the provided context is cancelled, or the watcher experiences + // a fatal condition, the returned channel will be closed. + WatchConfig(context.Context, WatchParams) <-chan Change +} + +// WatchParams holds parameters for watching for config changes. +type WatchParams struct { + // Service holds the name and optionally environment name used + // for filtering the config to watch. + Service struct { + Name string + Environment string + } +} + +// Change holds an agent configuration change: an error or the new config attributes. +type Change struct { + // Err holds an error that occurred while querying agent config. + Err error + + // Attrs holds the agent's configuration. May be empty. + Attrs map[string]string +} diff --git a/vendor/go.elastic.co/apm/apmtest/configwatcher.go b/vendor/go.elastic.co/apm/apmtest/configwatcher.go new file mode 100644 index 00000000000..723466a79e8 --- /dev/null +++ b/vendor/go.elastic.co/apm/apmtest/configwatcher.go @@ -0,0 +1,32 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmtest + +import ( + "context" + + "go.elastic.co/apm/apmconfig" +) + +// WatchConfigFunc is a function type that implements apmconfig.Watcher. +type WatchConfigFunc func(context.Context, apmconfig.WatchParams) <-chan apmconfig.Change + +// WatchConfig returns f(ctx, params). +func (f WatchConfigFunc) WatchConfig(ctx context.Context, params apmconfig.WatchParams) <-chan apmconfig.Change { + return f(ctx, params) +} diff --git a/vendor/go.elastic.co/apm/apmtest/discard.go b/vendor/go.elastic.co/apm/apmtest/discard.go new file mode 100644 index 00000000000..186b21af83e --- /dev/null +++ b/vendor/go.elastic.co/apm/apmtest/discard.go @@ -0,0 +1,52 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmtest + +import ( + "log" + + "go.elastic.co/apm" + "go.elastic.co/apm/transport/transporttest" +) + +// DiscardTracer is an apm.Tracer that discards all events. +// +// This tracer may be used by multiple tests, and so should +// not be modified or closed. +// +// Importing apmttest will close apm.DefaultTracer, and update +// it to this value. +var DiscardTracer *apm.Tracer + +// NewDiscardTracer returns a new apm.Tracer that discards all events. +func NewDiscardTracer() *apm.Tracer { + tracer, err := apm.NewTracerOptions(apm.TracerOptions{ + Transport: transporttest.Discard, + }) + if err != nil { + log.Fatal(err) + } + return tracer +} + +func init() { + apm.DefaultTracer.Close() + tracer := NewDiscardTracer() + DiscardTracer = tracer + apm.DefaultTracer = DiscardTracer +} diff --git a/vendor/go.elastic.co/apm/apmtest/httpsuite.go b/vendor/go.elastic.co/apm/apmtest/httpsuite.go new file mode 100644 index 00000000000..cf075b24abe --- /dev/null +++ b/vendor/go.elastic.co/apm/apmtest/httpsuite.go @@ -0,0 +1,137 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmtest + +import ( + "net/http" + "net/http/httptest" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "go.elastic.co/apm" + "go.elastic.co/apm/transport/transporttest" +) + +// HTTPTestSuite is a test suite for HTTP instrumentation modules. +type HTTPTestSuite struct { + suite.Suite + + // Handler holds an instrumented HTTP handler. Handler must + // support the following routes: + // + // GET /implicit_write (no explicit write on the response) + // GET /panic_before_write (panic without writing response) + // GET /panic_after_write (panic after writing response) + // + Handler http.Handler + + // Tracer is the apm.Tracer used to instrument Handler. + // + // HTTPTestSuite will close the tracer when all tests have + // been completed. + Tracer *apm.Tracer + + // Recorder is the transport used as the transport for Tracer. + Recorder *transporttest.RecorderTransport + + server *httptest.Server +} + +// SetupTest runs before each test. +func (s *HTTPTestSuite) SetupTest() { + s.Recorder.ResetPayloads() +} + +// SetupSuite runs before the tests in the suite are run. +func (s *HTTPTestSuite) SetupSuite() { + s.server = httptest.NewServer(s.Handler) +} + +// TearDownSuite runs after the tests in the suite are run. +func (s *HTTPTestSuite) TearDownSuite() { + if s.server != nil { + s.server.Close() + } + s.Tracer.Close() +} + +// TestImplicitWrite tests the behaviour of instrumented handlers +// for routes which do not explicitly write a response, but instead +// leave it to the framework to write an empty 200 response. +func (s *HTTPTestSuite) TestImplicitWrite() { + resp, err := http.Get(s.server.URL + "/implicit_write") + require.NoError(s.T(), err) + resp.Body.Close() + s.Equal(http.StatusOK, resp.StatusCode) + + s.Tracer.Flush(nil) + ps := s.Recorder.Payloads() + require.Len(s.T(), ps.Transactions, 1) + + tx := ps.Transactions[0] + s.Equal("HTTP 2xx", tx.Result) + s.Equal(resp.StatusCode, tx.Context.Response.StatusCode) +} + +// TestPanicBeforeWrite tests the behaviour of instrumented handlers +// for routes which panic before any headers are written. The handler +// is expected to recover the panic and write an empty 500 response. +func (s *HTTPTestSuite) TestPanicBeforeWrite() { + resp, err := http.Get(s.server.URL + "/panic_before_write") + require.NoError(s.T(), err) + resp.Body.Close() + s.Equal(http.StatusInternalServerError, resp.StatusCode) + + s.Tracer.Flush(nil) + ps := s.Recorder.Payloads() + require.Len(s.T(), ps.Transactions, 1) + require.Len(s.T(), ps.Errors, 1) + + tx := ps.Transactions[0] + s.Equal("HTTP 5xx", tx.Result) + s.Equal(resp.StatusCode, tx.Context.Response.StatusCode) + + e := ps.Errors[0] + s.Equal(tx.ID, e.ParentID) + s.Equal(resp.StatusCode, e.Context.Response.StatusCode) +} + +// TestPanicAfterWrite tests the behaviour of instrumented handlers +// for routes which panic after writing headers. The handler is +// expected to recover the panic without otherwise affecting the +// response. +func (s *HTTPTestSuite) TestPanicAfterWrite() { + resp, err := http.Get(s.server.URL + "/panic_after_write") + require.NoError(s.T(), err) + resp.Body.Close() + s.Equal(http.StatusOK, resp.StatusCode) + + s.Tracer.Flush(nil) + ps := s.Recorder.Payloads() + require.Len(s.T(), ps.Transactions, 1) + require.Len(s.T(), ps.Errors, 1) + + tx := ps.Transactions[0] + s.Equal("HTTP 2xx", tx.Result) + s.Equal(resp.StatusCode, tx.Context.Response.StatusCode) + + e := ps.Errors[0] + s.Equal(tx.ID, e.ParentID) + s.Equal(resp.StatusCode, e.Context.Response.StatusCode) +} diff --git a/vendor/go.elastic.co/apm/apmtest/recorder.go b/vendor/go.elastic.co/apm/apmtest/recorder.go new file mode 100644 index 00000000000..8f2e65519ee --- /dev/null +++ b/vendor/go.elastic.co/apm/apmtest/recorder.go @@ -0,0 +1,69 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmtest + +import ( + "context" + "fmt" + + "go.elastic.co/apm" + "go.elastic.co/apm/model" + "go.elastic.co/apm/transport/transporttest" +) + +// NewRecordingTracer returns a new RecordingTracer, containing a new +// Tracer using the RecorderTransport stored inside. +func NewRecordingTracer() *RecordingTracer { + var result RecordingTracer + tracer, err := apm.NewTracerOptions(apm.TracerOptions{ + Transport: &result.RecorderTransport, + }) + if err != nil { + panic(err) + } + result.Tracer = tracer + return &result +} + +// RecordingTracer holds an apm.Tracer and transporttest.RecorderTransport. +type RecordingTracer struct { + *apm.Tracer + transporttest.RecorderTransport +} + +// WithTransaction calls rt.WithTransactionOptions with a zero apm.TransactionOptions. +func (rt *RecordingTracer) WithTransaction(f func(ctx context.Context)) (model.Transaction, []model.Span, []model.Error) { + return rt.WithTransactionOptions(apm.TransactionOptions{}, f) +} + +// WithTransactionOptions starts a transaction with the given options, +// calls f with the transaction in the provided context, ends the transaction +// and flushes the tracer, and then returns the resulting events. +func (rt *RecordingTracer) WithTransactionOptions(opts apm.TransactionOptions, f func(ctx context.Context)) (model.Transaction, []model.Span, []model.Error) { + tx := rt.StartTransactionOptions("name", "type", opts) + ctx := apm.ContextWithTransaction(context.Background(), tx) + f(ctx) + + tx.End() + rt.Flush(nil) + payloads := rt.Payloads() + if n := len(payloads.Transactions); n != 1 { + panic(fmt.Errorf("expected 1 transaction, got %d", n)) + } + return payloads.Transactions[0], payloads.Spans, payloads.Errors +} diff --git a/vendor/go.elastic.co/apm/apmtest/recordlogger.go b/vendor/go.elastic.co/apm/apmtest/recordlogger.go new file mode 100644 index 00000000000..9196c36851b --- /dev/null +++ b/vendor/go.elastic.co/apm/apmtest/recordlogger.go @@ -0,0 +1,60 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmtest + +import "fmt" + +// RecordLogger is an implementation of apm.Logger, recording log entries. +type RecordLogger struct { + Records []LogRecord +} + +// Debugf logs debug messages. +func (l *RecordLogger) Debugf(format string, args ...interface{}) { + l.logf("debug", format, args...) +} + +// Errorf logs error messages. +func (l *RecordLogger) Errorf(format string, args ...interface{}) { + l.logf("error", format, args...) +} + +// Warningf logs error messages. +func (l *RecordLogger) Warningf(format string, args ...interface{}) { + l.logf("warning", format, args...) +} + +func (l *RecordLogger) logf(level string, format string, args ...interface{}) { + l.Records = append(l.Records, LogRecord{ + Level: level, + Format: format, + Message: fmt.Sprintf(format, args...), + }) +} + +// LogRecord holds the details of a log record. +type LogRecord struct { + // Level is the log level: "debug", "error", or "warning". + Level string + + // Format is the log message format, like "Thingy did foo %d times". + Format string + + // Message is the formatted message. + Message string +} diff --git a/vendor/go.elastic.co/apm/apmtest/testlogger.go b/vendor/go.elastic.co/apm/apmtest/testlogger.go new file mode 100644 index 00000000000..1bbbdf92a71 --- /dev/null +++ b/vendor/go.elastic.co/apm/apmtest/testlogger.go @@ -0,0 +1,45 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmtest + +// TestLogger is an implementation of apm.Logger, +// logging to a testing.T. +type TestLogger struct { + l LogfLogger +} + +// NewTestLogger returns a new TestLogger that logs messages to l. +func NewTestLogger(l LogfLogger) TestLogger { + return TestLogger{l: l} +} + +// Debugf logs debug messages. +func (t TestLogger) Debugf(format string, args ...interface{}) { + t.l.Logf("[DEBUG] "+format, args...) +} + +// Errorf logs error messages. +func (t TestLogger) Errorf(format string, args ...interface{}) { + t.l.Logf("[ERROR] "+format, args...) +} + +// LogfLogger is an interface with the a Logf method, +// implemented by *testing.T and *testing.B. +type LogfLogger interface { + Logf(string, ...interface{}) +} diff --git a/vendor/go.elastic.co/apm/apmtest/withtransaction.go b/vendor/go.elastic.co/apm/apmtest/withtransaction.go new file mode 100644 index 00000000000..3c19998a498 --- /dev/null +++ b/vendor/go.elastic.co/apm/apmtest/withtransaction.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmtest + +import ( + "context" + + "go.elastic.co/apm" + "go.elastic.co/apm/model" +) + +// WithTransaction is equivalent to calling WithTransactionOptions with a zero TransactionOptions. +func WithTransaction(f func(ctx context.Context)) (model.Transaction, []model.Span, []model.Error) { + return WithTransactionOptions(apm.TransactionOptions{}, f) +} + +// WithTransactionOptions calls f with a new context containing a transaction +// and transaction options, flushes the transaction to a test server, and returns +// the decoded transaction and any associated spans and errors. +func WithTransactionOptions(opts apm.TransactionOptions, f func(ctx context.Context)) (model.Transaction, []model.Span, []model.Error) { + tracer := NewRecordingTracer() + defer tracer.Close() + return tracer.WithTransactionOptions(opts, f) +} diff --git a/vendor/go.elastic.co/apm/breakdown.go b/vendor/go.elastic.co/apm/breakdown.go new file mode 100644 index 00000000000..df6cf519014 --- /dev/null +++ b/vendor/go.elastic.co/apm/breakdown.go @@ -0,0 +1,365 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + "unsafe" + + "go.elastic.co/apm/model" +) + +const ( + // breakdownMetricsLimit is the maximum number of breakdown metric + // buckets to accumulate per reporting period. Metrics are broken + // down by {transactionType, transactionName, spanType, spanSubtype} + // tuples. + breakdownMetricsLimit = 1000 + + // appSpanType is the special span type associated with transactions, + // for reporting transaction self-time. + appSpanType = "app" + + // Breakdown metric names. + transactionDurationCountMetricName = "transaction.duration.count" + transactionDurationSumMetricName = "transaction.duration.sum.us" + transactionBreakdownCountMetricName = "transaction.breakdown.count" + spanSelfTimeCountMetricName = "span.self_time.count" + spanSelfTimeSumMetricName = "span.self_time.sum.us" +) + +type pad32 struct { + // Zero-sized on 64-bit architectures, 4 bytes on 32-bit. + _ [(unsafe.Alignof(uint64(0)) % 8) / 4]uintptr +} + +var ( + breakdownMetricsLimitWarning = fmt.Sprintf(` +The limit of %d breakdown metricsets has been reached, no new metricsets will be created. +Try to name your transactions so that there are less distinct transaction names.`[1:], + breakdownMetricsLimit, + ) +) + +// spanTimingsKey identifies a span type and subtype, for use as the key in +// spanTimingsMap. +type spanTimingsKey struct { + spanType string + spanSubtype string +} + +// spanTiming records the number of times a {spanType, spanSubtype} pair +// has occurred (within the context of a transaction group), along with +// the sum of the span durations. +type spanTiming struct { + duration int64 + count uintptr +} + +// spanTimingsMap records span timings for a transaction group. +type spanTimingsMap map[spanTimingsKey]spanTiming + +// add accumulates the timing for a {spanType, spanSubtype} pair. +func (m spanTimingsMap) add(spanType, spanSubtype string, d time.Duration) { + k := spanTimingsKey{spanType: spanType, spanSubtype: spanSubtype} + timing := m[k] + timing.count++ + timing.duration += int64(d) + m[k] = timing +} + +// reset resets m back to its initial zero state. +func (m spanTimingsMap) reset() { + for k := range m { + delete(m, k) + } +} + +// breakdownMetrics holds a pair of breakdown metrics maps. The "active" map +// accumulates new breakdown metrics, and is swapped with the "inactive" map +// just prior to when metrics gathering begins. When metrics gathering +// completes, the inactive map will be empty. +// +// breakdownMetrics may be written to concurrently by the tracer, and any +// number of other goroutines when a transaction cannot be enqueued. +type breakdownMetrics struct { + enabled bool + + mu sync.RWMutex + active, inactive *breakdownMetricsMap +} + +func newBreakdownMetrics() *breakdownMetrics { + return &breakdownMetrics{ + active: newBreakdownMetricsMap(), + inactive: newBreakdownMetricsMap(), + } +} + +type breakdownMetricsMap struct { + mu sync.RWMutex + entries int + m map[uint64][]*breakdownMetricsMapEntry + space []breakdownMetricsMapEntry +} + +func newBreakdownMetricsMap() *breakdownMetricsMap { + return &breakdownMetricsMap{ + m: make(map[uint64][]*breakdownMetricsMapEntry), + space: make([]breakdownMetricsMapEntry, breakdownMetricsLimit), + } +} + +type breakdownMetricsMapEntry struct { + breakdownTiming + breakdownMetricsKey +} + +// breakdownMetricsKey identifies a transaction group, and optionally a +// spanTimingsKey, for recording transaction and span breakdown metrics. +type breakdownMetricsKey struct { + transactionType string + transactionName string + spanTimingsKey +} + +func (k breakdownMetricsKey) hash() uint64 { + h := newFnv1a() + h.add(k.transactionType) + h.add(k.transactionName) + if k.spanType != "" { + h.add(k.spanType) + } + if k.spanSubtype != "" { + h.add(k.spanSubtype) + } + return uint64(h) +} + +// breakdownTiming holds breakdown metrics. +type breakdownTiming struct { + // transaction holds the "transaction.duration" metric values. + transaction spanTiming + + // Padding to ensure the span field below is 64-bit aligned. + _ pad32 + + // span holds the "span.self_time" metric values. + span spanTiming + + // breakdownCount records the number of transactions for which we + // have calculated breakdown metrics. If breakdown metrics are + // enabled, this will be equal transaction.count. + breakdownCount uintptr +} + +func (lhs *breakdownTiming) accumulate(rhs breakdownTiming) { + atomic.AddUintptr(&lhs.transaction.count, rhs.transaction.count) + atomic.AddInt64(&lhs.transaction.duration, rhs.transaction.duration) + atomic.AddUintptr(&lhs.span.count, rhs.span.count) + atomic.AddInt64(&lhs.span.duration, rhs.span.duration) + atomic.AddUintptr(&lhs.breakdownCount, rhs.breakdownCount) +} + +// recordTransaction records breakdown metrics for td into m. +// +// recordTransaction returns true if breakdown metrics were +// completely recorded, and false if any metrics were not +// recorded due to the limit being reached. +func (m *breakdownMetrics) recordTransaction(td *TransactionData) bool { + m.mu.RLock() + defer m.mu.RUnlock() + + k := breakdownMetricsKey{ + transactionType: td.Type, + transactionName: td.Name, + } + k.spanType = appSpanType + + var breakdownCount int + var transactionSpanTiming spanTiming + var transactionDuration = spanTiming{count: 1, duration: int64(td.Duration)} + if td.breakdownMetricsEnabled { + breakdownCount = 1 + endTime := td.timestamp.Add(td.Duration) + transactionSelfTime := td.Duration - td.childrenTimer.finalDuration(endTime) + transactionSpanTiming = spanTiming{count: 1, duration: int64(transactionSelfTime)} + } + + if !m.active.record(k, breakdownTiming{ + transaction: transactionDuration, + breakdownCount: uintptr(breakdownCount), + span: transactionSpanTiming, + }) { + // We couldn't record the transaction's metricset, so we won't + // be able to record spans for that transaction either. + return false + } + + ok := true + for sk, timing := range td.spanTimings { + k.spanTimingsKey = sk + ok = ok && m.active.record(k, breakdownTiming{span: timing}) + } + return ok +} + +// record records a single breakdown metric, identified by k. +func (m *breakdownMetricsMap) record(k breakdownMetricsKey, bt breakdownTiming) bool { + hash := k.hash() + m.mu.RLock() + entries, ok := m.m[hash] + m.mu.RUnlock() + var offset int + if ok { + for offset = range entries { + if entries[offset].breakdownMetricsKey == k { + // The append may reallocate the entries, but the + // entries are pointers into m.activeSpace. Therefore, + // entries' timings can safely be atomically incremented + // without holding the read lock. + entries[offset].breakdownTiming.accumulate(bt) + return true + } + } + offset++ // where to start searching with the write lock below + } + + m.mu.Lock() + entries, ok = m.m[hash] + if ok { + for i := range entries[offset:] { + if entries[offset+i].breakdownMetricsKey == k { + m.mu.Unlock() + entries[offset+i].breakdownTiming.accumulate(bt) + return true + } + } + } else if m.entries >= breakdownMetricsLimit { + m.mu.Unlock() + return false + } + entry := &m.space[m.entries] + *entry = breakdownMetricsMapEntry{ + breakdownTiming: bt, + breakdownMetricsKey: k, + } + m.m[hash] = append(entries, entry) + m.entries++ + m.mu.Unlock() + return true +} + +// gather is called by builtinMetricsGatherer to gather breakdown metrics. +func (m *breakdownMetrics) gather(out *Metrics) { + // Hold m.mu only long enough to swap m.active and m.inactive. + // This will be blocked by metric updates, but that's OK; only + // metrics gathering will be delayed. After swapping we do not + // need to hold m.mu, since nothing concurrently accesses + // m.inactive while the gatherer is iterating over it. + m.mu.Lock() + m.active, m.inactive = m.inactive, m.active + m.mu.Unlock() + + for hash, entries := range m.inactive.m { + for _, entry := range entries { + if entry.transaction.count > 0 { + out.transactionGroupMetrics = append(out.transactionGroupMetrics, &model.Metrics{ + Transaction: model.MetricsTransaction{ + Type: entry.transactionType, + Name: entry.transactionName, + }, + Samples: map[string]model.Metric{ + transactionDurationCountMetricName: { + Value: float64(entry.transaction.count), + }, + transactionDurationSumMetricName: { + Value: durationMicros(time.Duration(entry.transaction.duration)), + }, + transactionBreakdownCountMetricName: { + Value: float64(entry.breakdownCount), + }, + }, + }) + } + if entry.span.count > 0 { + out.transactionGroupMetrics = append(out.transactionGroupMetrics, &model.Metrics{ + Transaction: model.MetricsTransaction{ + Type: entry.transactionType, + Name: entry.transactionName, + }, + Span: model.MetricsSpan{ + Type: entry.spanType, + Subtype: entry.spanSubtype, + }, + Samples: map[string]model.Metric{ + spanSelfTimeCountMetricName: { + Value: float64(entry.span.count), + }, + spanSelfTimeSumMetricName: { + Value: durationMicros(time.Duration(entry.span.duration)), + }, + }, + }) + } + entry.breakdownMetricsKey = breakdownMetricsKey{} // release strings + } + delete(m.inactive.m, hash) + } + m.inactive.entries = 0 +} + +// childrenTimer tracks time spent by children of a transaction or span. +// +// childrenTimer is not goroutine-safe. +type childrenTimer struct { + // active holds the number active children. + active int + + // start holds the timestamp at which active went from zero to one. + start time.Time + + // totalDuration holds the total duration of time periods in which + // at least one child was active. + totalDuration time.Duration +} + +func (t *childrenTimer) childStarted(start time.Time) { + t.active++ + if t.active == 1 { + t.start = start + } +} + +func (t *childrenTimer) childEnded(end time.Time) { + t.active-- + if t.active == 0 { + t.totalDuration += end.Sub(t.start) + } +} + +func (t *childrenTimer) finalDuration(end time.Time) time.Duration { + if t.active > 0 { + t.active = 0 + t.totalDuration += end.Sub(t.start) + } + return t.totalDuration +} diff --git a/vendor/go.elastic.co/apm/builtin_metrics.go b/vendor/go.elastic.co/apm/builtin_metrics.go new file mode 100644 index 00000000000..546384efc8b --- /dev/null +++ b/vendor/go.elastic.co/apm/builtin_metrics.go @@ -0,0 +1,164 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "context" + "runtime" + + sysinfo "github.com/elastic/go-sysinfo" + "github.com/elastic/go-sysinfo/types" +) + +// builtinMetricsGatherer is an MetricsGatherer which gathers builtin metrics: +// - goroutines +// - memstats (allocations, usage, GC, etc.) +// - system and process CPU and memory usage +type builtinMetricsGatherer struct { + tracer *Tracer + lastSysMetrics sysMetrics +} + +func newBuiltinMetricsGatherer(t *Tracer) *builtinMetricsGatherer { + g := &builtinMetricsGatherer{tracer: t} + if metrics, err := gatherSysMetrics(); err == nil { + g.lastSysMetrics = metrics + } + return g +} + +// GatherMetrics gathers mem metrics into m. +func (g *builtinMetricsGatherer) GatherMetrics(ctx context.Context, m *Metrics) error { + m.Add("golang.goroutines", nil, float64(runtime.NumGoroutine())) + g.gatherSystemMetrics(m) + g.gatherMemStatsMetrics(m) + g.tracer.breakdownMetrics.gather(m) + return nil +} + +func (g *builtinMetricsGatherer) gatherSystemMetrics(m *Metrics) { + metrics, err := gatherSysMetrics() + if err != nil { + return + } + systemCPU, processCPU := calculateCPUUsage(metrics.cpu, g.lastSysMetrics.cpu) + m.Add("system.cpu.total.norm.pct", nil, systemCPU) + m.Add("system.process.cpu.total.norm.pct", nil, processCPU) + m.Add("system.memory.total", nil, float64(metrics.mem.system.Total)) + m.Add("system.memory.actual.free", nil, float64(metrics.mem.system.Available)) + m.Add("system.process.memory.size", nil, float64(metrics.mem.process.Virtual)) + m.Add("system.process.memory.rss.bytes", nil, float64(metrics.mem.process.Resident)) + g.lastSysMetrics = metrics +} + +func (g *builtinMetricsGatherer) gatherMemStatsMetrics(m *Metrics) { + var mem runtime.MemStats + runtime.ReadMemStats(&mem) + + addUint64 := func(name string, v uint64) { + m.Add(name, nil, float64(v)) + } + add := func(name string, v float64) { + m.Add(name, nil, v) + } + + addUint64("golang.heap.allocations.mallocs", mem.Mallocs) + addUint64("golang.heap.allocations.frees", mem.Frees) + addUint64("golang.heap.allocations.objects", mem.HeapObjects) + addUint64("golang.heap.allocations.total", mem.TotalAlloc) + addUint64("golang.heap.allocations.allocated", mem.HeapAlloc) + addUint64("golang.heap.allocations.idle", mem.HeapIdle) + addUint64("golang.heap.allocations.active", mem.HeapInuse) + addUint64("golang.heap.system.total", mem.Sys) + addUint64("golang.heap.system.obtained", mem.HeapSys) + addUint64("golang.heap.system.stack", mem.StackSys) + addUint64("golang.heap.system.released", mem.HeapReleased) + addUint64("golang.heap.gc.next_gc_limit", mem.NextGC) + addUint64("golang.heap.gc.total_count", uint64(mem.NumGC)) + addUint64("golang.heap.gc.total_pause.ns", mem.PauseTotalNs) + add("golang.heap.gc.cpu_fraction", mem.GCCPUFraction) +} + +func calculateCPUUsage(current, last cpuMetrics) (systemUsage, processUsage float64) { + idleDelta := current.system.Idle + current.system.IOWait - last.system.Idle - last.system.IOWait + systemTotalDelta := current.system.Total() - last.system.Total() + if systemTotalDelta <= 0 { + return 0, 0 + } + + idlePercent := float64(idleDelta) / float64(systemTotalDelta) + systemUsage = 1 - idlePercent + + processTotalDelta := current.process.Total() - last.process.Total() + processUsage = float64(processTotalDelta) / float64(systemTotalDelta) + + return systemUsage, processUsage +} + +type sysMetrics struct { + cpu cpuMetrics + mem memoryMetrics +} + +type cpuMetrics struct { + process types.CPUTimes + system types.CPUTimes +} + +type memoryMetrics struct { + process types.MemoryInfo + system *types.HostMemoryInfo +} + +func gatherSysMetrics() (sysMetrics, error) { + proc, err := sysinfo.Self() + if err != nil { + return sysMetrics{}, err + } + host, err := sysinfo.Host() + if err != nil { + return sysMetrics{}, err + } + hostTimes, err := host.CPUTime() + if err != nil { + return sysMetrics{}, err + } + hostMemory, err := host.Memory() + if err != nil { + return sysMetrics{}, err + } + procTimes, err := proc.CPUTime() + if err != nil { + return sysMetrics{}, err + } + procMemory, err := proc.Memory() + if err != nil { + return sysMetrics{}, err + } + + return sysMetrics{ + cpu: cpuMetrics{ + system: hostTimes, + process: procTimes, + }, + mem: memoryMetrics{ + system: hostMemory, + process: procMemory, + }, + }, nil +} diff --git a/vendor/go.elastic.co/apm/capturebody.go b/vendor/go.elastic.co/apm/capturebody.go new file mode 100644 index 00000000000..5e3f402b012 --- /dev/null +++ b/vendor/go.elastic.co/apm/capturebody.go @@ -0,0 +1,198 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "bytes" + "io" + "net/http" + "net/url" + "sync" + "unicode/utf8" + + "go.elastic.co/apm/internal/apmstrings" + "go.elastic.co/apm/model" +) + +// CaptureBodyMode holds a value indicating how a tracer should capture +// HTTP request bodies: for transactions, for errors, for both, or neither. +type CaptureBodyMode int + +const ( + // CaptureBodyOff disables capturing of HTTP request bodies. This is + // the default mode. + CaptureBodyOff CaptureBodyMode = 0 + + // CaptureBodyErrors captures HTTP request bodies for only errors. + CaptureBodyErrors CaptureBodyMode = 1 + + // CaptureBodyTransactions captures HTTP request bodies for only + // transactions. + CaptureBodyTransactions CaptureBodyMode = 1 << 1 + + // CaptureBodyAll captures HTTP request bodies for both transactions + // and errors. + CaptureBodyAll CaptureBodyMode = CaptureBodyErrors | CaptureBodyTransactions +) + +var bodyCapturerPool = sync.Pool{ + New: func() interface{} { + return &BodyCapturer{} + }, +} + +// CaptureHTTPRequestBody replaces req.Body and returns a possibly nil +// BodyCapturer which can later be passed to Context.SetHTTPRequestBody +// for setting the request body in a transaction or error context. If the +// tracer is not configured to capture HTTP request bodies, then req.Body +// is left alone and nil is returned. +// +// This must be called before the request body is read. The BodyCapturer's +// Discard method should be called after it is no longer needed, in order +// to recycle its memory. +func (t *Tracer) CaptureHTTPRequestBody(req *http.Request) *BodyCapturer { + if req.Body == nil { + return nil + } + captureBody := t.instrumentationConfig().captureBody + if captureBody == CaptureBodyOff { + return nil + } + + bc := bodyCapturerPool.Get().(*BodyCapturer) + bc.captureBody = captureBody + bc.request = req + bc.originalBody = req.Body + bc.buffer.Reset() + req.Body = bodyCapturerReadCloser{BodyCapturer: bc} + return bc +} + +// bodyCapturerReadCloser implements io.ReadCloser using the embedded BodyCapturer. +type bodyCapturerReadCloser struct { + *BodyCapturer +} + +// Close closes the original body. +func (bc bodyCapturerReadCloser) Close() error { + return bc.originalBody.Close() +} + +// Read reads from the original body, copying into bc.buffer. +func (bc bodyCapturerReadCloser) Read(p []byte) (int, error) { + n, err := bc.originalBody.Read(p) + if n > 0 { + bc.buffer.Write(p[:n]) + } + return n, err +} + +// BodyCapturer is returned by Tracer.CaptureHTTPRequestBody to later be +// passed to Context.SetHTTPRequestBody. +// +// Calling Context.SetHTTPRequestBody will reset req.Body to its original +// value, and invalidates the BodyCapturer. +type BodyCapturer struct { + captureBody CaptureBodyMode + + readbuf [bytes.MinRead]byte + buffer limitedBuffer + request *http.Request + originalBody io.ReadCloser +} + +// Discard discards the body capturer: the original request body is +// replaced, and the body capturer is returned to a pool for reuse. +// The BodyCapturer must not be used after calling this. +// +// Discard has no effect if bc is nil. +func (bc *BodyCapturer) Discard() { + if bc == nil { + return + } + bc.request.Body = bc.originalBody + bodyCapturerPool.Put(bc) +} + +func (bc *BodyCapturer) setContext(out *model.RequestBody) bool { + if bc.request.PostForm != nil { + // We must copy the map in case we need to + // sanitize the values. Ideally we should only + // copy if sanitization is necessary, but body + // capture shouldn't typically be enabled so + // we don't currently optimize this. + postForm := make(url.Values, len(bc.request.PostForm)) + for k, v := range bc.request.PostForm { + vcopy := make([]string, len(v)) + for i := range vcopy { + vcopy[i] = truncateString(v[i]) + } + postForm[k] = vcopy + } + out.Form = postForm + return true + } + + body, n := apmstrings.Truncate(bc.buffer.String(), stringLengthLimit) + if n == stringLengthLimit { + // There is at least enough data in the buffer + // to hit the string length limit, so we don't + // need to read from bc.originalBody as well. + out.Raw = body + return true + } + + // Read the remaining body, limiting to the maximum number of bytes + // that could make up the truncation limit. We ignore any errors here, + // and just return whatever we can. + rem := utf8.UTFMax * (stringLengthLimit - n) + for { + buf := bc.readbuf[:] + if rem < bytes.MinRead { + buf = buf[:rem] + } + n, err := bc.originalBody.Read(buf) + if n > 0 { + bc.buffer.Write(buf[:n]) + rem -= n + } + if rem == 0 || err != nil { + break + } + } + body, _ = apmstrings.Truncate(bc.buffer.String(), stringLengthLimit) + out.Raw = body + return body != "" +} + +type limitedBuffer struct { + bytes.Buffer +} + +func (b *limitedBuffer) Write(p []byte) (n int, err error) { + rem := (stringLengthLimit * utf8.UTFMax) - b.Len() + n = len(p) + if n > rem { + p = p[:rem] + } + written, err := b.Buffer.Write(p) + if err != nil { + n = written + } + return n, err +} diff --git a/vendor/go.elastic.co/apm/config.go b/vendor/go.elastic.co/apm/config.go new file mode 100644 index 00000000000..10f86e26bd3 --- /dev/null +++ b/vendor/go.elastic.co/apm/config.go @@ -0,0 +1,448 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync/atomic" + "time" + "unsafe" + + "github.com/pkg/errors" + + "go.elastic.co/apm/internal/configutil" + "go.elastic.co/apm/internal/wildcard" + "go.elastic.co/apm/model" +) + +const ( + envMetricsInterval = "ELASTIC_APM_METRICS_INTERVAL" + envMaxSpans = "ELASTIC_APM_TRANSACTION_MAX_SPANS" + envTransactionSampleRate = "ELASTIC_APM_TRANSACTION_SAMPLE_RATE" + envSanitizeFieldNames = "ELASTIC_APM_SANITIZE_FIELD_NAMES" + envCaptureHeaders = "ELASTIC_APM_CAPTURE_HEADERS" + envCaptureBody = "ELASTIC_APM_CAPTURE_BODY" + envServiceName = "ELASTIC_APM_SERVICE_NAME" + envServiceVersion = "ELASTIC_APM_SERVICE_VERSION" + envEnvironment = "ELASTIC_APM_ENVIRONMENT" + envSpanFramesMinDuration = "ELASTIC_APM_SPAN_FRAMES_MIN_DURATION" + envActive = "ELASTIC_APM_ACTIVE" + envAPIRequestSize = "ELASTIC_APM_API_REQUEST_SIZE" + envAPIRequestTime = "ELASTIC_APM_API_REQUEST_TIME" + envAPIBufferSize = "ELASTIC_APM_API_BUFFER_SIZE" + envMetricsBufferSize = "ELASTIC_APM_METRICS_BUFFER_SIZE" + envDisableMetrics = "ELASTIC_APM_DISABLE_METRICS" + envGlobalLabels = "ELASTIC_APM_GLOBAL_LABELS" + envStackTraceLimit = "ELASTIC_APM_STACK_TRACE_LIMIT" + envCentralConfig = "ELASTIC_APM_CENTRAL_CONFIG" + envBreakdownMetrics = "ELASTIC_APM_BREAKDOWN_METRICS" + envUseElasticTraceparentHeader = "ELASTIC_APM_USE_ELASTIC_TRACEPARENT_HEADER" + + // NOTE(axw) profiling environment variables are experimental. + // They may be removed in a future minor version without being + // considered a breaking change. + envCPUProfileInterval = "ELASTIC_APM_CPU_PROFILE_INTERVAL" + envCPUProfileDuration = "ELASTIC_APM_CPU_PROFILE_DURATION" + envHeapProfileInterval = "ELASTIC_APM_HEAP_PROFILE_INTERVAL" + + defaultAPIRequestSize = 750 * configutil.KByte + defaultAPIRequestTime = 10 * time.Second + defaultAPIBufferSize = 1 * configutil.MByte + defaultMetricsBufferSize = 750 * configutil.KByte + defaultMetricsInterval = 30 * time.Second + defaultMaxSpans = 500 + defaultCaptureHeaders = true + defaultCaptureBody = CaptureBodyOff + defaultSpanFramesMinDuration = 5 * time.Millisecond + defaultStackTraceLimit = 50 + + minAPIBufferSize = 10 * configutil.KByte + maxAPIBufferSize = 100 * configutil.MByte + minAPIRequestSize = 1 * configutil.KByte + maxAPIRequestSize = 5 * configutil.MByte + minMetricsBufferSize = 10 * configutil.KByte + maxMetricsBufferSize = 100 * configutil.MByte +) + +var ( + defaultSanitizedFieldNames = configutil.ParseWildcardPatterns(strings.Join([]string{ + "password", + "passwd", + "pwd", + "secret", + "*key", + "*token*", + "*session*", + "*credit*", + "*card*", + "authorization", + "set-cookie", + }, ",")) + + globalLabels = func() model.StringMap { + var labels model.StringMap + for _, kv := range configutil.ParseListEnv(envGlobalLabels, ",", nil) { + i := strings.IndexRune(kv, '=') + if i > 0 { + k, v := strings.TrimSpace(kv[:i]), strings.TrimSpace(kv[i+1:]) + labels = append(labels, model.StringMapItem{ + Key: cleanLabelKey(k), + Value: truncateString(v), + }) + } + } + return labels + }() +) + +func initialRequestDuration() (time.Duration, error) { + return configutil.ParseDurationEnv(envAPIRequestTime, defaultAPIRequestTime) +} + +func initialMetricsInterval() (time.Duration, error) { + return configutil.ParseDurationEnv(envMetricsInterval, defaultMetricsInterval) +} + +func initialMetricsBufferSize() (int, error) { + size, err := configutil.ParseSizeEnv(envMetricsBufferSize, defaultMetricsBufferSize) + if err != nil { + return 0, err + } + if size < minMetricsBufferSize || size > maxMetricsBufferSize { + return 0, errors.Errorf( + "%s must be at least %s and less than %s, got %s", + envMetricsBufferSize, minMetricsBufferSize, maxMetricsBufferSize, size, + ) + } + return int(size), nil +} + +func initialAPIBufferSize() (int, error) { + size, err := configutil.ParseSizeEnv(envAPIBufferSize, defaultAPIBufferSize) + if err != nil { + return 0, err + } + if size < minAPIBufferSize || size > maxAPIBufferSize { + return 0, errors.Errorf( + "%s must be at least %s and less than %s, got %s", + envAPIBufferSize, minAPIBufferSize, maxAPIBufferSize, size, + ) + } + return int(size), nil +} + +func initialAPIRequestSize() (int, error) { + size, err := configutil.ParseSizeEnv(envAPIRequestSize, defaultAPIRequestSize) + if err != nil { + return 0, err + } + if size < minAPIRequestSize || size > maxAPIRequestSize { + return 0, errors.Errorf( + "%s must be at least %s and less than %s, got %s", + envAPIRequestSize, minAPIRequestSize, maxAPIRequestSize, size, + ) + } + return int(size), nil +} + +func initialMaxSpans() (int, error) { + value := os.Getenv(envMaxSpans) + if value == "" { + return defaultMaxSpans, nil + } + max, err := strconv.Atoi(value) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envMaxSpans) + } + return max, nil +} + +// initialSampler returns a nil Sampler if all transactions should be sampled. +func initialSampler() (Sampler, error) { + value := os.Getenv(envTransactionSampleRate) + return parseSampleRate(envTransactionSampleRate, value) +} + +// parseSampleRate parses a numeric sampling rate in the range [0,1.0], returning a Sampler. +func parseSampleRate(name, value string) (Sampler, error) { + if value == "" { + value = "1" + } + ratio, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", name) + } + if ratio < 0.0 || ratio > 1.0 { + return nil, errors.Errorf( + "invalid value for %s: %s (out of range [0,1.0])", + name, value, + ) + } + return NewRatioSampler(ratio), nil +} + +func initialSanitizedFieldNames() wildcard.Matchers { + return configutil.ParseWildcardPatternsEnv(envSanitizeFieldNames, defaultSanitizedFieldNames) +} + +func initialCaptureHeaders() (bool, error) { + return configutil.ParseBoolEnv(envCaptureHeaders, defaultCaptureHeaders) +} + +func initialCaptureBody() (CaptureBodyMode, error) { + value := os.Getenv(envCaptureBody) + if value == "" { + return defaultCaptureBody, nil + } + return parseCaptureBody(envCaptureBody, value) +} + +func parseCaptureBody(name, value string) (CaptureBodyMode, error) { + switch strings.TrimSpace(strings.ToLower(value)) { + case "all": + return CaptureBodyAll, nil + case "errors": + return CaptureBodyErrors, nil + case "transactions": + return CaptureBodyTransactions, nil + case "off": + return CaptureBodyOff, nil + } + return -1, errors.Errorf("invalid %s value %q", name, value) +} + +func initialService() (name, version, environment string) { + name = os.Getenv(envServiceName) + version = os.Getenv(envServiceVersion) + environment = os.Getenv(envEnvironment) + if name == "" { + name = filepath.Base(os.Args[0]) + if runtime.GOOS == "windows" { + name = strings.TrimSuffix(name, filepath.Ext(name)) + } + } + name = sanitizeServiceName(name) + return name, version, environment +} + +func initialSpanFramesMinDuration() (time.Duration, error) { + return configutil.ParseDurationEnv(envSpanFramesMinDuration, defaultSpanFramesMinDuration) +} + +func initialActive() (bool, error) { + return configutil.ParseBoolEnv(envActive, true) +} + +func initialDisabledMetrics() wildcard.Matchers { + return configutil.ParseWildcardPatternsEnv(envDisableMetrics, nil) +} + +func initialStackTraceLimit() (int, error) { + value := os.Getenv(envStackTraceLimit) + if value == "" { + return defaultStackTraceLimit, nil + } + limit, err := strconv.Atoi(value) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envStackTraceLimit) + } + return limit, nil +} + +func initialCentralConfigEnabled() (bool, error) { + return configutil.ParseBoolEnv(envCentralConfig, true) +} + +func initialBreakdownMetricsEnabled() (bool, error) { + return configutil.ParseBoolEnv(envBreakdownMetrics, true) +} + +func initialUseElasticTraceparentHeader() (bool, error) { + return configutil.ParseBoolEnv(envUseElasticTraceparentHeader, true) +} + +func initialCPUProfileIntervalDuration() (time.Duration, time.Duration, error) { + interval, err := configutil.ParseDurationEnv(envCPUProfileInterval, 0) + if err != nil || interval <= 0 { + return 0, 0, err + } + duration, err := configutil.ParseDurationEnv(envCPUProfileDuration, 0) + if err != nil || duration <= 0 { + return 0, 0, err + } + return interval, duration, nil +} + +func initialHeapProfileInterval() (time.Duration, error) { + return configutil.ParseDurationEnv(envHeapProfileInterval, 0) +} + +// updateRemoteConfig updates t and cfg with changes held in "attrs", and reverts to local +// config for config attributes that have been removed (exist in old but not in attrs). +// +// On return from updateRemoteConfig, unapplied config will have been removed from attrs. +func (t *Tracer) updateRemoteConfig(logger WarningLogger, old, attrs map[string]string) { + warningf := func(string, ...interface{}) {} + debugf := func(string, ...interface{}) {} + errorf := func(string, ...interface{}) {} + if logger != nil { + warningf = logger.Warningf + debugf = logger.Debugf + errorf = logger.Errorf + } + envName := func(k string) string { + return "ELASTIC_APM_" + strings.ToUpper(k) + } + + var updates []func(cfg *instrumentationConfig) + for k, v := range attrs { + if oldv, ok := old[k]; ok && oldv == v { + continue + } + switch envName(k) { + case envCaptureBody: + value, err := parseCaptureBody(k, v) + if err != nil { + errorf("central config failure: %s", err) + delete(attrs, k) + continue + } else { + updates = append(updates, func(cfg *instrumentationConfig) { + cfg.captureBody = value + }) + } + case envMaxSpans: + value, err := strconv.Atoi(v) + if err != nil { + errorf("central config failure: failed to parse %s: %s", k, err) + delete(attrs, k) + continue + } else { + updates = append(updates, func(cfg *instrumentationConfig) { + cfg.maxSpans = value + }) + } + case envTransactionSampleRate: + sampler, err := parseSampleRate(k, v) + if err != nil { + errorf("central config failure: %s", err) + delete(attrs, k) + continue + } else { + updates = append(updates, func(cfg *instrumentationConfig) { + cfg.sampler = sampler + }) + } + default: + warningf("central config failure: unsupported config: %s", k) + delete(attrs, k) + continue + } + debugf("central config update: updated %s to %s", k, v) + } + for k := range old { + if _, ok := attrs[k]; ok { + continue + } + updates = append(updates, func(cfg *instrumentationConfig) { + if f, ok := cfg.local[envName(k)]; ok { + f(&cfg.instrumentationConfigValues) + } + }) + debugf("central config update: reverted %s to local config", k) + } + if updates != nil { + remote := make(map[string]struct{}) + for k := range attrs { + remote[envName(k)] = struct{}{} + } + t.updateInstrumentationConfig(func(cfg *instrumentationConfig) { + cfg.remote = remote + for _, update := range updates { + update(cfg) + } + }) + } +} + +// instrumentationConfig returns the current instrumentationConfig. +// +// The returned value is immutable. +func (t *Tracer) instrumentationConfig() *instrumentationConfig { + config := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&t.instrumentationConfigInternal))) + return (*instrumentationConfig)(config) +} + +// setLocalInstrumentationConfig sets local transaction configuration with +// the specified environment variable key. +func (t *Tracer) setLocalInstrumentationConfig(envKey string, f func(cfg *instrumentationConfigValues)) { + t.updateInstrumentationConfig(func(cfg *instrumentationConfig) { + cfg.local[envKey] = f + if _, ok := cfg.remote[envKey]; !ok { + f(&cfg.instrumentationConfigValues) + } + }) +} + +func (t *Tracer) updateInstrumentationConfig(f func(cfg *instrumentationConfig)) { + for { + oldConfig := t.instrumentationConfig() + newConfig := *oldConfig + f(&newConfig) + if atomic.CompareAndSwapPointer( + (*unsafe.Pointer)(unsafe.Pointer(&t.instrumentationConfigInternal)), + unsafe.Pointer(oldConfig), + unsafe.Pointer(&newConfig), + ) { + return + } + } +} + +// instrumentationConfig holds current configuration values, as well as information +// required to revert from remote to local configuration. +type instrumentationConfig struct { + instrumentationConfigValues + + // local holds functions for setting instrumentationConfigValues to the most + // recently, locally specified configuration. + local map[string]func(*instrumentationConfigValues) + + // remote holds the environment variable keys for applied remote config. + remote map[string]struct{} +} + +// instrumentationConfigValues holds configuration that is accessible outside of the +// tracer loop, for instrumentation: StartTransaction, StartSpan, CaptureError, etc. +// +// NOTE(axw) when adding configuration here, you must also update `newTracer` to +// set the initial entry in instrumentationConfig.local, in order to properly reset +// to the local value, even if the default is the zero value. +type instrumentationConfigValues struct { + captureBody CaptureBodyMode + captureHeaders bool + maxSpans int + sampler Sampler + spanFramesMinDuration time.Duration + stackTraceLimit int + propagateLegacyHeader bool +} diff --git a/vendor/go.elastic.co/apm/context.go b/vendor/go.elastic.co/apm/context.go new file mode 100644 index 00000000000..9ab5e93f88a --- /dev/null +++ b/vendor/go.elastic.co/apm/context.go @@ -0,0 +1,256 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "fmt" + "net/http" + + "go.elastic.co/apm/internal/apmhttputil" + "go.elastic.co/apm/model" +) + +// Context provides methods for setting transaction and error context. +// +// NOTE this is entirely unrelated to the standard library's context.Context. +type Context struct { + model model.Context + request model.Request + requestBody model.RequestBody + requestSocket model.RequestSocket + response model.Response + user model.User + service model.Service + serviceFramework model.Framework + captureHeaders bool + captureBodyMask CaptureBodyMode +} + +func (c *Context) build() *model.Context { + switch { + case c.model.Request != nil: + case c.model.Response != nil: + case c.model.User != nil: + case c.model.Service != nil: + case len(c.model.Tags) != 0: + case len(c.model.Custom) != 0: + default: + return nil + } + return &c.model +} + +func (c *Context) reset() { + *c = Context{ + model: model.Context{ + Custom: c.model.Custom[:0], + Tags: c.model.Tags[:0], + }, + captureBodyMask: c.captureBodyMask, + request: model.Request{ + Headers: c.request.Headers[:0], + }, + response: model.Response{ + Headers: c.response.Headers[:0], + }, + } +} + +// SetTag calls SetLabel(key, value). +// +// SetTag is deprecated, and will be removed in a future major version. +func (c *Context) SetTag(key, value string) { + c.SetLabel(key, value) +} + +// SetLabel sets a label in the context. +// +// Invalid characters ('.', '*', and '"') in the key will be replaced with +// underscores. +// +// If the value is numerical or boolean, then it will be sent to the server +// as a JSON number or boolean; otherwise it will converted to a string, using +// `fmt.Sprint` if necessary. String values longer than 1024 characters will +// be truncated. +func (c *Context) SetLabel(key string, value interface{}) { + // Note that we do not attempt to de-duplicate the keys. + // This is OK, since json.Unmarshal will always take the + // final instance. + c.model.Tags = append(c.model.Tags, model.IfaceMapItem{ + Key: cleanLabelKey(key), + Value: makeLabelValue(value), + }) +} + +// SetCustom sets custom context. +// +// Invalid characters ('.', '*', and '"') in the key will be +// replaced with an underscore. The value may be any JSON-encodable +// value. +func (c *Context) SetCustom(key string, value interface{}) { + // Note that we do not attempt to de-duplicate the keys. + // This is OK, since json.Unmarshal will always take the + // final instance. + c.model.Custom = append(c.model.Custom, model.IfaceMapItem{ + Key: cleanLabelKey(key), + Value: value, + }) +} + +// SetFramework sets the framework name and version in the context. +// +// This is used for identifying the framework in which the context +// was created, such as Gin or Echo. +// +// If the name is empty, this is a no-op. If version is empty, then +// it will be set to "unspecified". +func (c *Context) SetFramework(name, version string) { + if name == "" { + return + } + if version == "" { + // Framework version is required. + version = "unspecified" + } + c.serviceFramework = model.Framework{ + Name: truncateString(name), + Version: truncateString(version), + } + c.service.Framework = &c.serviceFramework + c.model.Service = &c.service +} + +// SetHTTPRequest sets details of the HTTP request in the context. +// +// This function relates to server-side requests. Various proxy +// forwarding headers are taken into account to reconstruct the URL, +// and determining the client address. +// +// If the request URL contains user info, it will be removed and +// excluded from the URL's "full" field. +// +// If the request contains HTTP Basic Authentication, the username +// from that will be recorded in the context. Otherwise, if the +// request contains user info in the URL (i.e. a client-side URL), +// that will be used. +func (c *Context) SetHTTPRequest(req *http.Request) { + // Special cases to avoid calling into fmt.Sprintf in most cases. + var httpVersion string + switch { + case req.ProtoMajor == 1 && req.ProtoMinor == 1: + httpVersion = "1.1" + case req.ProtoMajor == 2 && req.ProtoMinor == 0: + httpVersion = "2.0" + default: + httpVersion = fmt.Sprintf("%d.%d", req.ProtoMajor, req.ProtoMinor) + } + + c.request = model.Request{ + Body: c.request.Body, + URL: apmhttputil.RequestURL(req), + Method: truncateString(req.Method), + HTTPVersion: httpVersion, + Cookies: req.Cookies(), + } + c.model.Request = &c.request + + if c.captureHeaders { + for k, values := range req.Header { + if k == "Cookie" { + // We capture cookies in the request structure. + continue + } + c.request.Headers = append(c.request.Headers, model.Header{ + Key: k, Values: values, + }) + } + } + + c.requestSocket = model.RequestSocket{ + Encrypted: req.TLS != nil, + RemoteAddress: apmhttputil.RemoteAddr(req), + } + if c.requestSocket != (model.RequestSocket{}) { + c.request.Socket = &c.requestSocket + } + + username, _, ok := req.BasicAuth() + if !ok && req.URL.User != nil { + username = req.URL.User.Username() + } + c.user.Username = truncateString(username) + if c.user.Username != "" { + c.model.User = &c.user + } +} + +// SetHTTPRequestBody sets the request body in context given a (possibly nil) +// BodyCapturer returned by Tracer.CaptureHTTPRequestBody. +func (c *Context) SetHTTPRequestBody(bc *BodyCapturer) { + if bc == nil || bc.captureBody&c.captureBodyMask == 0 { + return + } + if bc.setContext(&c.requestBody) { + c.request.Body = &c.requestBody + } +} + +// SetHTTPResponseHeaders sets the HTTP response headers in the context. +func (c *Context) SetHTTPResponseHeaders(h http.Header) { + if !c.captureHeaders { + return + } + for k, values := range h { + c.response.Headers = append(c.response.Headers, model.Header{ + Key: k, Values: values, + }) + } + if len(c.response.Headers) != 0 { + c.model.Response = &c.response + } +} + +// SetHTTPStatusCode records the HTTP response status code. +func (c *Context) SetHTTPStatusCode(statusCode int) { + c.response.StatusCode = statusCode + c.model.Response = &c.response +} + +// SetUserID sets the ID of the authenticated user. +func (c *Context) SetUserID(id string) { + c.user.ID = truncateString(id) + if c.user.ID != "" { + c.model.User = &c.user + } +} + +// SetUserEmail sets the email for the authenticated user. +func (c *Context) SetUserEmail(email string) { + c.user.Email = truncateString(email) + if c.user.Email != "" { + c.model.User = &c.user + } +} + +// SetUsername sets the username of the authenticated user. +func (c *Context) SetUsername(username string) { + c.user.Username = truncateString(username) + if c.user.Username != "" { + c.model.User = &c.user + } +} diff --git a/vendor/go.elastic.co/apm/doc.go b/vendor/go.elastic.co/apm/doc.go new file mode 100644 index 00000000000..6ca1ac8b26a --- /dev/null +++ b/vendor/go.elastic.co/apm/doc.go @@ -0,0 +1,21 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package apm provides an API for tracing +// transactions and capturing errors, sending the +// data to Elastic APM. +package apm // import "go.elastic.co/apm" diff --git a/vendor/go.elastic.co/apm/error.go b/vendor/go.elastic.co/apm/error.go new file mode 100644 index 00000000000..fcfd1b66543 --- /dev/null +++ b/vendor/go.elastic.co/apm/error.go @@ -0,0 +1,696 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "crypto/rand" + "fmt" + "net" + "os" + "reflect" + "syscall" + "time" + + "github.com/pkg/errors" + + "go.elastic.co/apm/internal/pkgerrorsutil" + "go.elastic.co/apm/model" + "go.elastic.co/apm/stacktrace" +) + +const ( + // maxErrorGraphSize is the maximum number of errors + // to report in an error tree. Once this number of + // nodes is reached, we will stop recursing through + // error causes. + maxErrorTreeNodes = 50 +) + +// Recovered creates an Error with t.NewError(err), where +// err is either v (if v implements error), or otherwise +// fmt.Errorf("%v", v). The value v is expected to have +// come from a panic. +func (t *Tracer) Recovered(v interface{}) *Error { + var e *Error + switch v := v.(type) { + case error: + e = t.NewError(v) + default: + e = t.NewError(fmt.Errorf("%v", v)) + } + return e +} + +// NewError returns a new Error with details taken from err. +// NewError will panic if called with a nil error. +// +// The exception message will be set to err.Error(). +// The exception module and type will be set to the package +// and type name of the cause of the error, respectively, +// where the cause has the same definition as given by +// github.com/pkg/errors. +// +// If err implements +// type interface { +// StackTrace() github.com/pkg/errors.StackTrace +// } +// or +// type interface { +// StackTrace() []stacktrace.Frame +// } +// then one of those will be used to set the error +// stacktrace. Otherwise, NewError will take a stacktrace. +// +// If err implements +// type interface {Type() string} +// then that will be used to set the error type. +// +// If err implements +// type interface {Code() string} +// or +// type interface {Code() float64} +// then one of those will be used to set the error code. +func (t *Tracer) NewError(err error) *Error { + if err == nil { + panic("NewError must be called with a non-nil error") + } + e := t.newError() + e.cause = err + e.err = err.Error() + rand.Read(e.ID[:]) // ignore error, can't do anything about it + initException(&e.exception, err, e.stackTraceLimit) + if len(e.exception.stacktrace) == 0 { + e.SetStacktrace(2) + } + return e +} + +// NewErrorLog returns a new Error for the given ErrorLogRecord. +// +// The resulting Error's stacktrace will not be set. Call the +// SetStacktrace method to set it, if desired. +// +// If r.Message is empty, "[EMPTY]" will be used. +func (t *Tracer) NewErrorLog(r ErrorLogRecord) *Error { + e := t.newError() + e.log = ErrorLogRecord{ + Message: truncateString(r.Message), + MessageFormat: truncateString(r.MessageFormat), + Level: truncateString(r.Level), + LoggerName: truncateString(r.LoggerName), + } + if e.log.Message == "" { + e.log.Message = "[EMPTY]" + } + e.cause = r.Error + e.err = e.log.Message + rand.Read(e.ID[:]) // ignore error, can't do anything about it + if r.Error != nil { + initException(&e.exception, r.Error, e.stackTraceLimit) + } + return e +} + +// newError returns a new Error associated with the Tracer. +func (t *Tracer) newError() *Error { + e, _ := t.errorDataPool.Get().(*ErrorData) + if e == nil { + e = &ErrorData{ + tracer: t, + Context: Context{ + captureBodyMask: CaptureBodyErrors, + }, + } + } + e.Timestamp = time.Now() + + instrumentationConfig := t.instrumentationConfig() + e.Context.captureHeaders = instrumentationConfig.captureHeaders + e.stackTraceLimit = instrumentationConfig.stackTraceLimit + + return &Error{ErrorData: e} +} + +// Error describes an error occurring in the monitored service. +type Error struct { + // ErrorData holds the error data. This field is set to nil when + // the error's Send method is called. + *ErrorData + + // cause holds the original error. + // + // It is accessible via the Cause method: + // https://godoc.org/github.com/pkg/errors#Cause + cause error + + // string holds original error string + err string +} + +// ErrorData holds the details for an error, and is embedded inside Error. +// When the error is sent, its ErrorData field will be set to nil. +type ErrorData struct { + tracer *Tracer + stackTraceLimit int + exception exceptionData + log ErrorLogRecord + logStacktrace []stacktrace.Frame + transactionSampled bool + transactionType string + + // ID is the unique identifier of the error. This is set by + // the various error constructors, and is exposed only so + // the error ID can be logged or displayed to the user. + ID ErrorID + + // TraceID is the unique identifier of the trace in which + // this error occurred. If the error is not associated with + // a trace, this will be the zero value. + TraceID TraceID + + // TransactionID is the unique identifier of the transaction + // in which this error occurred. If the error is not associated + // with a transaction, this will be the zero value. + TransactionID SpanID + + // ParentID is the unique identifier of the transaction or span + // in which this error occurred. If the error is not associated + // with a transaction or span, this will be the zero value. + ParentID SpanID + + // Culprit is the name of the function that caused the error. + // + // This is initially unset; if it remains unset by the time + // Send is invoked, and the error has a stacktrace, the first + // non-library frame in the stacktrace will be considered the + // culprit. + Culprit string + + // Timestamp records the time at which the error occurred. + // This is set when the Error object is created, but may + // be overridden any time before the Send method is called. + Timestamp time.Time + + // Handled records whether or not the error was handled. This + // is ignored by "log" errors with no associated error value. + Handled bool + + // Context holds the context for this error. + Context Context +} + +// Cause returns original error assigned to Error, nil if Error or Error.cause is nil. +// https://godoc.org/github.com/pkg/errors#Cause +func (e *Error) Cause() error { + if e != nil { + return e.cause + } + return nil +} + +// Error returns string message for error. +// if Error or Error.cause is nil, "[EMPTY]" will be used. +func (e *Error) Error() string { + if e != nil { + return e.err + } + return "[EMPTY]" +} + +// SetTransaction sets TraceID, TransactionID, and ParentID to the transaction's +// IDs, and records the transaction's Type and whether or not it was sampled. +// +// If any custom context has been recorded in tx, it will also be carried across +// to e, but will not override any custom context already recorded on e. +func (e *Error) SetTransaction(tx *Transaction) { + tx.mu.RLock() + traceContext := tx.traceContext + var txType string + var custom model.IfaceMap + if !tx.ended() { + txType = tx.Type + custom = tx.Context.model.Custom + } + tx.mu.RUnlock() + e.setSpanData(traceContext, traceContext.Span, txType, custom) +} + +// SetSpan sets TraceID, TransactionID, and ParentID to the span's IDs. +// +// There is no need to call both SetTransaction and SetSpan. If you do call +// both, then SetSpan must be called second in order to set the error's +// ParentID correctly. +// +// If any custom context has been recorded in s's transaction, it will +// also be carried across to e, but will not override any custom context +// already recorded on e. +func (e *Error) SetSpan(s *Span) { + var txType string + var custom model.IfaceMap + if s.tx != nil { + s.tx.mu.RLock() + if !s.tx.ended() { + txType = s.tx.Type + custom = s.tx.Context.model.Custom + } + s.tx.mu.RUnlock() + } + e.setSpanData(s.traceContext, s.transactionID, txType, custom) +} + +func (e *Error) setSpanData( + traceContext TraceContext, + transactionID SpanID, + transactionType string, + customContext model.IfaceMap, +) { + e.TraceID = traceContext.Trace + e.ParentID = traceContext.Span + e.TransactionID = transactionID + e.transactionSampled = traceContext.Options.Recorded() + if e.transactionSampled { + e.transactionType = transactionType + } + if n := len(customContext); n != 0 { + m := len(e.Context.model.Custom) + e.Context.model.Custom = append(e.Context.model.Custom, customContext...) + // If there was already custom context in e, shift the custom context from + // tx to the beginning of the slice so that e's context takes precedence. + if m != 0 { + copy(e.Context.model.Custom[n:], e.Context.model.Custom[:m]) + copy(e.Context.model.Custom[:n], customContext) + } + } +} + +// Send enqueues the error for sending to the Elastic APM server. +// +// Send will set e.ErrorData to nil, so the error must not be +// modified after Send returns. +func (e *Error) Send() { + if e == nil || e.sent() { + return + } + e.ErrorData.enqueue() + e.ErrorData = nil +} + +func (e *Error) sent() bool { + return e.ErrorData == nil +} + +func (e *ErrorData) enqueue() { + select { + case e.tracer.events <- tracerEvent{eventType: errorEvent, err: e}: + default: + // Enqueuing an error should never block. + e.tracer.statsMu.Lock() + e.tracer.stats.ErrorsDropped++ + e.tracer.statsMu.Unlock() + e.reset() + } +} + +func (e *ErrorData) reset() { + *e = ErrorData{ + tracer: e.tracer, + logStacktrace: e.logStacktrace[:0], + Context: e.Context, + exception: e.exception, + } + e.Context.reset() + e.exception.reset() + e.tracer.errorDataPool.Put(e) +} + +type exceptionData struct { + message string + stacktrace []stacktrace.Frame + cause []exceptionData + ErrorDetails +} + +func (e *exceptionData) reset() { + *e = exceptionData{ + cause: e.cause[:0], + stacktrace: e.stacktrace[:0], + ErrorDetails: ErrorDetails{ + attrs: e.ErrorDetails.attrs, + Cause: e.ErrorDetails.Cause[:0], + }, + } + for k := range e.attrs { + delete(e.attrs, k) + } +} + +func initException(e *exceptionData, err error, stackTraceLimit int) { + b := exceptionDataBuilder{stackTraceLimit: stackTraceLimit} + b.init(e, err) +} + +type exceptionDataBuilder struct { + stackTraceLimit int + errorCount int + pointerErrors map[uintptr]struct{} +} + +func (b *exceptionDataBuilder) init(e *exceptionData, err error) bool { + b.errorCount++ + reflectValue := reflect.ValueOf(err) + reflectType := reflectValue.Type() + switch reflectType.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: + // Prevent infinite recursion due to cyclic error causes. + ptrVal := reflectValue.Pointer() + if b.pointerErrors == nil { + b.pointerErrors = map[uintptr]struct{}{ptrVal: struct{}{}} + } else { + if _, ok := b.pointerErrors[ptrVal]; ok { + return false + } + b.pointerErrors[ptrVal] = struct{}{} + } + } + + e.message = truncateString(err.Error()) + if e.message == "" { + e.message = "[EMPTY]" + } + + namedType := reflectType + if reflectType.Name() == "" && reflectType.Kind() == reflect.Ptr { + namedType = reflectType.Elem() + } + e.Type.Name = namedType.Name() + e.Type.PackagePath = namedType.PkgPath() + + // If the error implements Type, use that to + // override the type name determined through + // reflection. + if err, ok := err.(interface { + Type() string + }); ok { + e.Type.Name = err.Type() + } + + // If the error implements a Code method, use + // that to set the exception code. + switch err := err.(type) { + case interface { + Code() string + }: + e.Code.String = err.Code() + case interface { + Code() float64 + }: + e.Code.Number = err.Code() + } + + // If the error implements an Unwrap or Cause method, use that to set the cause error. + // Unwrap is defined by errors wrapped using fmt.Errorf, while Cause is defined by + // errors wrapped using pkg/errors.Wrap. + switch err := err.(type) { + case interface{ Unwrap() error }: + if cause := err.Unwrap(); cause != nil { + e.ErrorDetails.Cause = append(e.ErrorDetails.Cause, cause) + } + case interface{ Cause() error }: + if cause := err.Cause(); cause != nil { + e.ErrorDetails.Cause = append(e.ErrorDetails.Cause, cause) + } + } + + // Run registered ErrorDetailers over the error. + for _, ed := range typeErrorDetailers[reflectType] { + ed.ErrorDetails(err, &e.ErrorDetails) + } + for _, ed := range errorDetailers { + ed.ErrorDetails(err, &e.ErrorDetails) + } + + e.Code.String = truncateString(e.Code.String) + e.Type.Name = truncateString(e.Type.Name) + e.Type.PackagePath = truncateString(e.Type.PackagePath) + b.initErrorStacktrace(&e.stacktrace, err) + + for _, err := range e.ErrorDetails.Cause { + if b.errorCount >= maxErrorTreeNodes { + break + } + var data exceptionData + if b.init(&data, err) { + e.cause = append(e.cause, data) + } + } + return true +} + +func (b *exceptionDataBuilder) initErrorStacktrace(out *[]stacktrace.Frame, err error) { + type internalStackTracer interface { + StackTrace() []stacktrace.Frame + } + type errorsStackTracer interface { + StackTrace() errors.StackTrace + } + switch stackTracer := err.(type) { + case internalStackTracer: + stackTrace := stackTracer.StackTrace() + if b.stackTraceLimit >= 0 && len(stackTrace) > b.stackTraceLimit { + stackTrace = stackTrace[:b.stackTraceLimit] + } + *out = append(*out, stackTrace...) + case errorsStackTracer: + stackTrace := stackTracer.StackTrace() + pkgerrorsutil.AppendStacktrace(stackTrace, out, b.stackTraceLimit) + } +} + +// SetStacktrace sets the stacktrace for the error, +// skipping the first skip number of frames, excluding +// the SetStacktrace function. +func (e *Error) SetStacktrace(skip int) { + out := &e.exception.stacktrace + if e.log.Message != "" { + out = &e.logStacktrace + } + *out = stacktrace.AppendStacktrace((*out)[:0], skip+1, e.stackTraceLimit) +} + +// ErrorLogRecord holds details of an error log record. +type ErrorLogRecord struct { + // Message holds the message for the log record, + // e.g. "failed to connect to %s". + // + // If this is empty, "[EMPTY]" will be used. + Message string + + // MessageFormat holds the non-interpolated format + // of the log record, e.g. "failed to connect to %s". + // + // This is optional. + MessageFormat string + + // Level holds the severity level of the log record. + // + // This is optional. + Level string + + // LoggerName holds the name of the logger used. + // + // This is optional. + LoggerName string + + // Error is an error associated with the log record. + // + // This is optional. + Error error +} + +// ErrorID uniquely identifies an error. +type ErrorID TraceID + +// String returns id in its hex-encoded format. +func (id ErrorID) String() string { + return TraceID(id).String() +} + +func init() { + RegisterErrorDetailer(ErrorDetailerFunc(func(err error, details *ErrorDetails) { + if errTemporary(err) { + details.SetAttr("temporary", true) + } + if errTimeout(err) { + details.SetAttr("timeout", true) + } + })) + RegisterTypeErrorDetailer(reflect.TypeOf(&net.OpError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { + opErr := err.(*net.OpError) + details.SetAttr("op", opErr.Op) + details.SetAttr("net", opErr.Net) + if opErr.Source != nil { + if addr := opErr.Source; addr != nil { + details.SetAttr("source", fmt.Sprintf("%s:%s", addr.Network(), addr.String())) + } + } + if opErr.Addr != nil { + if addr := opErr.Addr; addr != nil { + details.SetAttr("addr", fmt.Sprintf("%s:%s", addr.Network(), addr.String())) + } + } + details.Cause = append(details.Cause, opErr.Err) + })) + RegisterTypeErrorDetailer(reflect.TypeOf(&os.LinkError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { + linkErr := err.(*os.LinkError) + details.SetAttr("op", linkErr.Op) + details.SetAttr("old", linkErr.Old) + details.SetAttr("new", linkErr.New) + details.Cause = append(details.Cause, linkErr.Err) + })) + RegisterTypeErrorDetailer(reflect.TypeOf(&os.PathError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { + pathErr := err.(*os.PathError) + details.SetAttr("op", pathErr.Op) + details.SetAttr("path", pathErr.Path) + details.Cause = append(details.Cause, pathErr.Err) + })) + RegisterTypeErrorDetailer(reflect.TypeOf(&os.SyscallError{}), ErrorDetailerFunc(func(err error, details *ErrorDetails) { + syscallErr := err.(*os.SyscallError) + details.SetAttr("syscall", syscallErr.Syscall) + details.Cause = append(details.Cause, syscallErr.Err) + })) + RegisterTypeErrorDetailer(reflect.TypeOf(syscall.Errno(0)), ErrorDetailerFunc(func(err error, details *ErrorDetails) { + errno := err.(syscall.Errno) + details.Code.String = errnoName(errno) + if details.Code.String == "" { + details.Code.Number = float64(errno) + } + })) +} + +func errTemporary(err error) bool { + type temporaryError interface { + Temporary() bool + } + terr, ok := err.(temporaryError) + return ok && terr.Temporary() +} + +func errTimeout(err error) bool { + type timeoutError interface { + Timeout() bool + } + terr, ok := err.(timeoutError) + return ok && terr.Timeout() +} + +// RegisterTypeErrorDetailer registers e to be called for any error with +// the concrete type t. +// +// Each ErrorDetailer registered in this way will be called, in the order +// registered, for each error of type t created via Tracer.NewError or +// Tracer.NewErrorLog. +// +// RegisterTypeErrorDetailer must not be called during tracer operation; +// it is intended to be called at package init time. +func RegisterTypeErrorDetailer(t reflect.Type, e ErrorDetailer) { + typeErrorDetailers[t] = append(typeErrorDetailers[t], e) +} + +// RegisterErrorDetailer registers e in the global list of ErrorDetailers. +// +// Each ErrorDetailer registered in this way will be called, in the order +// registered, for each error created via Tracer.NewError or Tracer.NewErrorLog. +// +// RegisterErrorDetailer must not be called during tracer operation; it is +// intended to be called at package init time. +func RegisterErrorDetailer(e ErrorDetailer) { + errorDetailers = append(errorDetailers, e) +} + +var ( + typeErrorDetailers = make(map[reflect.Type][]ErrorDetailer) + errorDetailers []ErrorDetailer +) + +// ErrorDetails holds details of an error, which can be altered or +// extended by registering an ErrorDetailer with RegisterErrorDetailer +// or RegisterTypeErrorDetailer. +type ErrorDetails struct { + attrs map[string]interface{} + + // Type holds information about the error type, initialized + // with the type name and type package path using reflection. + Type struct { + // Name holds the error type name. + Name string + + // PackagePath holds the error type package path. + PackagePath string + } + + // Code holds an error code. + Code struct { + // String holds a string-based error code. If this is set, then Number is ignored. + // + // This field will be initialized to the result of calling an error's Code method, + // if the error implements the following interface: + // + // type interface StringCoder { + // Code() string + // } + String string + + // Number holds a numerical error code. This is ignored if String is set. + // + // This field will be initialized to the result of calling an error's Code + // method, if the error implements the following interface: + // + // type interface NumberCoder { + // Code() float64 + // } + Number float64 + } + + // Cause holds the errors that were the cause of this error. + Cause []error +} + +// SetAttr sets the attribute with key k to value v. +func (d *ErrorDetails) SetAttr(k string, v interface{}) { + if d.attrs == nil { + d.attrs = make(map[string]interface{}) + } + d.attrs[k] = v +} + +// ErrorDetailer defines an interface for altering or extending the ErrorDetails for an error. +// +// ErrorDetailers can be registered using the package-level functions RegisterErrorDetailer and +// RegisterTypeErrorDetailer. +type ErrorDetailer interface { + // ErrorDetails is called to update or alter details for err. + ErrorDetails(err error, details *ErrorDetails) +} + +// ErrorDetailerFunc is a function type implementing ErrorDetailer. +type ErrorDetailerFunc func(error, *ErrorDetails) + +// ErrorDetails calls f(err, details). +func (f ErrorDetailerFunc) ErrorDetails(err error, details *ErrorDetails) { + f(err, details) +} diff --git a/vendor/go.elastic.co/apm/error_unix.go b/vendor/go.elastic.co/apm/error_unix.go new file mode 100644 index 00000000000..e54f301612d --- /dev/null +++ b/vendor/go.elastic.co/apm/error_unix.go @@ -0,0 +1,30 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build !windows + +package apm + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +func errnoName(err syscall.Errno) string { + return unix.ErrnoName(err) +} diff --git a/vendor/go.elastic.co/apm/error_windows.go b/vendor/go.elastic.co/apm/error_windows.go new file mode 100644 index 00000000000..e95ac0f248d --- /dev/null +++ b/vendor/go.elastic.co/apm/error_windows.go @@ -0,0 +1,27 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "syscall" +) + +func errnoName(err syscall.Errno) string { + // There's currently no equivalent of unix.ErrnoName for Windows. + return "" +} diff --git a/vendor/go.elastic.co/apm/fmt.go b/vendor/go.elastic.co/apm/fmt.go new file mode 100644 index 00000000000..4d1ce03577b --- /dev/null +++ b/vendor/go.elastic.co/apm/fmt.go @@ -0,0 +1,85 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "context" + "fmt" + "io" +) + +// TraceFormatter returns a fmt.Formatter that can be used to +// format the identifiers of the transaction and span in ctx. +// +// The returned Formatter understands the following verbs: +// +// %v: trace ID, transaction ID, and span ID (if existing), space-separated +// the plus flag (%+v) adds field names, e.g. "trace.id=... transaction.id=..." +// %t: trace ID (hex-encoded, or empty string if non-existent) +// the plus flag (%+T) adds the field name, e.g. "trace.id=..." +// %x: transaction ID (hex-encoded, or empty string if non-existent) +// the plus flag (%+t) adds the field name, e.g. "transaction.id=..." +// %s: span ID (hex-encoded, or empty string if non-existent) +// the plus flag (%+s) adds the field name, e.g. "span.id=..." +func TraceFormatter(ctx context.Context) fmt.Formatter { + f := traceFormatter{tx: TransactionFromContext(ctx)} + if f.tx != nil { + f.span = SpanFromContext(ctx) + } + return f +} + +type traceFormatter struct { + tx *Transaction + span *Span +} + +func (t traceFormatter) Format(f fmt.State, c rune) { + switch c { + case 'v': + if t.tx != nil { + t.writeField(f, "trace.id", t.tx.TraceContext().Trace.String()) + f.Write([]byte{' '}) + t.writeField(f, "transaction.id", t.tx.TraceContext().Span.String()) + if t.span != nil { + f.Write([]byte{' '}) + t.writeField(f, "span.id", t.span.TraceContext().Span.String()) + } + } + case 't': + if t.tx != nil { + t.writeField(f, "trace.id", t.tx.TraceContext().Trace.String()) + } + case 'x': + if t.tx != nil { + t.writeField(f, "transaction.id", t.tx.TraceContext().Span.String()) + } + case 's': + if t.span != nil { + t.writeField(f, "span.id", t.span.TraceContext().Span.String()) + } + } +} + +func (t traceFormatter) writeField(f fmt.State, name, value string) { + if f.Flag('+') { + io.WriteString(f, name) + f.Write([]byte{'='}) + } + io.WriteString(f, value) +} diff --git a/vendor/go.elastic.co/apm/fnv.go b/vendor/go.elastic.co/apm/fnv.go new file mode 100644 index 00000000000..0741e224f26 --- /dev/null +++ b/vendor/go.elastic.co/apm/fnv.go @@ -0,0 +1,42 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// Based on Go's pkg/hash/fnv. +// +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package apm + +const ( + offset64 = 14695981039346656037 + prime64 = 1099511628211 +) + +type fnv1a uint64 + +func newFnv1a() fnv1a { + return offset64 +} + +func (f *fnv1a) add(s string) { + for i := 0; i < len(s); i++ { + *f ^= fnv1a(s[i]) + *f *= prime64 + } +} diff --git a/vendor/go.elastic.co/apm/go.mod b/vendor/go.elastic.co/apm/go.mod new file mode 100644 index 00000000000..c7f1c4bbe5f --- /dev/null +++ b/vendor/go.elastic.co/apm/go.mod @@ -0,0 +1,17 @@ +module go.elastic.co/apm + +require ( + github.com/armon/go-radix v1.0.0 + github.com/cucumber/godog v0.8.1 + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/elastic/go-sysinfo v1.1.1 + github.com/google/go-cmp v0.3.1 + github.com/pkg/errors v0.8.1 + github.com/prometheus/procfs v0.0.3 // indirect + github.com/santhosh-tekuri/jsonschema v1.2.4 + github.com/stretchr/testify v1.4.0 + go.elastic.co/fastjson v1.0.0 + golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e +) + +go 1.13 diff --git a/vendor/go.elastic.co/apm/go.sum b/vendor/go.elastic.co/apm/go.sum new file mode 100644 index 00000000000..6f89a3c9558 --- /dev/null +++ b/vendor/go.elastic.co/apm/go.sum @@ -0,0 +1,53 @@ +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= +github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elastic/go-sysinfo v1.1.1 h1:ZVlaLDyhVkDfjwPGU55CQRCRolNpc7P0BbyhhQZQmMI= +github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= +github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY= +github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= +github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.elastic.co/fastjson v1.0.0 h1:ooXV/ABvf+tBul26jcVViPT3sBir0PvXgibYB1IQQzg= +go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= diff --git a/vendor/go.elastic.co/apm/gocontext.go b/vendor/go.elastic.co/apm/gocontext.go new file mode 100644 index 00000000000..d238c065669 --- /dev/null +++ b/vendor/go.elastic.co/apm/gocontext.go @@ -0,0 +1,138 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "context" + + "go.elastic.co/apm/internal/apmcontext" +) + +// ContextWithSpan returns a copy of parent in which the given span +// is stored, associated with the key ContextSpanKey. +func ContextWithSpan(parent context.Context, s *Span) context.Context { + return apmcontext.ContextWithSpan(parent, s) +} + +// ContextWithTransaction returns a copy of parent in which the given +// transaction is stored, associated with the key ContextTransactionKey. +func ContextWithTransaction(parent context.Context, t *Transaction) context.Context { + return apmcontext.ContextWithTransaction(parent, t) +} + +// SpanFromContext returns the current Span in context, if any. The span must +// have been added to the context previously using ContextWithSpan, or the +// top-level StartSpan function. +func SpanFromContext(ctx context.Context) *Span { + value, _ := apmcontext.SpanFromContext(ctx).(*Span) + return value +} + +// TransactionFromContext returns the current Transaction in context, if any. +// The transaction must have been added to the context previously using +// ContextWithTransaction. +func TransactionFromContext(ctx context.Context) *Transaction { + value, _ := apmcontext.TransactionFromContext(ctx).(*Transaction) + return value +} + +// DetachedContext returns a new context detached from the lifetime +// of ctx, but which still returns the values of ctx. +// +// DetachedContext can be used to maintain the trace context required +// to correlate events, but where the operation is "fire-and-forget", +// and should not be affected by the deadline or cancellation of ctx. +func DetachedContext(ctx context.Context) context.Context { + return &detachedContext{Context: context.Background(), orig: ctx} +} + +type detachedContext struct { + context.Context + orig context.Context +} + +// Value returns c.orig.Value(key). +func (c *detachedContext) Value(key interface{}) interface{} { + return c.orig.Value(key) +} + +// StartSpan is equivalent to calling StartSpanOptions with a zero SpanOptions struct. +func StartSpan(ctx context.Context, name, spanType string) (*Span, context.Context) { + return StartSpanOptions(ctx, name, spanType, SpanOptions{}) +} + +// StartSpanOptions starts and returns a new Span within the sampled transaction +// and parent span in the context, if any. If the span isn't dropped, it will be +// stored in the resulting context. +// +// If opts.Parent is non-zero, its value will be used in preference to any parent +// span in ctx. +// +// StartSpanOptions always returns a non-nil Span. Its End method must be called +// when the span completes. +func StartSpanOptions(ctx context.Context, name, spanType string, opts SpanOptions) (*Span, context.Context) { + var span *Span + if opts.parent = SpanFromContext(ctx); opts.parent != nil { + if opts.parent.tx == nil && opts.parent.tracer != nil { + span = opts.parent.tracer.StartSpan(name, spanType, opts.parent.transactionID, opts) + } else { + span = opts.parent.tx.StartSpanOptions(name, spanType, opts) + } + } else { + tx := TransactionFromContext(ctx) + span = tx.StartSpanOptions(name, spanType, opts) + } + if !span.Dropped() { + ctx = ContextWithSpan(ctx, span) + } + return span, ctx +} + +// CaptureError returns a new Error related to the sampled transaction +// and span present in the context, if any, and sets its exception info +// from err. The Error.Handled field will be set to true, and a stacktrace +// set either from err, or from the caller. +// +// If the provided error is nil, then CaptureError will also return nil; +// otherwise a non-nil Error will always be returned. If there is no +// transaction or span in the context, then the returned Error's Send +// method will have no effect. +func CaptureError(ctx context.Context, err error) *Error { + if err == nil { + return nil + } + if span := SpanFromContext(ctx); span != nil { + if span.tracer == nil { + return &Error{cause: err, err: err.Error()} + } + e := span.tracer.NewError(err) + e.Handled = true + e.SetSpan(span) + return e + } else if tx := TransactionFromContext(ctx); tx != nil { + if tx.tracer == nil { + return &Error{cause: err, err: err.Error()} + } + e := tx.tracer.NewError(err) + e.Handled = true + e.SetTransaction(tx) + return e + } else { + return &Error{cause: err, err: err.Error()} + } +} diff --git a/vendor/go.elastic.co/apm/gofuzz.go b/vendor/go.elastic.co/apm/gofuzz.go new file mode 100644 index 00000000000..1fbbcaf3384 --- /dev/null +++ b/vendor/go.elastic.co/apm/gofuzz.go @@ -0,0 +1,270 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build gofuzz + +package apm + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/pkg/errors" + "github.com/santhosh-tekuri/jsonschema" + + "go.elastic.co/apm/internal/apmschema" + "go.elastic.co/apm/model" + "go.elastic.co/apm/stacktrace" + "go.elastic.co/fastjson" +) + +func Fuzz(data []byte) int { + type Payload struct { + Service *model.Service `json:"service"` + Process *model.Process `json:"process,omitempty"` + System *model.System `json:"system,omitempty"` + Errors []*model.Error `json:"errors"` + Transactions []*model.Transaction `json:"transactions"` + } + var payload Payload + if err := json.Unmarshal(data, &payload); err != nil { + return 0 + } + + tracer := DefaultTracer + tracer.Transport = &gofuzzTransport{} + tracer.SetCaptureBody(CaptureBodyAll) + + setContext := func(in *model.Context, out *Context) error { + if in == nil { + return nil + } + for k, v := range in.Tags { + out.SetLabel(k, v) + } + if in.Request != nil { + var body io.Reader + var postForm url.Values + if in.Request.Body != nil { + body = strings.NewReader(in.Request.Body.Raw) + if in.Request.Body.Form != nil { + postForm = in.Request.Body.Form + } + } + req, err := http.NewRequest(in.Request.Method, in.Request.URL.Full, body) + if err != nil { + return err + } + capturedBody := tracer.CaptureHTTPRequestBody(req) + if in.Request.Socket != nil { + req.RemoteAddr = in.Request.Socket.RemoteAddress + if in.Request.Socket.Encrypted { + req.TLS = new(tls.ConnectionState) + } + } + req.PostForm = postForm + if in.User != nil && in.User.Username != "" { + req.SetBasicAuth(in.User.Username, "") + } + + var major, minor int + if n, err := fmt.Sscanf(in.Request.HTTPVersion, "%d.%d", &major, &minor); err != nil { + return err + } else if n != 2 { + return errors.Errorf("invalid HTTP version %s", in.Request.HTTPVersion) + } + req.ProtoMajor = major + req.ProtoMinor = minor + + if in.Request.Headers != nil { + if in.Request.Headers.UserAgent != "" { + req.Header.Set("User-Agent", in.Request.Headers.UserAgent) + } + if in.Request.Headers.ContentType != "" { + req.Header.Set("Content-Type", in.Request.Headers.ContentType) + } + if in.Request.Headers.Cookie != "" { + for _, v := range strings.Split(in.Request.Headers.Cookie, ";") { + req.Header.Add("Cookie", v) + } + } + } + + out.SetHTTPRequest(req) + out.SetHTTPRequestBody(capturedBody) + } + if in.Response != nil { + out.SetHTTPStatusCode(in.Response.StatusCode) + if in.Response.Finished != nil { + out.SetHTTPResponseFinished(*in.Response.Finished) + } + if in.Response.HeadersSent != nil { + out.SetHTTPResponseHeadersSent(*in.Response.HeadersSent) + } + if in.Response.Headers != nil { + h := make(http.Header) + h.Set("Content-Type", in.Response.Headers.ContentType) + out.SetHTTPResponseHeaders(h) + } + } + return nil + } + + for _, t := range payload.Transactions { + if t == nil { + continue + } + tx := tracer.StartTransaction(t.Name, t.Type) + tx.Result = t.Result + tx.Timestamp = time.Time(t.Timestamp) + if setContext(t.Context, &tx.Context) != nil { + return 0 + } + for _, s := range t.Spans { + span := tx.StartSpan(s.Name, s.Type, nil) + span.Timestamp = tx.Timestamp.Add(time.Duration(s.Start * float64(time.Millisecond))) + if s.Context != nil && s.Context.Database != nil { + span.Context.SetDatabase(DatabaseSpanContext{ + Instance: s.Context.Database.Instance, + Statement: s.Context.Database.Statement, + Type: s.Context.Database.Type, + User: s.Context.Database.User, + }) + } + span.Duration = time.Duration(s.Duration * float64(time.Millisecond)) + span.End() + } + tx.Duration = time.Duration(t.Duration * float64(time.Millisecond)) + tx.End() + } + + for _, e := range payload.Errors { + if e == nil { + continue + } + var err *Error + if e.Log.Message != "" { + err = tracer.NewErrorLog(ErrorLogRecord{ + Message: e.Log.Message, + MessageFormat: e.Log.ParamMessage, + Level: e.Log.Level, + LoggerName: e.Log.LoggerName, + }) + } else { + ee := exceptionError{e.Exception} + if e.Exception.Code.String != "" { + err = tracer.NewError(stringCodeException{ee}) + } else { + err = tracer.NewError(float64CodeException{ee}) + } + } + if setContext(e.Context, &err.Context) != nil { + return 0 + } + err.Culprit = e.Culprit + err.Timestamp = time.Time(e.Timestamp) + err.Send() + } + + return 0 +} + +type float64CodeException struct { + exceptionError +} + +func (e float64CodeException) Code() float64 { + return e.x.Code.Number +} + +type stringCodeException struct { + exceptionError +} + +func (e stringCodeException) Code() string { + return e.x.Code.String +} + +type exceptionError struct { + x model.Exception +} + +func (e exceptionError) Type() string { + return e.x.Type +} + +func (e exceptionError) Error() string { + return e.x.Message +} + +func (e exceptionError) StackTrace() []stacktrace.Frame { + if len(e.x.Stacktrace) == 0 { + return nil + } + frames := make([]stacktrace.Frame, len(e.x.Stacktrace)) + for i, f := range e.x.Stacktrace { + frames[i].Function = f.Function + frames[i].File = f.File + frames[i].Line = f.Line + } + return frames +} + +type gofuzzTransport struct { + writer fastjson.Writer +} + +func (t *gofuzzTransport) SendErrors(ctx context.Context, payload *model.ErrorsPayload) error { + t.writer.Reset() + if err := payload.MarshalFastJSON(&t.writer); err != nil { + return err + } + t.validate(apmschema.Errors) + return nil +} + +func (t *gofuzzTransport) SendMetrics(ctx context.Context, payload *model.MetricsPayload) error { + t.writer.Reset() + if err := payload.MarshalFastJSON(&t.writer); err != nil { + return err + } + t.validate(apmschema.Metrics) + return nil +} + +func (t *gofuzzTransport) SendTransactions(ctx context.Context, payload *model.TransactionsPayload) error { + t.writer.Reset() + if err := payload.MarshalFastJSON(&t.writer); err != nil { + return err + } + t.validate(apmschema.Transactions) + return nil +} + +func (t *gofuzzTransport) validate(schema *jsonschema.Schema) { + if err := schema.Validate(bytes.NewReader(t.writer.Bytes())); err != nil { + panic(err) + } +} diff --git a/vendor/go.elastic.co/apm/internal/apmcontext/context.go b/vendor/go.elastic.co/apm/internal/apmcontext/context.go new file mode 100644 index 00000000000..e6ad7101937 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmcontext/context.go @@ -0,0 +1,78 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmcontext + +import "context" + +var ( + // ContextWithSpan takes a context and span and returns a new context + // from which the span can be extracted using SpanFromContext. + // + // ContextWithSpan is used by apm.ContextWithSpan. It is a + // variable to allow other packages, such as apmot, to replace it + // at package init time. + ContextWithSpan = DefaultContextWithSpan + + // ContextWithTransaction takes a context and transaction and returns + // a new context from which the transaction can be extracted using + // TransactionFromContext. + // + // ContextWithTransaction is used by apm.ContextWithTransaction. + // It is a variable to allow other packages, such as apmot, to replace + // it at package init time. + ContextWithTransaction = DefaultContextWithTransaction + + // SpanFromContext returns a span included in the context using + // ContextWithSpan. + // + // SpanFromContext is used by apm.SpanFromContext. It is a + // variable to allow other packages, such as apmot, to replace it + // at package init time. + SpanFromContext = DefaultSpanFromContext + + // TransactionFromContext returns a transaction included in the context + // using ContextWithTransaction. + // + // TransactionFromContext is used by apm.TransactionFromContext. + // It is a variable to allow other packages, such as apmot, to replace + // it at package init time. + TransactionFromContext = DefaultTransactionFromContext +) + +type spanKey struct{} +type transactionKey struct{} + +// DefaultContextWithSpan is the default value for ContextWithSpan. +func DefaultContextWithSpan(ctx context.Context, span interface{}) context.Context { + return context.WithValue(ctx, spanKey{}, span) +} + +// DefaultContextWithTransaction is the default value for ContextWithTransaction. +func DefaultContextWithTransaction(ctx context.Context, tx interface{}) context.Context { + return context.WithValue(ctx, transactionKey{}, tx) +} + +// DefaultSpanFromContext is the default value for SpanFromContext. +func DefaultSpanFromContext(ctx context.Context) interface{} { + return ctx.Value(spanKey{}) +} + +// DefaultTransactionFromContext is the default value for TransactionFromContext. +func DefaultTransactionFromContext(ctx context.Context) interface{} { + return ctx.Value(transactionKey{}) +} diff --git a/vendor/go.elastic.co/apm/internal/apmhostutil/container.go b/vendor/go.elastic.co/apm/internal/apmhostutil/container.go new file mode 100644 index 00000000000..ff734852b2d --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmhostutil/container.go @@ -0,0 +1,34 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhostutil + +import "go.elastic.co/apm/model" + +// Container returns information about the container running the process, or an +// error the container information could not be determined. +func Container() (*model.Container, error) { + return containerInfo() +} + +// Kubernetes returns information about the Kubernetes node and pod running +// the process, or an error if they could not be determined. This information +// does not include the KUBERNETES_* environment variables that can be set via +// the Downward API. +func Kubernetes() (*model.Kubernetes, error) { + return kubernetesInfo() +} diff --git a/vendor/go.elastic.co/apm/internal/apmhostutil/container_linux.go b/vendor/go.elastic.co/apm/internal/apmhostutil/container_linux.go new file mode 100644 index 00000000000..4ce16440549 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmhostutil/container_linux.go @@ -0,0 +1,156 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build linux + +package apmhostutil + +import ( + "bufio" + "errors" + "io" + "os" + "path" + "regexp" + "strings" + "sync" + + "go.elastic.co/apm/model" +) + +const ( + systemdScopeSuffix = ".scope" +) + +var ( + cgroupContainerInfoOnce sync.Once + cgroupContainerInfoError error + kubernetes *model.Kubernetes + container *model.Container + + kubepodsRegexp = regexp.MustCompile( + "" + + `(?:^/kubepods/[^/]+/pod([^/]+)/$)|` + + `(?:^/kubepods\.slice/kubepods-[^/]+\.slice/kubepods-[^/]+-pod([^/]+)\.slice/$)`, + ) + + containerIDRegexp = regexp.MustCompile( + "^" + + "[[:xdigit:]]{64}|" + + "[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4,}" + + "$", + ) +) + +func containerInfo() (*model.Container, error) { + container, _, err := cgroupContainerInfo() + return container, err +} + +func kubernetesInfo() (*model.Kubernetes, error) { + _, kubernetes, err := cgroupContainerInfo() + if err == nil && kubernetes == nil { + return nil, errors.New("could not determine kubernetes info") + } + return kubernetes, err +} + +func cgroupContainerInfo() (*model.Container, *model.Kubernetes, error) { + cgroupContainerInfoOnce.Do(func() { + cgroupContainerInfoError = func() error { + f, err := os.Open("/proc/self/cgroup") + if err != nil { + return err + } + defer f.Close() + + c, k, err := readCgroupContainerInfo(f) + if err != nil { + return err + } + if c == nil { + return errors.New("could not determine container info") + } + container = c + kubernetes = k + return nil + }() + }) + return container, kubernetes, cgroupContainerInfoError +} + +func readCgroupContainerInfo(r io.Reader) (*model.Container, *model.Kubernetes, error) { + var container *model.Container + var kubernetes *model.Kubernetes + s := bufio.NewScanner(r) + for s.Scan() { + fields := strings.SplitN(s.Text(), ":", 3) + if len(fields) != 3 { + continue + } + cgroupPath := fields[2] + + // Depending on the filesystem driver used for cgroup + // management, the paths in /proc/pid/cgroup will have + // one of the following formats in a Docker container: + // + // systemd: /system.slice/docker-.scope + // cgroupfs: /docker/ + // + // In a Kubernetes pod, the cgroup path will look like: + // + // systemd: /kubepods.slice/kubepods-.slice/kubepods--pod.slice/.scope + // cgroupfs: /kubepods//pod/ + // + dir, id := path.Split(cgroupPath) + if strings.HasSuffix(id, systemdScopeSuffix) { + id = id[:len(id)-len(systemdScopeSuffix)] + if dash := strings.IndexRune(id, '-'); dash != -1 { + id = id[dash+1:] + } + } + if match := kubepodsRegexp.FindStringSubmatch(dir); match != nil { + // By default, Kubernetes will set the hostname of + // the pod containers to the pod name. Users that + // override the name should use the Downard API to + // override the pod name. + hostname, _ := os.Hostname() + uid := match[1] + if uid == "" { + // Systemd cgroup driver is being used, + // so we need to unescape '_' back to '-'. + uid = strings.Replace(match[2], "_", "-", -1) + } + kubernetes = &model.Kubernetes{ + Pod: &model.KubernetesPod{ + Name: hostname, + UID: uid, + }, + } + // We don't check the contents of the last path segment + // when we've matched "^/kubepods"; we assume that it is + // a valid container ID. + container = &model.Container{ID: id} + } else if containerIDRegexp.MatchString(id) { + container = &model.Container{ID: id} + } + } + if err := s.Err(); err != nil { + return nil, nil, err + } + return container, kubernetes, nil +} diff --git a/vendor/go.elastic.co/apm/internal/apmhostutil/container_nonlinux.go b/vendor/go.elastic.co/apm/internal/apmhostutil/container_nonlinux.go new file mode 100644 index 00000000000..a02a44292fb --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmhostutil/container_nonlinux.go @@ -0,0 +1,36 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build !linux + +package apmhostutil + +import ( + "runtime" + + "github.com/pkg/errors" + + "go.elastic.co/apm/model" +) + +func containerInfo() (*model.Container, error) { + return nil, errors.Errorf("container ID computation not implemented for %s", runtime.GOOS) +} + +func kubernetesInfo() (*model.Kubernetes, error) { + return nil, errors.Errorf("kubernetes info gathering not implemented for %s", runtime.GOOS) +} diff --git a/vendor/go.elastic.co/apm/internal/apmhttputil/forwarded.go b/vendor/go.elastic.co/apm/internal/apmhttputil/forwarded.go new file mode 100644 index 00000000000..9001178150d --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmhttputil/forwarded.go @@ -0,0 +1,74 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttputil + +import ( + "strconv" + "strings" +) + +// ForwardedHeader holds information extracted from a "Forwarded" HTTP header. +type ForwardedHeader struct { + For string + Host string + Proto string +} + +// ParseForwarded parses a "Forwarded" HTTP header. +func ParseForwarded(f string) ForwardedHeader { + // We only consider the first value in the sequence, + // if there are multiple. Disregard everything after + // the first comma. + if comma := strings.IndexRune(f, ','); comma != -1 { + f = f[:comma] + } + var result ForwardedHeader + for f != "" { + field := f + if semi := strings.IndexRune(f, ';'); semi != -1 { + field = f[:semi] + f = f[semi+1:] + } else { + f = "" + } + eq := strings.IndexRune(field, '=') + if eq == -1 { + // Malformed field, ignore. + continue + } + key := strings.TrimSpace(field[:eq]) + value := strings.TrimSpace(field[eq+1:]) + if len(value) > 0 && value[0] == '"' { + var err error + value, err = strconv.Unquote(value) + if err != nil { + // Malformed, ignore + continue + } + } + switch strings.ToLower(key) { + case "for": + result.For = value + case "host": + result.Host = value + case "proto": + result.Proto = value + } + } + return result +} diff --git a/vendor/go.elastic.co/apm/internal/apmhttputil/remoteaddr.go b/vendor/go.elastic.co/apm/internal/apmhttputil/remoteaddr.go new file mode 100644 index 00000000000..e79400e6a8a --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmhttputil/remoteaddr.go @@ -0,0 +1,60 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttputil + +import ( + "net/http" + "strconv" +) + +// RemoteAddr returns the remote (peer) socket address for req, +// a server HTTP request. +func RemoteAddr(req *http.Request) string { + remoteAddr, _ := splitHost(req.RemoteAddr) + return remoteAddr +} + +// DestinationAddr returns the destination server address and port +// for req, a client HTTP request. +// +// If req.URL.Host contains a port it will be returned, and otherwise +// the default port according to req.URL.Scheme will be returned. If +// the included port is not a valid integer, or no port is included +// and the scheme is unknown, the returned port value will be zero. +func DestinationAddr(req *http.Request) (string, int) { + host, strport := splitHost(req.URL.Host) + var port int + if strport != "" { + port, _ = strconv.Atoi(strport) + } else { + port = SchemeDefaultPort(req.URL.Scheme) + } + return host, port +} + +// SchemeDefaultPort returns the default port for the given URI scheme, +// if known, or 0 otherwise. +func SchemeDefaultPort(scheme string) int { + switch scheme { + case "http": + return 80 + case "https": + return 443 + } + return 0 +} diff --git a/vendor/go.elastic.co/apm/internal/apmhttputil/url.go b/vendor/go.elastic.co/apm/internal/apmhttputil/url.go new file mode 100644 index 00000000000..45ea94181ad --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmhttputil/url.go @@ -0,0 +1,113 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttputil + +import ( + "net" + "net/http" + "strings" + + "go.elastic.co/apm/internal/apmstrings" + "go.elastic.co/apm/model" +) + +// RequestURL returns a model.URL for req. +// +// If req contains an absolute URI, the values will be split and +// sanitized, but no further processing performed. For all other +// requests (i.e. most server-side requests), we reconstruct the +// URL based on various proxy forwarding headers and other request +// attributes. +func RequestURL(req *http.Request) model.URL { + out := model.URL{ + Path: truncateString(req.URL.Path), + Search: truncateString(req.URL.RawQuery), + Hash: truncateString(req.URL.Fragment), + } + if req.URL.Host != "" { + // Absolute URI: client-side or proxy request, so ignore the + // headers. + hostname, port := splitHost(req.URL.Host) + out.Hostname = truncateString(hostname) + out.Port = truncateString(port) + out.Protocol = truncateString(req.URL.Scheme) + return out + } + + // This is a server-side request URI, which contains only the path. + // We synthesize the full URL by extracting the host and protocol + // from headers, or inferring from other properties. + var fullHost string + forwarded := ParseForwarded(req.Header.Get("Forwarded")) + if forwarded.Host != "" { + fullHost = forwarded.Host + out.Protocol = truncateString(forwarded.Proto) + } else if xfh := req.Header.Get("X-Forwarded-Host"); xfh != "" { + fullHost = xfh + } else { + fullHost = req.Host + } + hostname, port := splitHost(fullHost) + out.Hostname = truncateString(hostname) + out.Port = truncateString(port) + + // Protocol might be extracted from the Forwarded header. If it's not, + // look for various other headers. + if out.Protocol == "" { + if proto := req.Header.Get("X-Forwarded-Proto"); proto != "" { + out.Protocol = truncateString(proto) + } else if proto := req.Header.Get("X-Forwarded-Protocol"); proto != "" { + out.Protocol = truncateString(proto) + } else if proto := req.Header.Get("X-Url-Scheme"); proto != "" { + out.Protocol = truncateString(proto) + } else if req.Header.Get("Front-End-Https") == "on" { + out.Protocol = "https" + } else if req.Header.Get("X-Forwarded-Ssl") == "on" { + out.Protocol = "https" + } else if req.TLS != nil { + out.Protocol = "https" + } else { + // Assume http otherwise. + out.Protocol = "http" + } + } + return out +} + +func splitHost(in string) (host, port string) { + if strings.LastIndexByte(in, ':') == -1 { + // In the common (relative to other "errors") case that + // there is no colon, we can avoid allocations by not + // calling SplitHostPort. + return in, "" + } + host, port, err := net.SplitHostPort(in) + if err != nil { + if n := len(in); n > 1 && in[0] == '[' && in[n-1] == ']' { + in = in[1 : n-1] + } + return in, "" + } + return host, port +} + +func truncateString(s string) string { + // At the time of writing, all length limits are 1024. + s, _ = apmstrings.Truncate(s, 1024) + return s +} diff --git a/vendor/go.elastic.co/apm/internal/apmlog/logger.go b/vendor/go.elastic.co/apm/internal/apmlog/logger.go new file mode 100644 index 00000000000..d5cb1689338 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmlog/logger.go @@ -0,0 +1,173 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmlog + +import ( + "fmt" + "io" + "log" + "os" + "strings" + "sync" + "time" + + "go.elastic.co/fastjson" +) + +var ( + // DefaultLogger is the default Logger to use, if ELASTIC_APM_LOG_* are specified. + DefaultLogger Logger + + fastjsonPool = &sync.Pool{ + New: func() interface{} { + return &fastjson.Writer{} + }, + } +) + +func init() { + initDefaultLogger() +} + +func initDefaultLogger() { + fileStr := strings.TrimSpace(os.Getenv("ELASTIC_APM_LOG_FILE")) + if fileStr == "" { + return + } + + var logWriter io.Writer + switch strings.ToLower(fileStr) { + case "stdout": + logWriter = os.Stdout + case "stderr": + logWriter = os.Stderr + default: + f, err := os.Create(fileStr) + if err != nil { + log.Printf("failed to create %q: %s (disabling logging)", fileStr, err) + return + } + logWriter = &syncFile{File: f} + } + + logLevel := errorLevel + if levelStr := strings.TrimSpace(os.Getenv("ELASTIC_APM_LOG_LEVEL")); levelStr != "" { + level, err := parseLogLevel(levelStr) + if err != nil { + log.Printf("invalid ELASTIC_APM_LOG_LEVEL %q, falling back to %q", levelStr, logLevel) + } else { + logLevel = level + } + } + DefaultLogger = levelLogger{w: logWriter, level: logLevel} +} + +const ( + debugLevel logLevel = iota + infoLevel + warnLevel + errorLevel + noLevel +) + +type logLevel uint8 + +func (l logLevel) String() string { + switch l { + case debugLevel: + return "debug" + case infoLevel: + return "info" + case warnLevel: + return "warn" + case errorLevel: + return "error" + } + return "" +} + +func parseLogLevel(s string) (logLevel, error) { + switch strings.ToLower(s) { + case "debug": + return debugLevel, nil + case "info": + return infoLevel, nil + case "warn": + return warnLevel, nil + case "error": + return errorLevel, nil + } + return noLevel, fmt.Errorf("invalid log level string %q", s) +} + +// Logger provides methods for logging. +type Logger interface { + Debugf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + Warningf(format string, args ...interface{}) +} + +type levelLogger struct { + w io.Writer + level logLevel +} + +// Debugf logs a message with log.Printf, with a DEBUG prefix. +func (l levelLogger) Debugf(format string, args ...interface{}) { + l.logf(debugLevel, format, args...) +} + +// Errorf logs a message with log.Printf, with an ERROR prefix. +func (l levelLogger) Errorf(format string, args ...interface{}) { + l.logf(errorLevel, format, args...) +} + +// Warningf logs a message with log.Printf, with a WARNING prefix. +func (l levelLogger) Warningf(format string, args ...interface{}) { + l.logf(warnLevel, format, args...) +} + +func (l levelLogger) logf(level logLevel, format string, args ...interface{}) { + if level < l.level { + return + } + jw := fastjsonPool.Get().(*fastjson.Writer) + jw.RawString(`{"level":"`) + jw.RawString(level.String()) + jw.RawString(`","time":"`) + jw.Time(time.Now(), time.RFC3339) + jw.RawString(`","message":`) + jw.String(fmt.Sprintf(format, args...)) + jw.RawString("}\n") + l.w.Write(jw.Bytes()) + jw.Reset() + fastjsonPool.Put(jw) +} + +type syncFile struct { + mu sync.Mutex + *os.File +} + +// Write calls f.File.Write with f.mu held, to protect multiple Tracers +// in the same process from one another. +func (f *syncFile) Write(data []byte) (int, error) { + f.mu.Lock() + defer f.mu.Unlock() + return f.File.Write(data) +} diff --git a/vendor/go.elastic.co/apm/internal/apmschema/schema.go b/vendor/go.elastic.co/apm/internal/apmschema/schema.go new file mode 100644 index 00000000000..412c8498847 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmschema/schema.go @@ -0,0 +1,69 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmschema + +import ( + "log" + "path" + "path/filepath" + "runtime" + + "github.com/santhosh-tekuri/jsonschema" +) + +var ( + // Error is the compiled JSON Schema for an error. + Error *jsonschema.Schema + + // Metadata is the compiled JSON Schema for metadata. + Metadata *jsonschema.Schema + + // MetricSet is the compiled JSON Schema for a set of metrics. + MetricSet *jsonschema.Schema + + // Span is the compiled JSON Schema for a span. + Span *jsonschema.Schema + + // Transaction is the compiled JSON Schema for a transaction. + Transaction *jsonschema.Schema +) + +func init() { + _, filename, _, ok := runtime.Caller(0) + if !ok { + panic("source line info not available") + } + compiler := jsonschema.NewCompiler() + compiler.Draft = jsonschema.Draft4 + schemaDir := path.Join(filepath.ToSlash(filepath.Dir(filename)), "jsonschema") + if runtime.GOOS == "windows" { + schemaDir = "/" + schemaDir + } + compile := func(filepath string, out **jsonschema.Schema) { + schema, err := compiler.Compile("file://" + path.Join(schemaDir, filepath)) + if err != nil { + log.Fatal(err) + } + *out = schema + } + compile("errors/error.json", &Error) + compile("metadata.json", &Metadata) + compile("metricsets/metricset.json", &MetricSet) + compile("spans/span.json", &Span) + compile("transactions/transaction.json", &Transaction) +} diff --git a/vendor/go.elastic.co/apm/internal/apmschema/update.sh b/vendor/go.elastic.co/apm/internal/apmschema/update.sh new file mode 100644 index 00000000000..c47c86a25b5 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmschema/update.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +set -ex + +BRANCH=master + +FILES=( \ + "errors/error.json" \ + "sourcemaps/payload.json" \ + "spans/span.json" \ + "transactions/mark.json" \ + "transactions/transaction.json" \ + "metricsets/metricset.json" \ + "metricsets/sample.json" \ + "context.json" \ + "message.json" \ + "metadata.json" \ + "process.json" \ + "request.json" \ + "service.json" \ + "span_subtype.json" \ + "span_type.json" \ + "stacktrace_frame.json" \ + "system.json" \ + "tags.json" \ + "timestamp_epoch.json" \ + "transaction_name.json" \ + "transaction_type.json" \ + "user.json" \ +) + +mkdir -p jsonschema/errors jsonschema/transactions jsonschema/sourcemaps jsonschema/spans jsonschema/metricsets + +for i in "${FILES[@]}"; do + o=jsonschema/$i + curl -sf https://raw.githubusercontent.com/elastic/apm-server/${BRANCH}/docs/spec/${i} --compressed -o $o +done diff --git a/vendor/go.elastic.co/apm/internal/apmstrings/truncate.go b/vendor/go.elastic.co/apm/internal/apmstrings/truncate.go new file mode 100644 index 00000000000..0ed2b6d78af --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmstrings/truncate.go @@ -0,0 +1,31 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmstrings + +// Truncate returns s truncated at n runes, and the number +// of runes in the resulting string (<= n). +func Truncate(s string, n int) (string, int) { + var j int + for i := range s { + if j == n { + return s[:i], n + } + j++ + } + return s, j +} diff --git a/vendor/go.elastic.co/apm/internal/apmversion/version.go b/vendor/go.elastic.co/apm/internal/apmversion/version.go new file mode 100644 index 00000000000..0b3675b9420 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/apmversion/version.go @@ -0,0 +1,23 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmversion + +const ( + // AgentVersion is the Elastic APM Go Agent version. + AgentVersion = "1.7.2" +) diff --git a/vendor/go.elastic.co/apm/internal/configutil/duration.go b/vendor/go.elastic.co/apm/internal/configutil/duration.go new file mode 100644 index 00000000000..f29a3dbf693 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/configutil/duration.go @@ -0,0 +1,73 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package configutil + +import ( + "fmt" + "strconv" + "strings" + "time" + "unicode" +) + +// ParseDuration parses s as a duration, accepting a subset +// of the syntax supported by time.ParseDuration. +// +// Valid time units are "ms", "s", "m". +func ParseDuration(s string) (time.Duration, error) { + orig := s + var mul time.Duration = 1 + if strings.HasPrefix(s, "-") { + mul = -1 + s = s[1:] + } + + sep := -1 + for i, c := range s { + if sep == -1 { + if c < '0' || c > '9' { + sep = i + break + } + } + } + if sep == -1 { + return 0, fmt.Errorf("missing unit in duration %s (allowed units: ms, s, m)", orig) + } + + n, err := strconv.ParseInt(s[:sep], 10, 32) + if err != nil { + return 0, fmt.Errorf("invalid duration %s", orig) + } + switch s[sep:] { + case "ms": + mul *= time.Millisecond + case "s": + mul *= time.Second + case "m": + mul *= time.Minute + default: + for _, c := range s[sep:] { + if unicode.IsSpace(c) { + return 0, fmt.Errorf("invalid character %q in duration %s", c, orig) + } + } + return 0, fmt.Errorf("invalid unit in duration %s (allowed units: ms, s, m)", orig) + } + return mul * time.Duration(n), nil +} diff --git a/vendor/go.elastic.co/apm/internal/configutil/env.go b/vendor/go.elastic.co/apm/internal/configutil/env.go new file mode 100644 index 00000000000..04ac3cb97d4 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/configutil/env.go @@ -0,0 +1,95 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package configutil + +import ( + "os" + "strconv" + "time" + + "github.com/pkg/errors" + + "go.elastic.co/apm/internal/wildcard" +) + +// ParseDurationEnv gets the value of the environment variable envKey +// and, if set, parses it as a duration. If the environment variable +// is unset, defaultDuration is returned. +func ParseDurationEnv(envKey string, defaultDuration time.Duration) (time.Duration, error) { + value := os.Getenv(envKey) + if value == "" { + return defaultDuration, nil + } + d, err := ParseDuration(value) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envKey) + } + return d, nil +} + +// ParseSizeEnv gets the value of the environment variable envKey +// and, if set, parses it as a size. If the environment variable +// is unset, defaultSize is returned. +func ParseSizeEnv(envKey string, defaultSize Size) (Size, error) { + value := os.Getenv(envKey) + if value == "" { + return defaultSize, nil + } + s, err := ParseSize(value) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envKey) + } + return s, nil +} + +// ParseBoolEnv gets the value of the environment variable envKey +// and, if set, parses it as a boolean. If the environment variable +// is unset, defaultValue is returned. +func ParseBoolEnv(envKey string, defaultValue bool) (bool, error) { + value := os.Getenv(envKey) + if value == "" { + return defaultValue, nil + } + b, err := strconv.ParseBool(value) + if err != nil { + return false, errors.Wrapf(err, "failed to parse %s", envKey) + } + return b, nil +} + +// ParseListEnv gets the value of the environment variable envKey +// and, if set, parses it as a list separated by sep. If the environment +// variable is unset, defaultValue is returned. +func ParseListEnv(envKey, sep string, defaultValue []string) []string { + value := os.Getenv(envKey) + if value == "" { + return defaultValue + } + return ParseList(value, sep) +} + +// ParseWildcardPatternsEnv gets the value of the environment variable envKey +// and, if set, parses it as a list of wildcard patterns. If the environment +// variable is unset, defaultValue is returned. +func ParseWildcardPatternsEnv(envKey string, defaultValue wildcard.Matchers) wildcard.Matchers { + value := os.Getenv(envKey) + if value == "" { + return defaultValue + } + return ParseWildcardPatterns(value) +} diff --git a/vendor/go.elastic.co/apm/internal/configutil/list.go b/vendor/go.elastic.co/apm/internal/configutil/list.go new file mode 100644 index 00000000000..ceed90199d4 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/configutil/list.go @@ -0,0 +1,34 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package configutil + +import "strings" + +// ParseList parses s as a list of strings, separated by sep, +// and with whitespace trimmed from the list items, omitting +// empty items. +func ParseList(s, sep string) []string { + var list []string + for _, item := range strings.Split(s, sep) { + item = strings.TrimSpace(item) + if item != "" { + list = append(list, item) + } + } + return list +} diff --git a/vendor/go.elastic.co/apm/internal/configutil/size.go b/vendor/go.elastic.co/apm/internal/configutil/size.go new file mode 100644 index 00000000000..84f9657f857 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/configutil/size.go @@ -0,0 +1,105 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package configutil + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +// Size represents a size in bytes. +type Size int64 + +// Common power-of-two sizes. +const ( + Byte Size = 1 + KByte Size = 1024 + MByte Size = 1024 * 1024 + GByte Size = 1024 * 1024 * 1024 +) + +// Bytes returns s as a number of bytes. +func (s Size) Bytes() int64 { + return int64(s) +} + +// String returns s in its most compact string representation. +func (s Size) String() string { + if s == 0 { + return "0B" + } + switch { + case s%GByte == 0: + return fmt.Sprintf("%dGB", s/GByte) + case s%MByte == 0: + return fmt.Sprintf("%dMB", s/MByte) + case s%KByte == 0: + return fmt.Sprintf("%dKB", s/KByte) + default: + return fmt.Sprintf("%dB", s) + } +} + +// ParseSize parses s as a size, in bytes. +// +// Valid size units are "b", "kb", "mb", "gb". +func ParseSize(s string) (Size, error) { + orig := s + var mul Size = 1 + if strings.HasPrefix(s, "-") { + mul = -1 + s = s[1:] + } + + sep := -1 + for i, c := range s { + if sep == -1 { + if c < '0' || c > '9' { + sep = i + break + } + } + } + if sep == -1 { + return 0, fmt.Errorf("missing unit in size %s (allowed units: B, KB, MB, GB)", orig) + } + + n, err := strconv.ParseInt(s[:sep], 10, 32) + if err != nil { + return 0, fmt.Errorf("invalid size %s", orig) + } + switch strings.ToLower(s[sep:]) { + case "gb": + mul = GByte + case "mb": + mul = MByte + case "kb": + mul = KByte + case "b": + default: + for _, c := range s[sep:] { + if unicode.IsSpace(c) { + return 0, fmt.Errorf("invalid character %q in size %s", c, orig) + } + } + return 0, fmt.Errorf("invalid unit in size %s (allowed units: B, KB, MB, GB)", orig) + } + return mul * Size(n), nil +} diff --git a/vendor/go.elastic.co/apm/internal/configutil/wildcards.go b/vendor/go.elastic.co/apm/internal/configutil/wildcards.go new file mode 100644 index 00000000000..1c47383fc62 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/configutil/wildcards.go @@ -0,0 +1,62 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package configutil + +import ( + "strings" + + "go.elastic.co/apm/internal/wildcard" +) + +// ParseWildcardPatterns parses s as a comma-separated list of wildcard patterns, +// and returns wildcard.Matchers for each. +// +// Patterns support the "*" wildcard, which will match zero or more characters. +// A prefix of (?-i) treats the pattern case-sensitively, while a prefix of (?i) +// treats the pattern case-insensitively (the default). All other characters in +// the pattern are matched exactly. +func ParseWildcardPatterns(s string) wildcard.Matchers { + patterns := ParseList(s, ",") + matchers := make(wildcard.Matchers, len(patterns)) + for i, p := range patterns { + matchers[i] = ParseWildcardPattern(p) + } + return matchers +} + +// ParseWildcardPattern parses p as a wildcard pattern, returning a wildcard.Matcher. +// +// Patterns support the "*" wildcard, which will match zero or more characters. +// A prefix of (?-i) treats the pattern case-sensitively, while a prefix of (?i) +// treats the pattern case-insensitively (the default). All other characters in +// the pattern are matched exactly. +func ParseWildcardPattern(p string) *wildcard.Matcher { + const ( + caseSensitivePrefix = "(?-i)" + caseInsensitivePrefix = "(?i)" + ) + caseSensitive := wildcard.CaseInsensitive + switch { + case strings.HasPrefix(p, caseSensitivePrefix): + caseSensitive = wildcard.CaseSensitive + p = p[len(caseSensitivePrefix):] + case strings.HasPrefix(p, caseInsensitivePrefix): + p = p[len(caseInsensitivePrefix):] + } + return wildcard.NewMatcher(p, caseSensitive) +} diff --git a/vendor/go.elastic.co/apm/internal/iochan/doc.go b/vendor/go.elastic.co/apm/internal/iochan/doc.go new file mode 100644 index 00000000000..4898c05bf6e --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/iochan/doc.go @@ -0,0 +1,19 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package iochan provides a channel-based io.Reader. +package iochan diff --git a/vendor/go.elastic.co/apm/internal/iochan/reader.go b/vendor/go.elastic.co/apm/internal/iochan/reader.go new file mode 100644 index 00000000000..0025667306b --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/iochan/reader.go @@ -0,0 +1,110 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package iochan + +import ( + "sync" +) + +// Reader is a channel-based io.Reader. +// +// Reader is safe for use in a single producer, single consumer pattern. +type Reader struct { + // C can be used for receiving read requests. + // + // Once a read request is received, it must be responded + // to, in order to avoid blocking the reader. + C <-chan ReadRequest + c chan ReadRequest + resp chan readResponse + + mu sync.Mutex + readClosed bool + writeClosed bool + readErr error +} + +// NewReader returns a new Reader. +func NewReader() *Reader { + c := make(chan ReadRequest) + return &Reader{ + C: c, + c: c, + resp: make(chan readResponse, 1), + } +} + +// CloseWrite closes reader.C. CloseWrite is idempotent, +// but must not be called concurrently with Read. +func (r *Reader) CloseWrite() { + r.mu.Lock() + defer r.mu.Unlock() + if !r.writeClosed { + r.writeClosed = true + close(r.c) + } +} + +// CloseRead closes the reader such that any waiting or future +// Reads return err. Additional calls to CloseRead have no +// effect. CloseRead must not be called concurrently with +// ReadRequest.Respond. +func (r *Reader) CloseRead(err error) error { + r.mu.Lock() + defer r.mu.Unlock() + if !r.readClosed { + r.readClosed = true + r.readErr = err + close(r.resp) + } + return nil +} + +// Read sends a ReadRequest to r.C containing buf, and returns the +// response sent by the channel consumer via the read request's +// Response method. +func (r *Reader) Read(buf []byte) (int, error) { + select { + case <-r.resp: + return 0, r.readErr + case r.c <- ReadRequest{Buf: buf, response: r.resp}: + } + resp, ok := <-r.resp + if !ok { + return 0, r.readErr + } + return resp.N, resp.Err +} + +// ReadRequest holds the buffer and response channel for a read request. +type ReadRequest struct { + // Buf is the read buffer into which data should be read. + Buf []byte + response chan<- readResponse +} + +// Respond responds to the Read request. Respond must not be called +// concurrently with Reader.Close. +func (rr *ReadRequest) Respond(n int, err error) { + rr.response <- readResponse{N: n, Err: err} +} + +type readResponse struct { + N int + Err error +} diff --git a/vendor/go.elastic.co/apm/internal/pkgerrorsutil/pkgerrors.go b/vendor/go.elastic.co/apm/internal/pkgerrorsutil/pkgerrors.go new file mode 100644 index 00000000000..01ace8b18d3 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/pkgerrorsutil/pkgerrors.go @@ -0,0 +1,60 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package pkgerrorsutil + +import ( + "reflect" + "runtime" + "unsafe" + + "github.com/pkg/errors" + + "go.elastic.co/apm/stacktrace" +) + +var ( + uintptrType = reflect.TypeOf(uintptr(0)) + runtimeFrameType = reflect.TypeOf(runtime.Frame{}) + errorsStackTraceUintptr = uintptrType.ConvertibleTo(reflect.TypeOf(*new(errors.Frame))) + errorsStackTraceFrame = reflect.TypeOf(*new(errors.Frame)).ConvertibleTo(runtimeFrameType) +) + +// AppendStacktrace appends stack frames to out, based on stackTrace. +func AppendStacktrace(stackTrace errors.StackTrace, out *[]stacktrace.Frame, limit int) { + // github.com/pkg/errors 0.8.x and earlier represent + // stack frames as uintptr; 0.9.0 and later represent + // them as runtime.Frames. + // + // TODO(axw) drop support for older github.com/pkg/errors + // versions when we release go.elastic.co/apm v2.0.0. + if errorsStackTraceUintptr { + pc := make([]uintptr, len(stackTrace)) + for i, frame := range stackTrace { + pc[i] = *(*uintptr)(unsafe.Pointer(&frame)) + } + *out = stacktrace.AppendCallerFrames(*out, pc, limit) + } else if errorsStackTraceFrame { + if limit >= 0 && len(stackTrace) > limit { + stackTrace = stackTrace[:limit] + } + for _, frame := range stackTrace { + rf := (*runtime.Frame)(unsafe.Pointer(&frame)) + *out = append(*out, stacktrace.RuntimeFrame(*rf)) + } + } +} diff --git a/vendor/go.elastic.co/apm/internal/ringbuffer/buffer.go b/vendor/go.elastic.co/apm/internal/ringbuffer/buffer.go new file mode 100644 index 00000000000..4994172d144 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/ringbuffer/buffer.go @@ -0,0 +1,142 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package ringbuffer + +import ( + "bytes" + "encoding/binary" + "io" + "io/ioutil" +) + +// BlockHeaderSize is the size of the block header, in bytes. +const BlockHeaderSize = 5 + +// BlockTag is a block tag, which can be used for classification. +type BlockTag uint8 + +// BlockHeader holds a fixed-size block header. +type BlockHeader struct { + // Tag is the block's tag. + Tag BlockTag + + // Size is the size of the block data, in bytes. + Size uint32 +} + +// Buffer is a ring buffer of byte blocks. +type Buffer struct { + buf []byte + headerbuf [BlockHeaderSize]byte + len int + write int + read int + + // Evicted will be called when an old block is evicted to make place for a new one. + Evicted func(BlockHeader) +} + +// New returns a new Buffer with the given size in bytes. +func New(size int) *Buffer { + return &Buffer{ + buf: make([]byte, size), + Evicted: func(BlockHeader) {}, + } +} + +// Len returns the number of bytes currently in the buffer, including +// block-accounting bytes. +func (b *Buffer) Len() int { + return b.len +} + +// Cap returns the capacity of the buffer. +func (b *Buffer) Cap() int { + return len(b.buf) +} + +// WriteBlockTo writes the oldest block in b to w, returning the block header and the number of bytes written to w. +func (b *Buffer) WriteBlockTo(w io.Writer) (header BlockHeader, written int64, err error) { + if b.len == 0 { + return header, 0, io.EOF + } + if n := copy(b.headerbuf[:], b.buf[b.read:]); n < len(b.headerbuf) { + b.read = copy(b.headerbuf[n:], b.buf[:]) + } else { + b.read = (b.read + n) % b.Cap() + } + b.len -= len(b.headerbuf) + header.Tag = BlockTag(b.headerbuf[0]) + header.Size = binary.LittleEndian.Uint32(b.headerbuf[1:]) + size := int(header.Size) + + if b.read+size > b.Cap() { + tail := b.buf[b.read:] + n, err := w.Write(tail) + if err != nil { + b.read = (b.read + size) % b.Cap() + b.len -= size + len(b.headerbuf) + return header, int64(n), err + } + size -= n + written = int64(n) + b.read = 0 + b.len -= n + } + n, err := w.Write(b.buf[b.read : b.read+size]) + if err != nil { + return header, written + int64(n), err + } + written += int64(n) + b.read = (b.read + size) % b.Cap() + b.len -= size + return header, written, nil +} + +// WriteBlock writes p as a block to b, with tag t. +// +// If len(p)+BlockHeaderSize > b.Cap(), bytes.ErrTooLarge will be returned. +// If the buffer does not currently have room for the block, then the +// oldest blocks will be evicted until enough room is available. +func (b *Buffer) WriteBlock(p []byte, tag BlockTag) (int, error) { + lenp := len(p) + if lenp+BlockHeaderSize > b.Cap() { + return 0, bytes.ErrTooLarge + } + for lenp+BlockHeaderSize > b.Cap()-b.Len() { + header, _, err := b.WriteBlockTo(ioutil.Discard) + if err != nil { + return 0, err + } + b.Evicted(header) + } + b.headerbuf[0] = uint8(tag) + binary.LittleEndian.PutUint32(b.headerbuf[1:], uint32(lenp)) + if n := copy(b.buf[b.write:], b.headerbuf[:]); n < len(b.headerbuf) { + b.write = copy(b.buf, b.headerbuf[n:]) + } else { + b.write = (b.write + n) % b.Cap() + } + if n := copy(b.buf[b.write:], p); n < lenp { + b.write = copy(b.buf, p[n:]) + } else { + b.write = (b.write + n) % b.Cap() + } + b.len += lenp + BlockHeaderSize + return lenp, nil +} diff --git a/vendor/go.elastic.co/apm/internal/ringbuffer/doc.go b/vendor/go.elastic.co/apm/internal/ringbuffer/doc.go new file mode 100644 index 00000000000..897578849c3 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/ringbuffer/doc.go @@ -0,0 +1,22 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package ringbuffer provides a ring buffer for storing blocks of bytes. +// Bytes are written and read in discrete blocks. If the buffer becomes +// full, then writing to it will evict the oldest blocks until there is +// space for a new one. +package ringbuffer diff --git a/vendor/go.elastic.co/apm/internal/wildcard/doc.go b/vendor/go.elastic.co/apm/internal/wildcard/doc.go new file mode 100644 index 00000000000..07645ad995b --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/wildcard/doc.go @@ -0,0 +1,19 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package wildcard provides a fast, zero-allocation wildcard matcher. +package wildcard diff --git a/vendor/go.elastic.co/apm/internal/wildcard/matcher.go b/vendor/go.elastic.co/apm/internal/wildcard/matcher.go new file mode 100644 index 00000000000..e406eb6eb8d --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/wildcard/matcher.go @@ -0,0 +1,142 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package wildcard + +import ( + "strings" + "unicode" + "unicode/utf8" +) + +// CaseSensitivity controls the case sensitivity of matching. +type CaseSensitivity bool + +// CaseSensitivity values. +const ( + CaseSensitive CaseSensitivity = true + CaseInsensitive CaseSensitivity = false +) + +// NewMatcher constructs a new wildcard matcher for the given pattern. +// +// If p is the empty string, it will match only the empty string. +// If p is not a valid UTF-8 string, matching behaviour is undefined. +func NewMatcher(p string, caseSensitive CaseSensitivity) *Matcher { + parts := strings.Split(p, "*") + m := &Matcher{ + wildcardBegin: strings.HasPrefix(p, "*"), + wildcardEnd: strings.HasSuffix(p, "*"), + caseSensitive: caseSensitive, + } + for _, part := range parts { + if part == "" { + continue + } + if !m.caseSensitive { + part = strings.ToLower(part) + } + m.parts = append(m.parts, part) + } + return m +} + +// Matcher matches strings against a wildcard pattern with configurable case sensitivity. +type Matcher struct { + parts []string + wildcardBegin bool + wildcardEnd bool + caseSensitive CaseSensitivity +} + +// Match reports whether s matches m's wildcard pattern. +func (m *Matcher) Match(s string) bool { + if len(m.parts) == 0 && !m.wildcardBegin && !m.wildcardEnd { + return s == "" + } + if len(m.parts) == 1 && !m.wildcardBegin && !m.wildcardEnd { + if m.caseSensitive { + return s == m.parts[0] + } + return len(s) == len(m.parts[0]) && hasPrefixLower(s, m.parts[0]) == 0 + } + parts := m.parts + if !m.wildcardEnd && len(parts) > 0 { + part := parts[len(parts)-1] + if m.caseSensitive { + if !strings.HasSuffix(s, part) { + return false + } + } else { + if len(s) < len(part) { + return false + } + if hasPrefixLower(s[len(s)-len(part):], part) != 0 { + return false + } + } + parts = parts[:len(parts)-1] + } + for i, part := range parts { + j := -1 + if m.caseSensitive { + if i > 0 || m.wildcardBegin { + j = strings.Index(s, part) + } else { + if !strings.HasPrefix(s, part) { + return false + } + j = 0 + } + } else { + off := 0 + for j == -1 && len(s)-off >= len(part) { + skip := hasPrefixLower(s[off:], part) + if skip == 0 { + j = off + } else { + if i == 0 && !m.wildcardBegin { + return false + } + off += skip + } + } + } + if j == -1 { + return false + } + s = s[j+len(part):] + } + return true +} + +// hasPrefixLower reports whether or not s begins with prefixLower, +// returning 0 if it does, and the number of bytes representing the +// first rune in s otherwise. +func hasPrefixLower(s, prefixLower string) (skip int) { + var firstSize int + for i, r := range prefixLower { + r2, size := utf8.DecodeRuneInString(s[i:]) + if firstSize == 0 { + firstSize = size + } + if r2 != r && r2 != unicode.ToUpper(r) { + return firstSize + } + } + return 0 +} diff --git a/vendor/go.elastic.co/apm/internal/wildcard/matchers.go b/vendor/go.elastic.co/apm/internal/wildcard/matchers.go new file mode 100644 index 00000000000..5d443701913 --- /dev/null +++ b/vendor/go.elastic.co/apm/internal/wildcard/matchers.go @@ -0,0 +1,31 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package wildcard + +// Matchers is a slice of Matcher, matching any of the contained matchers. +type Matchers []*Matcher + +// MatchAny returns true iff any of the matchers returns true. +func (m Matchers) MatchAny(s string) bool { + for _, m := range m { + if m.Match(s) { + return true + } + } + return false +} diff --git a/vendor/go.elastic.co/apm/logger.go b/vendor/go.elastic.co/apm/logger.go new file mode 100644 index 00000000000..8e30e5918a8 --- /dev/null +++ b/vendor/go.elastic.co/apm/logger.go @@ -0,0 +1,54 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +// Logger is an interface for logging, used by the tracer +// to log tracer errors and other interesting events. +type Logger interface { + // Debugf logs a message at debug level. + Debugf(format string, args ...interface{}) + + // Errorf logs a message at error level. + Errorf(format string, args ...interface{}) +} + +// WarningLogger extends Logger with a Warningf method. +// +// TODO(axw) this will be removed in v2.0.0, and the +// Warningf method will be added directly to Logger. +type WarningLogger interface { + Logger + + // Warningf logs a message at warning level. + Warningf(format string, args ...interface{}) +} + +func makeWarningLogger(l Logger) WarningLogger { + if wl, ok := l.(WarningLogger); ok { + return wl + } + return debugWarningLogger{Logger: l} +} + +type debugWarningLogger struct { + Logger +} + +func (l debugWarningLogger) Warningf(format string, args ...interface{}) { + l.Debugf(format, args...) +} diff --git a/vendor/go.elastic.co/apm/metrics.go b/vendor/go.elastic.co/apm/metrics.go new file mode 100644 index 00000000000..6f5ecb7e201 --- /dev/null +++ b/vendor/go.elastic.co/apm/metrics.go @@ -0,0 +1,161 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "context" + "sort" + "strings" + "sync" + + "go.elastic.co/apm/internal/wildcard" + "go.elastic.co/apm/model" +) + +// Metrics holds a set of metrics. +type Metrics struct { + disabled wildcard.Matchers + + mu sync.Mutex + metrics []*model.Metrics + + // transactionGroupMetrics holds metrics which are scoped to transaction + // groups, and are not sorted according to their labels. + transactionGroupMetrics []*model.Metrics +} + +func (m *Metrics) reset() { + m.metrics = m.metrics[:0] + m.transactionGroupMetrics = m.transactionGroupMetrics[:0] +} + +// MetricLabel is a name/value pair for labeling metrics. +type MetricLabel struct { + // Name is the label name. + Name string + + // Value is the label value. + Value string +} + +// MetricsGatherer provides an interface for gathering metrics. +type MetricsGatherer interface { + // GatherMetrics gathers metrics and adds them to m. + // + // If ctx.Done() is signaled, gathering should be aborted and + // ctx.Err() returned. If GatherMetrics returns an error, it + // will be logged, but otherwise there is no effect; the + // implementation must take care not to leave m in an invalid + // state due to errors. + GatherMetrics(ctx context.Context, m *Metrics) error +} + +// GatherMetricsFunc is a function type implementing MetricsGatherer. +type GatherMetricsFunc func(context.Context, *Metrics) error + +// GatherMetrics calls f(ctx, m). +func (f GatherMetricsFunc) GatherMetrics(ctx context.Context, m *Metrics) error { + return f(ctx, m) +} + +// Add adds a metric with the given name, labels, and value, +// The labels are expected to be sorted lexicographically. +func (m *Metrics) Add(name string, labels []MetricLabel, value float64) { + m.addMetric(name, labels, model.Metric{Value: value}) +} + +func (m *Metrics) addMetric(name string, labels []MetricLabel, metric model.Metric) { + if m.disabled.MatchAny(name) { + return + } + m.mu.Lock() + defer m.mu.Unlock() + + var metrics *model.Metrics + results := make([]int, len(m.metrics)) + i := sort.Search(len(m.metrics), func(j int) bool { + results[j] = compareLabels(m.metrics[j].Labels, labels) + return results[j] >= 0 + }) + if i < len(results) && results[i] == 0 { + // labels are equal + metrics = m.metrics[i] + } else { + var modelLabels model.StringMap + if len(labels) > 0 { + modelLabels = make(model.StringMap, len(labels)) + for i, l := range labels { + modelLabels[i] = model.StringMapItem{ + Key: l.Name, Value: l.Value, + } + } + } + metrics = &model.Metrics{ + Labels: modelLabels, + Samples: make(map[string]model.Metric), + } + if i == len(results) { + m.metrics = append(m.metrics, metrics) + } else { + m.metrics = append(m.metrics, nil) + copy(m.metrics[i+1:], m.metrics[i:]) + m.metrics[i] = metrics + } + } + metrics.Samples[name] = metric +} + +func compareLabels(a model.StringMap, b []MetricLabel) int { + na, nb := len(a), len(b) + n := na + if na > nb { + n = nb + } + for i := 0; i < n; i++ { + la, lb := a[i], b[i] + d := strings.Compare(la.Key, lb.Name) + if d == 0 { + d = strings.Compare(la.Value, lb.Value) + } + if d != 0 { + return d + } + } + switch { + case na < nb: + return -1 + case na > nb: + return 1 + } + return 0 +} + +func gatherMetrics(ctx context.Context, g MetricsGatherer, m *Metrics, logger Logger) { + defer func() { + if r := recover(); r != nil { + if logger != nil { + logger.Debugf("%T.GatherMetrics panicked: %s", g, r) + } + } + }() + if err := g.GatherMetrics(ctx, m); err != nil { + if logger != nil && err != context.Canceled { + logger.Debugf("%T.GatherMetrics failed: %s", g, err) + } + } +} diff --git a/vendor/go.elastic.co/apm/model/doc.go b/vendor/go.elastic.co/apm/model/doc.go new file mode 100644 index 00000000000..3fe20d31a65 --- /dev/null +++ b/vendor/go.elastic.co/apm/model/doc.go @@ -0,0 +1,21 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package model provides the Elastic APM model types. +// +// https://www.elastic.co/guide/en/apm/server/current/intake-api.html +package model diff --git a/vendor/go.elastic.co/apm/model/generate.sh b/vendor/go.elastic.co/apm/model/generate.sh new file mode 100644 index 00000000000..f403a8dd5a9 --- /dev/null +++ b/vendor/go.elastic.co/apm/model/generate.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -e +go run go.elastic.co/fastjson/cmd/generate-fastjson -f -o marshal_fastjson.go . +exec go-licenser marshal_fastjson.go diff --git a/vendor/go.elastic.co/apm/model/gofuzz.go b/vendor/go.elastic.co/apm/model/gofuzz.go new file mode 100644 index 00000000000..04fb279e252 --- /dev/null +++ b/vendor/go.elastic.co/apm/model/gofuzz.go @@ -0,0 +1,82 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build gofuzz + +package model + +import ( + "bytes" + "encoding/json" + + "go.elastic.co/apm/internal/apmschema" + "go.elastic.co/fastjson" +) + +func Fuzz(data []byte) int { + type Payload struct { + Service *Service `json:"service"` + Process *Process `json:"process,omitempty"` + System *System `json:"system,omitempty"` + Errors []*Error `json:"errors"` + Transactions []Transaction `json:"transactions"` + } + + var payload Payload + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&payload); err != nil { + return -1 + } + raw := make(map[string]interface{}) + if err := json.Unmarshal(data, &raw); err != nil { + return -1 + } + + if len(payload.Errors) != 0 { + payload := ErrorsPayload{ + Service: payload.Service, + Process: payload.Process, + System: payload.System, + Errors: payload.Errors, + } + var w fastjson.Writer + if err := payload.MarshalFastJSON(&w); err != nil { + panic(err) + } + if err := apmschema.Errors.Validate(bytes.NewReader(w.Bytes())); err != nil { + panic(err) + } + } + + if len(payload.Transactions) != 0 { + payload := TransactionsPayload{ + Service: payload.Service, + Process: payload.Process, + System: payload.System, + Transactions: payload.Transactions, + } + var w fastjson.Writer + if err := payload.MarshalFastJSON(&w); err != nil { + panic(err) + } + if err := apmschema.Transactions.Validate(bytes.NewReader(w.Bytes())); err != nil { + panic(err) + } + } + return 0 +} diff --git a/vendor/go.elastic.co/apm/model/maps.go b/vendor/go.elastic.co/apm/model/maps.go new file mode 100644 index 00000000000..7313d9cf7b0 --- /dev/null +++ b/vendor/go.elastic.co/apm/model/maps.go @@ -0,0 +1,48 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package model + +// StringMap is a slice-representation of map[string]string, +// optimized for fast JSON encoding. +// +// Slice items are expected to be ordered by key. +type StringMap []StringMapItem + +// StringMapItem holds a string key and value. +type StringMapItem struct { + // Key is the map item's key. + Key string + + // Value is the map item's value. + Value string +} + +// IfaceMap is a slice-representation of map[string]interface{}, +// optimized for fast JSON encoding. +// +// Slice items are expected to be ordered by key. +type IfaceMap []IfaceMapItem + +// IfaceMapItem holds a string key and value. +type IfaceMapItem struct { + // Key is the map item's key. + Key string + + // Value is the map item's value. + Value interface{} +} diff --git a/vendor/go.elastic.co/apm/model/marshal.go b/vendor/go.elastic.co/apm/model/marshal.go new file mode 100644 index 00000000000..298d4bcdb68 --- /dev/null +++ b/vendor/go.elastic.co/apm/model/marshal.go @@ -0,0 +1,639 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package model + +import ( + "encoding/hex" + "encoding/json" + "net/http" + "net/url" + "sort" + "strings" + "time" + + "github.com/pkg/errors" + + "go.elastic.co/fastjson" +) + +//go:generate sh generate.sh + +// MarshalFastJSON writes the JSON representation of t to w. +func (t Time) MarshalFastJSON(w *fastjson.Writer) error { + w.Int64(time.Time(t).UnixNano() / int64(time.Microsecond)) + return nil +} + +// UnmarshalJSON unmarshals the JSON data into t. +func (t *Time) UnmarshalJSON(data []byte) error { + var usec int64 + if err := json.Unmarshal(data, &usec); err != nil { + return err + } + *t = Time(time.Unix(usec/1000000, (usec%1000000)*1000).UTC()) + return nil +} + +// UnmarshalJSON unmarshals the JSON data into v. +func (v *HTTPSpanContext) UnmarshalJSON(data []byte) error { + var httpSpanContext struct { + URL string + StatusCode int `json:"status_code"` + } + if err := json.Unmarshal(data, &httpSpanContext); err != nil { + return err + } + u, err := url.Parse(httpSpanContext.URL) + if err != nil { + return err + } + v.URL = u + v.StatusCode = httpSpanContext.StatusCode + return nil +} + +// MarshalFastJSON writes the JSON representation of v to w. +func (v *HTTPSpanContext) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + beforeURL := w.Size() + w.RawString(`"url":"`) + if v.marshalURL(w) { + w.RawByte('"') + } else { + w.Rewind(beforeURL) + } + if v.StatusCode > 0 { + w.RawString(`,"status_code":`) + w.Int64(int64(v.StatusCode)) + } + w.RawByte('}') + return nil +} + +func (v *HTTPSpanContext) marshalURL(w *fastjson.Writer) bool { + if v.URL.Scheme != "" { + if !marshalScheme(w, v.URL.Scheme) { + return false + } + w.RawString("://") + } else { + w.RawString("http://") + } + w.StringContents(v.URL.Host) + if v.URL.Path == "" { + w.RawByte('/') + } else { + if v.URL.Path[0] != '/' { + w.RawByte('/') + } + w.StringContents(v.URL.Path) + } + if v.URL.RawQuery != "" { + w.RawByte('?') + w.StringContents(v.URL.RawQuery) + } + if v.URL.Fragment != "" { + w.RawByte('#') + w.StringContents(v.URL.Fragment) + } + return true +} + +// MarshalFastJSON writes the JSON representation of v to w. +func (v *URL) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Hash != "" { + const prefix = ",\"hash\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Hash) + } + if v.Hostname != "" { + const prefix = ",\"hostname\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Hostname) + } + if v.Path != "" { + const prefix = `,"pathname":"` + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if v.Path[0] != '/' { + w.RawByte('/') + } + w.StringContents(v.Path) + w.RawByte('"') + } + if v.Port != "" { + const prefix = ",\"port\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Port) + } + schemeBegin := -1 + schemeEnd := -1 + if v.Protocol != "" { + before := w.Size() + const prefix = ",\"protocol\":\"" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + schemeBegin = w.Size() + if marshalScheme(w, v.Protocol) { + schemeEnd = w.Size() + w.RawByte('"') + } else { + w.Rewind(before) + } + } + if v.Search != "" { + const prefix = ",\"search\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Search) + } + if schemeEnd != -1 && v.Hostname != "" && v.Path != "" { + before := w.Size() + w.RawString(",\"full\":") + if !v.marshalFullURL(w, w.Bytes()[schemeBegin:schemeEnd]) { + w.Rewind(before) + } + } + w.RawByte('}') + return nil +} + +func marshalScheme(w *fastjson.Writer, scheme string) bool { + // Canonicalize the scheme to lowercase. Don't use + // strings.ToLower, as it's too general and requires + // additional memory allocations. + // + // The scheme should start with a letter, and may + // then be followed by letters, digits, '+', '-', + // and '.'. We don't validate the scheme here, we + // just use those restrictions as a basis for + // optimization; anything not in that set will + // mean the full URL is omitted. + for i := 0; i < len(scheme); i++ { + c := scheme[i] + switch { + case c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '+' || c == '-' || c == '.': + w.RawByte(c) + case c >= 'A' && c <= 'Z': + w.RawByte(c + 'a' - 'A') + default: + return false + } + } + return true +} + +func (v *URL) marshalFullURL(w *fastjson.Writer, scheme []byte) bool { + w.RawByte('"') + before := w.Size() + w.RawBytes(scheme) + w.RawString("://") + if strings.IndexByte(v.Hostname, ':') == -1 { + w.StringContents(v.Hostname) + } else { + w.RawByte('[') + w.StringContents(v.Hostname) + w.RawByte(']') + } + if v.Port != "" { + w.RawByte(':') + w.StringContents(v.Port) + } + if !strings.HasPrefix(v.Path, "/") { + w.RawByte('/') + } + w.StringContents(v.Path) + if v.Search != "" { + w.RawByte('?') + w.StringContents(v.Search) + } + if v.Hash != "" { + w.RawByte('#') + w.StringContents(v.Hash) + } + if n := w.Size() - before; n > 1024 { + // Truncate the full URL to 1024 bytes. + w.Rewind(w.Size() - n + 1024) + } + w.RawByte('"') + return true +} + +func (l *Log) isZero() bool { + return l.Message == "" +} + +func (e *Exception) isZero() bool { + return e.Message == "" +} + +func (c Cookies) isZero() bool { + return len(c) == 0 +} + +// MarshalFastJSON writes the JSON representation of c to w. +func (c Cookies) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true +outer: + for i := len(c) - 1; i >= 0; i-- { + for j := i + 1; j < len(c); j++ { + if c[i].Name == c[j].Name { + continue outer + } + } + if first { + first = false + } else { + w.RawByte(',') + } + w.String(c[i].Name) + w.RawByte(':') + w.String(c[i].Value) + } + w.RawByte('}') + return nil +} + +// UnmarshalJSON unmarshals the JSON data into c. +func (c *Cookies) UnmarshalJSON(data []byte) error { + m := make(map[string]string) + if err := json.Unmarshal(data, &m); err != nil { + return err + } + *c = make([]*http.Cookie, 0, len(m)) + for k, v := range m { + *c = append(*c, &http.Cookie{ + Name: k, + Value: v, + }) + } + sort.Slice(*c, func(i, j int) bool { + return (*c)[i].Name < (*c)[j].Name + }) + return nil +} + +func (hs Headers) isZero() bool { + return len(hs) == 0 +} + +// MarshalFastJSON writes the JSON representation of h to w. +func (hs Headers) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + for i, h := range hs { + if i != 0 { + w.RawByte(',') + } + w.String(h.Key) + w.RawByte(':') + if len(h.Values) == 1 { + // Just one item, add the item directly. + w.String(h.Values[0]) + } else { + // Zero or multiple items, include them all. + w.RawByte('[') + for i, v := range h.Values { + if i != 0 { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + } + w.RawByte('}') + return nil +} + +// MarshalFastJSON writes the JSON representation of h to w. +func (*Header) MarshalFastJSON(w *fastjson.Writer) error { + panic("unreachable") +} + +// UnmarshalJSON unmarshals the JSON data into c. +func (hs *Headers) UnmarshalJSON(data []byte) error { + var m map[string]interface{} + if err := json.Unmarshal(data, &m); err != nil { + return err + } + for k, v := range m { + switch v := v.(type) { + case string: + *hs = append(*hs, Header{Key: k, Values: []string{v}}) + case []interface{}: + var values []string + for _, v := range v { + switch v := v.(type) { + case string: + values = append(values, v) + default: + return errors.Errorf("expected string, got %T", v) + } + } + *hs = append(*hs, Header{Key: k, Values: values}) + default: + return errors.Errorf("expected string or []string, got %T", v) + } + } + sort.Slice(*hs, func(i, j int) bool { + return (*hs)[i].Key < (*hs)[j].Key + }) + return nil +} + +// MarshalFastJSON writes the JSON representation of c to w. +func (c *ExceptionCode) MarshalFastJSON(w *fastjson.Writer) error { + if c.String != "" { + w.String(c.String) + } else { + w.Float64(c.Number) + } + return nil +} + +// UnmarshalJSON unmarshals the JSON data into c. +func (c *ExceptionCode) UnmarshalJSON(data []byte) error { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v := v.(type) { + case string: + c.String = v + case float64: + c.Number = v + default: + return errors.Errorf("expected string or number, got %T", v) + } + return nil +} + +// isZero is used by fastjson to implement omitempty. +func (c *ExceptionCode) isZero() bool { + return c.String == "" && c.Number == 0 +} + +// MarshalFastJSON writes the JSON representation of b to w. +func (b *RequestBody) MarshalFastJSON(w *fastjson.Writer) error { + if b.Form != nil { + w.RawByte('{') + first := true + for k, v := range b.Form { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + if len(v) == 1 { + // Just one item, add the item directly. + w.String(v[0]) + } else { + // Zero or multiple items, include them all. + w.RawByte('[') + first := true + for _, v := range v { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + } + w.RawByte('}') + } else { + w.String(b.Raw) + } + return nil +} + +// UnmarshalJSON unmarshals the JSON data into b. +func (b *RequestBody) UnmarshalJSON(data []byte) error { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v := v.(type) { + case string: + b.Raw = v + return nil + case map[string]interface{}: + form := make(url.Values, len(v)) + for k, v := range v { + switch v := v.(type) { + case string: + form.Set(k, v) + case []interface{}: + for _, v := range v { + switch v := v.(type) { + case string: + form.Add(k, v) + default: + return errors.Errorf("expected string, got %T", v) + } + } + default: + return errors.Errorf("expected string or []string, got %T", v) + } + } + b.Form = form + default: + return errors.Errorf("expected string or map, got %T", v) + } + return nil +} + +func (m StringMap) isZero() bool { + return len(m) == 0 +} + +// MarshalFastJSON writes the JSON representation of m to w. +func (m StringMap) MarshalFastJSON(w *fastjson.Writer) (firstErr error) { + w.RawByte('{') + first := true + for _, item := range m { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(item.Key) + w.RawByte(':') + if err := fastjson.Marshal(w, item.Value); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return nil +} + +// UnmarshalJSON unmarshals the JSON data into m. +func (m *StringMap) UnmarshalJSON(data []byte) error { + var mm map[string]string + if err := json.Unmarshal(data, &mm); err != nil { + return err + } + *m = make(StringMap, 0, len(mm)) + for k, v := range mm { + *m = append(*m, StringMapItem{Key: k, Value: v}) + } + sort.Slice(*m, func(i, j int) bool { + return (*m)[i].Key < (*m)[j].Key + }) + return nil +} + +// MarshalFastJSON exists to prevent code generation for StringMapItem. +func (*StringMapItem) MarshalFastJSON(*fastjson.Writer) error { + panic("unreachable") +} + +func (m IfaceMap) isZero() bool { + return len(m) == 0 +} + +// MarshalFastJSON writes the JSON representation of m to w. +func (m IfaceMap) MarshalFastJSON(w *fastjson.Writer) (firstErr error) { + w.RawByte('{') + first := true + for _, item := range m { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(item.Key) + w.RawByte(':') + if err := fastjson.Marshal(w, item.Value); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return nil +} + +// UnmarshalJSON unmarshals the JSON data into m. +func (m *IfaceMap) UnmarshalJSON(data []byte) error { + var mm map[string]interface{} + if err := json.Unmarshal(data, &mm); err != nil { + return err + } + *m = make(IfaceMap, 0, len(mm)) + for k, v := range mm { + *m = append(*m, IfaceMapItem{Key: k, Value: v}) + } + sort.Slice(*m, func(i, j int) bool { + return (*m)[i].Key < (*m)[j].Key + }) + return nil +} + +// MarshalFastJSON exists to prevent code generation for IfaceMapItem. +func (*IfaceMapItem) MarshalFastJSON(*fastjson.Writer) error { + panic("unreachable") +} + +func (id *TraceID) isZero() bool { + return *id == TraceID{} +} + +// MarshalFastJSON writes the JSON representation of id to w. +func (id *TraceID) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('"') + writeHex(w, id[:]) + w.RawByte('"') + return nil +} + +// UnmarshalJSON unmarshals the JSON data into id. +func (id *TraceID) UnmarshalJSON(data []byte) error { + _, err := hex.Decode(id[:], data[1:len(data)-1]) + return err +} + +func (id *SpanID) isZero() bool { + return *id == SpanID{} +} + +// UnmarshalJSON unmarshals the JSON data into id. +func (id *SpanID) UnmarshalJSON(data []byte) error { + _, err := hex.Decode(id[:], data[1:len(data)-1]) + return err +} + +// MarshalFastJSON writes the JSON representation of id to w. +func (id *SpanID) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('"') + writeHex(w, id[:]) + w.RawByte('"') + return nil +} + +func (t *ErrorTransaction) isZero() bool { + return *t == ErrorTransaction{} +} + +func (t *MetricsTransaction) isZero() bool { + return *t == MetricsTransaction{} +} + +func (s *MetricsSpan) isZero() bool { + return *s == MetricsSpan{} +} + +func writeHex(w *fastjson.Writer, v []byte) { + const hextable = "0123456789abcdef" + for _, v := range v { + w.RawByte(hextable[v>>4]) + w.RawByte(hextable[v&0x0f]) + } +} diff --git a/vendor/go.elastic.co/apm/model/marshal_fastjson.go b/vendor/go.elastic.co/apm/model/marshal_fastjson.go new file mode 100644 index 00000000000..cd5749c2533 --- /dev/null +++ b/vendor/go.elastic.co/apm/model/marshal_fastjson.go @@ -0,0 +1,1297 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Code generated by "generate-fastjson". DO NOT EDIT. + +package model + +import ( + "go.elastic.co/fastjson" +) + +func (v *Service) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + first := true + if v.Agent != nil { + const prefix = ",\"agent\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Agent.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Environment != "" { + const prefix = ",\"environment\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Environment) + } + if v.Framework != nil { + const prefix = ",\"framework\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Framework.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Language != nil { + const prefix = ",\"language\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Language.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Name != "" { + const prefix = ",\"name\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Name) + } + if v.Node != nil { + const prefix = ",\"node\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Node.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Runtime != nil { + const prefix = ",\"runtime\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Runtime.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Version != "" { + const prefix = ",\"version\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Version) + } + w.RawByte('}') + return firstErr +} + +func (v *Agent) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + w.RawString(",\"version\":") + w.String(v.Version) + w.RawByte('}') + return nil +} + +func (v *Framework) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + w.RawString(",\"version\":") + w.String(v.Version) + w.RawByte('}') + return nil +} + +func (v *Language) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + if v.Version != "" { + w.RawString(",\"version\":") + w.String(v.Version) + } + w.RawByte('}') + return nil +} + +func (v *Runtime) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + w.RawString(",\"version\":") + w.String(v.Version) + w.RawByte('}') + return nil +} + +func (v *ServiceNode) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + if v.ConfiguredName != "" { + w.RawString("\"configured_name\":") + w.String(v.ConfiguredName) + } + w.RawByte('}') + return nil +} + +func (v *System) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + first := true + if v.Architecture != "" { + const prefix = ",\"architecture\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Architecture) + } + if v.Container != nil { + const prefix = ",\"container\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Container.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Hostname != "" { + const prefix = ",\"hostname\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Hostname) + } + if v.Kubernetes != nil { + const prefix = ",\"kubernetes\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Kubernetes.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Platform != "" { + const prefix = ",\"platform\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Platform) + } + w.RawByte('}') + return firstErr +} + +func (v *Process) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"pid\":") + w.Int64(int64(v.Pid)) + if v.Argv != nil { + w.RawString(",\"argv\":") + w.RawByte('[') + for i, v := range v.Argv { + if i != 0 { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + if v.Ppid != nil { + w.RawString(",\"ppid\":") + w.Int64(int64(*v.Ppid)) + } + if v.Title != "" { + w.RawString(",\"title\":") + w.String(v.Title) + } + w.RawByte('}') + return nil +} + +func (v *Container) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"id\":") + w.String(v.ID) + w.RawByte('}') + return nil +} + +func (v *Kubernetes) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + first := true + if v.Namespace != "" { + const prefix = ",\"namespace\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Namespace) + } + if v.Node != nil { + const prefix = ",\"node\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Node.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Pod != nil { + const prefix = ",\"pod\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Pod.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *KubernetesNode) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + if v.Name != "" { + w.RawString("\"name\":") + w.String(v.Name) + } + w.RawByte('}') + return nil +} + +func (v *KubernetesPod) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Name != "" { + const prefix = ",\"name\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Name) + } + if v.UID != "" { + const prefix = ",\"uid\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.UID) + } + w.RawByte('}') + return nil +} + +func (v *Transaction) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"duration\":") + w.Float64(v.Duration) + w.RawString(",\"id\":") + if err := v.ID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"name\":") + w.String(v.Name) + w.RawString(",\"span_count\":") + if err := v.SpanCount.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"timestamp\":") + if err := v.Timestamp.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"trace_id\":") + if err := v.TraceID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"type\":") + w.String(v.Type) + if v.Context != nil { + w.RawString(",\"context\":") + if err := v.Context.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.ParentID.isZero() { + w.RawString(",\"parent_id\":") + if err := v.ParentID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Result != "" { + w.RawString(",\"result\":") + w.String(v.Result) + } + if v.Sampled != nil { + w.RawString(",\"sampled\":") + w.Bool(*v.Sampled) + } + w.RawByte('}') + return firstErr +} + +func (v *SpanCount) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"dropped\":") + w.Int64(int64(v.Dropped)) + w.RawString(",\"started\":") + w.Int64(int64(v.Started)) + w.RawByte('}') + return nil +} + +func (v *Span) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"duration\":") + w.Float64(v.Duration) + w.RawString(",\"id\":") + if err := v.ID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"name\":") + w.String(v.Name) + w.RawString(",\"timestamp\":") + if err := v.Timestamp.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"trace_id\":") + if err := v.TraceID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"transaction_id\":") + if err := v.TransactionID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"type\":") + w.String(v.Type) + if v.Action != "" { + w.RawString(",\"action\":") + w.String(v.Action) + } + if v.Context != nil { + w.RawString(",\"context\":") + if err := v.Context.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.ParentID.isZero() { + w.RawString(",\"parent_id\":") + if err := v.ParentID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Stacktrace != nil { + w.RawString(",\"stacktrace\":") + w.RawByte('[') + for i, v := range v.Stacktrace { + if i != 0 { + w.RawByte(',') + } + if err := v.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte(']') + } + if v.Subtype != "" { + w.RawString(",\"subtype\":") + w.String(v.Subtype) + } + w.RawByte('}') + return firstErr +} + +func (v *SpanContext) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + first := true + if v.Database != nil { + const prefix = ",\"db\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Database.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Destination != nil { + const prefix = ",\"destination\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Destination.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.HTTP != nil { + const prefix = ",\"http\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.HTTP.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.Tags.isZero() { + const prefix = ",\"tags\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Tags.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *DestinationSpanContext) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + first := true + if v.Address != "" { + const prefix = ",\"address\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Address) + } + if v.Port != 0 { + const prefix = ",\"port\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Int64(int64(v.Port)) + } + if v.Service != nil { + const prefix = ",\"service\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Service.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *DestinationServiceSpanContext) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Name != "" { + const prefix = ",\"name\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Name) + } + if v.Resource != "" { + const prefix = ",\"resource\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Resource) + } + if v.Type != "" { + const prefix = ",\"type\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Type) + } + w.RawByte('}') + return nil +} + +func (v *DatabaseSpanContext) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Instance != "" { + const prefix = ",\"instance\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Instance) + } + if v.RowsAffected != nil { + const prefix = ",\"rows_affected\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Int64(*v.RowsAffected) + } + if v.Statement != "" { + const prefix = ",\"statement\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Statement) + } + if v.Type != "" { + const prefix = ",\"type\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Type) + } + if v.User != "" { + const prefix = ",\"user\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.User) + } + w.RawByte('}') + return nil +} + +func (v *Context) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + first := true + if !v.Custom.isZero() { + const prefix = ",\"custom\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Custom.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Request != nil { + const prefix = ",\"request\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Request.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Response != nil { + const prefix = ",\"response\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Response.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Service != nil { + const prefix = ",\"service\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Service.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.Tags.isZero() { + const prefix = ",\"tags\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Tags.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.User != nil { + const prefix = ",\"user\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.User.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *User) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Email != "" { + const prefix = ",\"email\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Email) + } + if v.ID != "" { + const prefix = ",\"id\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.ID) + } + if v.Username != "" { + const prefix = ",\"username\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Username) + } + w.RawByte('}') + return nil +} + +func (v *Error) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"id\":") + if err := v.ID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"timestamp\":") + if err := v.Timestamp.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + if v.Context != nil { + w.RawString(",\"context\":") + if err := v.Context.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Culprit != "" { + w.RawString(",\"culprit\":") + w.String(v.Culprit) + } + if !v.Exception.isZero() { + w.RawString(",\"exception\":") + if err := v.Exception.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.Log.isZero() { + w.RawString(",\"log\":") + if err := v.Log.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.ParentID.isZero() { + w.RawString(",\"parent_id\":") + if err := v.ParentID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.TraceID.isZero() { + w.RawString(",\"trace_id\":") + if err := v.TraceID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.Transaction.isZero() { + w.RawString(",\"transaction\":") + if err := v.Transaction.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.TransactionID.isZero() { + w.RawString(",\"transaction_id\":") + if err := v.TransactionID.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *ErrorTransaction) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Sampled != nil { + const prefix = ",\"sampled\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Bool(*v.Sampled) + } + if v.Type != "" { + const prefix = ",\"type\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Type) + } + w.RawByte('}') + return nil +} + +func (v *Exception) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"handled\":") + w.Bool(v.Handled) + w.RawString(",\"message\":") + w.String(v.Message) + if v.Attributes != nil { + w.RawString(",\"attributes\":") + w.RawByte('{') + { + first := true + for k, v := range v.Attributes { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + if err := fastjson.Marshal(w, v); err != nil && firstErr == nil { + firstErr = err + } + } + } + w.RawByte('}') + } + if v.Cause != nil { + w.RawString(",\"cause\":") + w.RawByte('[') + for i, v := range v.Cause { + if i != 0 { + w.RawByte(',') + } + if err := v.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte(']') + } + if !v.Code.isZero() { + w.RawString(",\"code\":") + if err := v.Code.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Module != "" { + w.RawString(",\"module\":") + w.String(v.Module) + } + if v.Stacktrace != nil { + w.RawString(",\"stacktrace\":") + w.RawByte('[') + for i, v := range v.Stacktrace { + if i != 0 { + w.RawByte(',') + } + if err := v.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte(']') + } + if v.Type != "" { + w.RawString(",\"type\":") + w.String(v.Type) + } + w.RawByte('}') + return firstErr +} + +func (v *StacktraceFrame) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"filename\":") + w.String(v.File) + w.RawString(",\"lineno\":") + w.Int64(int64(v.Line)) + if v.AbsolutePath != "" { + w.RawString(",\"abs_path\":") + w.String(v.AbsolutePath) + } + if v.Column != nil { + w.RawString(",\"colno\":") + w.Int64(int64(*v.Column)) + } + if v.ContextLine != "" { + w.RawString(",\"context_line\":") + w.String(v.ContextLine) + } + if v.Function != "" { + w.RawString(",\"function\":") + w.String(v.Function) + } + if v.LibraryFrame != false { + w.RawString(",\"library_frame\":") + w.Bool(v.LibraryFrame) + } + if v.Module != "" { + w.RawString(",\"module\":") + w.String(v.Module) + } + if v.PostContext != nil { + w.RawString(",\"post_context\":") + w.RawByte('[') + for i, v := range v.PostContext { + if i != 0 { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + if v.PreContext != nil { + w.RawString(",\"pre_context\":") + w.RawByte('[') + for i, v := range v.PreContext { + if i != 0 { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + if v.Vars != nil { + w.RawString(",\"vars\":") + w.RawByte('{') + { + first := true + for k, v := range v.Vars { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + if err := fastjson.Marshal(w, v); err != nil && firstErr == nil { + firstErr = err + } + } + } + w.RawByte('}') + } + w.RawByte('}') + return firstErr +} + +func (v *Log) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"message\":") + w.String(v.Message) + if v.Level != "" { + w.RawString(",\"level\":") + w.String(v.Level) + } + if v.LoggerName != "" { + w.RawString(",\"logger_name\":") + w.String(v.LoggerName) + } + if v.ParamMessage != "" { + w.RawString(",\"param_message\":") + w.String(v.ParamMessage) + } + if v.Stacktrace != nil { + w.RawString(",\"stacktrace\":") + w.RawByte('[') + for i, v := range v.Stacktrace { + if i != 0 { + w.RawByte(',') + } + if err := v.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte(']') + } + w.RawByte('}') + return firstErr +} + +func (v *Request) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"method\":") + w.String(v.Method) + w.RawString(",\"url\":") + if err := v.URL.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + if v.Body != nil { + w.RawString(",\"body\":") + if err := v.Body.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.Cookies.isZero() { + w.RawString(",\"cookies\":") + if err := v.Cookies.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Env != nil { + w.RawString(",\"env\":") + w.RawByte('{') + { + first := true + for k, v := range v.Env { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + w.String(v) + } + } + w.RawByte('}') + } + if !v.Headers.isZero() { + w.RawString(",\"headers\":") + if err := v.Headers.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.HTTPVersion != "" { + w.RawString(",\"http_version\":") + w.String(v.HTTPVersion) + } + if v.Socket != nil { + w.RawString(",\"socket\":") + if err := v.Socket.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *RequestSocket) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Encrypted != false { + const prefix = ",\"encrypted\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Bool(v.Encrypted) + } + if v.RemoteAddress != "" { + const prefix = ",\"remote_address\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.RemoteAddress) + } + w.RawByte('}') + return nil +} + +func (v *Response) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + first := true + if v.Finished != nil { + const prefix = ",\"finished\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Bool(*v.Finished) + } + if !v.Headers.isZero() { + const prefix = ",\"headers\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + if err := v.Headers.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.HeadersSent != nil { + const prefix = ",\"headers_sent\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Bool(*v.HeadersSent) + } + if v.StatusCode != 0 { + const prefix = ",\"status_code\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Int64(int64(v.StatusCode)) + } + w.RawByte('}') + return firstErr +} + +func (v *Metrics) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"samples\":") + if v.Samples == nil { + w.RawString("null") + } else { + w.RawByte('{') + { + first := true + for k, v := range v.Samples { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + if err := v.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + } + w.RawByte('}') + } + w.RawString(",\"timestamp\":") + if err := v.Timestamp.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + if !v.Span.isZero() { + w.RawString(",\"span\":") + if err := v.Span.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.Labels.isZero() { + w.RawString(",\"tags\":") + if err := v.Labels.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if !v.Transaction.isZero() { + w.RawString(",\"transaction\":") + if err := v.Transaction.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *MetricsTransaction) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Name != "" { + const prefix = ",\"name\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Name) + } + if v.Type != "" { + const prefix = ",\"type\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Type) + } + w.RawByte('}') + return nil +} + +func (v *MetricsSpan) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Subtype != "" { + const prefix = ",\"subtype\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Subtype) + } + if v.Type != "" { + const prefix = ",\"type\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Type) + } + w.RawByte('}') + return nil +} + +func (v *Metric) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"value\":") + w.Float64(v.Value) + w.RawByte('}') + return nil +} diff --git a/vendor/go.elastic.co/apm/model/model.go b/vendor/go.elastic.co/apm/model/model.go new file mode 100644 index 00000000000..71568d4c0d6 --- /dev/null +++ b/vendor/go.elastic.co/apm/model/model.go @@ -0,0 +1,671 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package model + +import ( + "net/http" + "net/url" + "time" +) + +// Service represents the service handling transactions being traced. +type Service struct { + // Name is the immutable name of the service. + Name string `json:"name,omitempty"` + + // Version is the version of the service, if it has one. + Version string `json:"version,omitempty"` + + // Environment is the name of the service's environment, if it has + // one, e.g. "production" or "staging". + Environment string `json:"environment,omitempty"` + + // Agent holds information about the Elastic APM agent tracing this + // service's transactions. + Agent *Agent `json:"agent,omitempty"` + + // Framework holds information about the service's framework, if any. + Framework *Framework `json:"framework,omitempty"` + + // Language holds information about the programming language in which + // the service is written. + Language *Language `json:"language,omitempty"` + + // Runtime holds information about the programming language runtime + // running this service. + Runtime *Runtime `json:"runtime,omitempty"` + + // Node holds unique information about each service node + Node *ServiceNode `json:"node,omitempty"` +} + +// Agent holds information about the Elastic APM agent. +type Agent struct { + // Name is the name of the Elastic APM agent, e.g. "Go". + Name string `json:"name"` + + // Version is the version of the Elastic APM agent, e.g. "1.0.0". + Version string `json:"version"` +} + +// Framework holds information about the framework (typically web) +// used by the service. +type Framework struct { + // Name is the name of the framework. + Name string `json:"name"` + + // Version is the version of the framework. + Version string `json:"version"` +} + +// Language holds information about the programming language used. +type Language struct { + // Name is the name of the programming language. + Name string `json:"name"` + + // Version is the version of the programming language. + Version string `json:"version,omitempty"` +} + +// Runtime holds information about the programming language runtime. +type Runtime struct { + // Name is the name of the programming language runtime. + Name string `json:"name"` + + // Version is the version of the programming language runtime. + Version string `json:"version"` +} + +// ServiceNode holds unique information about each service node +type ServiceNode struct { + // ConfiguredName holds the name of the service node + ConfiguredName string `json:"configured_name,omitempty"` +} + +// System represents the system (operating system and machine) running the +// service. +type System struct { + // Architecture is the system's hardware architecture. + Architecture string `json:"architecture,omitempty"` + + // Hostname is the system's hostname. + Hostname string `json:"hostname,omitempty"` + + // Platform is the system's platform, or operating system name. + Platform string `json:"platform,omitempty"` + + // Container describes the container running the service. + Container *Container `json:"container,omitempty"` + + // Kubernetes describes the kubernetes node and pod running the service. + Kubernetes *Kubernetes `json:"kubernetes,omitempty"` +} + +// Process represents an operating system process. +type Process struct { + // Pid is the process ID. + Pid int `json:"pid"` + + // Ppid is the parent process ID, if known. + Ppid *int `json:"ppid,omitempty"` + + // Title is the title of the process. + Title string `json:"title,omitempty"` + + // Argv holds the command line arguments used to start the process. + Argv []string `json:"argv,omitempty"` +} + +// Container represents the container (e.g. Docker) running the service. +type Container struct { + // ID is the unique container ID. + ID string `json:"id"` +} + +// Kubernetes describes properties of the Kubernetes node and pod in which +// the service is running. +type Kubernetes struct { + // Namespace names the Kubernetes namespace in which the pod exists. + Namespace string `json:"namespace,omitempty"` + + // Node describes the Kubernetes node running the service's pod. + Node *KubernetesNode `json:"node,omitempty"` + + // Pod describes the Kubernetes pod running the service. + Pod *KubernetesPod `json:"pod,omitempty"` +} + +// KubernetesNode describes a Kubernetes node. +type KubernetesNode struct { + // Name holds the node name. + Name string `json:"name,omitempty"` +} + +// KubernetesPod describes a Kubernetes pod. +type KubernetesPod struct { + // Name holds the pod name. + Name string `json:"name,omitempty"` + + // UID holds the pod UID. + UID string `json:"uid,omitempty"` +} + +// Transaction represents a transaction handled by the service. +type Transaction struct { + // ID holds the 64-bit hex-encoded transaction ID. + ID SpanID `json:"id"` + + // TraceID holds the ID of the trace that this transaction is a part of. + TraceID TraceID `json:"trace_id"` + + // ParentID holds the ID of the transaction's parent span or transaction. + ParentID SpanID `json:"parent_id,omitempty"` + + // Name holds the name of the transaction. + Name string `json:"name"` + + // Type identifies the service-domain specific type of the request, + // e.g. "request" or "backgroundjob". + Type string `json:"type"` + + // Timestamp holds the time at which the transaction started. + Timestamp Time `json:"timestamp"` + + // Duration records how long the transaction took to complete, + // in milliseconds. + Duration float64 `json:"duration"` + + // Result holds the result of the transaction, e.g. the status code + // for HTTP requests. + Result string `json:"result,omitempty"` + + // Context holds contextual information relating to the transaction. + Context *Context `json:"context,omitempty"` + + // Sampled indicates that the transaction was sampled, and + // includes all available information. Non-sampled transactions + // omit Context. + // + // If Sampled is unspecified (nil), it is equivalent to setting + // it to true. + Sampled *bool `json:"sampled,omitempty"` + + // SpanCount holds statistics on spans within a transaction. + SpanCount SpanCount `json:"span_count"` +} + +// SpanCount holds statistics on spans within a transaction. +type SpanCount struct { + // Dropped holds the number of spans dropped within a transaction. + // This does not include spans that were started and dropped due + // to full buffers, network errors, etc. + Dropped int `json:"dropped"` + + // Started holds the number of spans started within a transaction. + Started int `json:"started"` +} + +// Span represents a span within a transaction. +type Span struct { + // Name holds the name of the span. + Name string `json:"name"` + + // Timestamp holds the time at which the span started. + Timestamp Time `json:"timestamp"` + + // Duration holds the duration of the span, in milliseconds. + Duration float64 `json:"duration"` + + // Type identifies the overarching type of the span, + // e.g. "db" or "external". + Type string `json:"type"` + + // Subtype identifies the subtype of the span, + // e.g. "mysql" or "http". + Subtype string `json:"subtype,omitempty"` + + // Action identifies the action that is being undertaken, e.g. "query". + Action string `json:"action,omitempty"` + + // ID holds the ID of the span. + ID SpanID `json:"id"` + + // TransactionID holds the ID of the transaction of which the span is a part. + TransactionID SpanID `json:"transaction_id"` + + // TraceID holds the ID of the trace that this span is a part of. + TraceID TraceID `json:"trace_id"` + + // ParentID holds the ID of the span's parent (span or transaction). + ParentID SpanID `json:"parent_id,omitempty"` + + // Context holds contextual information relating to the span. + Context *SpanContext `json:"context,omitempty"` + + // Stacktrace holds stack frames corresponding to the span. + Stacktrace []StacktraceFrame `json:"stacktrace,omitempty"` +} + +// SpanContext holds contextual information relating to the span. +type SpanContext struct { + // Destination holds information about a destination service. + Destination *DestinationSpanContext `json:"destination,omitempty"` + + // Database holds contextual information for database + // operation spans. + Database *DatabaseSpanContext `json:"db,omitempty"` + + // HTTP holds contextual information for HTTP client request spans. + HTTP *HTTPSpanContext `json:"http,omitempty"` + + // Tags holds user-defined key/value pairs. + Tags IfaceMap `json:"tags,omitempty"` +} + +// DestinationSpanContext holds contextual information about the destination +// for a span that relates to an operation involving an external service. +type DestinationSpanContext struct { + // Address holds the network address of the destination service. + // This may be a hostname, FQDN, or (IPv4 or IPv6) network address. + Address string `json:"address,omitempty"` + + // Port holds the network port for the destination service. + Port int `json:"port,omitempty"` + + // Service holds additional destination service context. + Service *DestinationServiceSpanContext `json:"service,omitempty"` +} + +// DestinationServiceSpanContext holds contextual information about a +// destination service,. +type DestinationServiceSpanContext struct { + // Type holds the destination service type. + Type string `json:"type,omitempty"` + + // Name holds the destination service name. + Name string `json:"name,omitempty"` + + // Resource identifies the destination service + // resource, e.g. a URI or message queue name. + Resource string `json:"resource,omitempty"` +} + +// DatabaseSpanContext holds contextual information for database +// operation spans. +type DatabaseSpanContext struct { + // Instance holds the database instance name. + Instance string `json:"instance,omitempty"` + + // Statement holds the database statement (e.g. query). + Statement string `json:"statement,omitempty"` + + // RowsAffected holds the number of rows affected by the + // database operation. + RowsAffected *int64 `json:"rows_affected,omitempty"` + + // Type holds the database type. For any SQL database, + // this should be "sql"; for others, the lower-cased + // database category, e.g. "cassandra", "hbase", "redis". + Type string `json:"type,omitempty"` + + // User holds the username used for database access. + User string `json:"user,omitempty"` +} + +// HTTPSpanContext holds contextual information for HTTP client request spans. +type HTTPSpanContext struct { + // URL is the request URL. + URL *url.URL + + // StatusCode holds the HTTP response status code. + StatusCode int `json:"status_code,omitempty"` +} + +// Context holds contextual information relating to a transaction or error. +type Context struct { + // Custom holds custom context relating to the transaction or error. + Custom IfaceMap `json:"custom,omitempty"` + + // Request holds details of the HTTP request relating to the + // transaction or error, if relevant. + Request *Request `json:"request,omitempty"` + + // Response holds details of the HTTP response relating to the + // transaction or error, if relevant. + Response *Response `json:"response,omitempty"` + + // User holds details of the authenticated user relating to the + // transaction or error, if relevant. + User *User `json:"user,omitempty"` + + // Tags holds user-defined key/value pairs. + Tags IfaceMap `json:"tags,omitempty"` + + // Service holds values to overrides service-level metadata. + Service *Service `json:"service,omitempty"` +} + +// User holds information about an authenticated user. +type User struct { + // Username holds the username of the user. + Username string `json:"username,omitempty"` + + // ID identifies the user, e.g. a primary key. This may be + // a string or number. + ID string `json:"id,omitempty"` + + // Email holds the email address of the user. + Email string `json:"email,omitempty"` +} + +// Error represents an error occurring in the service. +type Error struct { + // Timestamp holds the time at which the error occurred. + Timestamp Time `json:"timestamp"` + + // ID holds the 128-bit hex-encoded error ID. + ID TraceID `json:"id"` + + // TraceID holds the ID of the trace within which the error occurred. + TraceID TraceID `json:"trace_id,omitempty"` + + // ParentID holds the ID of the transaction within which the error + // occurred. + ParentID SpanID `json:"parent_id,omitempty"` + + // TransactionID holds the ID of the transaction within which the error occurred. + TransactionID SpanID `json:"transaction_id,omitempty"` + + // Culprit holds the name of the function which + // produced the error. + Culprit string `json:"culprit,omitempty"` + + // Context holds contextual information relating to the error. + Context *Context `json:"context,omitempty"` + + // Exception holds details of the exception (error or panic) + // to which this error relates. + Exception Exception `json:"exception,omitempty"` + + // Log holds additional information added when logging the error. + Log Log `json:"log,omitempty"` + + // Transaction holds information about the transaction within which the error occurred. + Transaction ErrorTransaction `json:"transaction,omitempty"` +} + +// ErrorTransaction holds information about the transaction within which an error occurred. +type ErrorTransaction struct { + // Sampled indicates that the transaction was sampled. + Sampled *bool `json:"sampled,omitempty"` + + // Type holds the transaction type. + Type string `json:"type,omitempty"` +} + +// Exception represents an exception: an error or panic. +type Exception struct { + // Message holds the error message. + Message string `json:"message"` + + // Code holds the error code. This may be a number or a string. + Code ExceptionCode `json:"code,omitempty"` + + // Type holds the type of the exception. + Type string `json:"type,omitempty"` + + // Module holds the exception type's module namespace. + Module string `json:"module,omitempty"` + + // Attributes holds arbitrary exception-type specific attributes. + Attributes map[string]interface{} `json:"attributes,omitempty"` + + // Stacktrace holds stack frames corresponding to the exception. + Stacktrace []StacktraceFrame `json:"stacktrace,omitempty"` + + // Handled indicates whether or not the error was caught and handled. + Handled bool `json:"handled"` + + // Cause holds the causes of this error. + Cause []Exception `json:"cause,omitempty"` +} + +// ExceptionCode represents an exception code as either a number or a string. +type ExceptionCode struct { + String string + Number float64 +} + +// StacktraceFrame describes a stack frame. +type StacktraceFrame struct { + // AbsolutePath holds the absolute path of the source file for the + // stack frame. + AbsolutePath string `json:"abs_path,omitempty"` + + // File holds the base filename of the source file for the stack frame. + File string `json:"filename"` + + // Line holds the line number of the source for the stack frame. + Line int `json:"lineno"` + + // Column holds the column number of the source for the stack frame. + Column *int `json:"colno,omitempty"` + + // Module holds the module to which the frame belongs. For Go, we + // use the package path (e.g. "net/http"). + Module string `json:"module,omitempty"` + + // Function holds the name of the function to which the frame belongs. + Function string `json:"function,omitempty"` + + // LibraryFrame indicates whether or not the frame corresponds to + // library or user code. + LibraryFrame bool `json:"library_frame,omitempty"` + + // ContextLine holds the line of source code to which the frame + // corresponds. + ContextLine string `json:"context_line,omitempty"` + + // PreContext holds zero or more lines of source code preceding the + // line corresponding to the frame. + PreContext []string `json:"pre_context,omitempty"` + + // PostContext holds zero or more lines of source code proceeding the + // line corresponding to the frame. + PostContext []string `json:"post_context,omitempty"` + + // Vars holds local variables for this stack frame. + Vars map[string]interface{} `json:"vars,omitempty"` +} + +// Log holds additional information added when logging an error. +type Log struct { + // Message holds the logged error message. + Message string `json:"message"` + + // Level holds the severity of the log record. + Level string `json:"level,omitempty"` + + // LoggerName holds the name of the logger used. + LoggerName string `json:"logger_name,omitempty"` + + // ParamMessage holds a parameterized message, e.g. + // "Could not connect to %s". The string is not interpreted, + // but may be used for grouping errors. + ParamMessage string `json:"param_message,omitempty"` + + // Stacktrace holds stack frames corresponding to the error. + Stacktrace []StacktraceFrame `json:"stacktrace,omitempty"` +} + +// Request represents an HTTP request. +type Request struct { + // URL is the request URL. + URL URL `json:"url"` + + // Method holds the HTTP request method. + Method string `json:"method"` + + // Headers holds the request headers. + Headers Headers `json:"headers,omitempty"` + + // Body holds the request body, if body capture is enabled. + Body *RequestBody `json:"body,omitempty"` + + // HTTPVersion holds the HTTP version of the request. + HTTPVersion string `json:"http_version,omitempty"` + + // Cookies holds the parsed cookies. + Cookies Cookies `json:"cookies,omitempty"` + + // Env holds environment information passed from the + // web framework to the request handler. + Env map[string]string `json:"env,omitempty"` + + // Socket holds transport-level information. + Socket *RequestSocket `json:"socket,omitempty"` +} + +// Cookies holds a collection of HTTP cookies. +type Cookies []*http.Cookie + +// RequestBody holds a request body. +// +// Exactly one of Raw or Form must be set. +type RequestBody struct { + // Raw holds the raw body content. + Raw string + + // Form holds the form data from POST, PATCH, or PUT body parameters. + Form url.Values +} + +// Headers holds a collection of HTTP headers. +type Headers []Header + +// Header holds an HTTP header, with one or more values. +type Header struct { + Key string + Values []string +} + +// RequestSocket holds transport-level information relating to an HTTP request. +type RequestSocket struct { + // Encrypted indicates whether or not the request was sent + // as an SSL/HTTPS request. + Encrypted bool `json:"encrypted,omitempty"` + + // RemoteAddress holds the remote address for the request. + RemoteAddress string `json:"remote_address,omitempty"` +} + +// URL represents a server-side (transaction) request URL, +// broken down into its constituent parts. +type URL struct { + // Full is the full URL, e.g. + // "https://example.com:443/search/?q=elasticsearch#top". + Full string `json:"full,omitempty"` + + // Protocol is the scheme of the URL, e.g. "https". + Protocol string `json:"protocol,omitempty"` + + // Hostname is the hostname for the URL, e.g. "example.com". + Hostname string `json:"hostname,omitempty"` + + // Port is the port number in the URL, e.g. "443". + Port string `json:"port,omitempty"` + + // Path is the path of the URL, e.g. "/search". + Path string `json:"pathname,omitempty"` + + // Search is the query string of the URL, e.g. "q=elasticsearch". + Search string `json:"search,omitempty"` + + // Hash is the fragment for references, e.g. "top" in the + // URL example provided for Full. + Hash string `json:"hash,omitempty"` +} + +// Response represents an HTTP response. +type Response struct { + // StatusCode holds the HTTP response status code. + StatusCode int `json:"status_code,omitempty"` + + // Headers holds the response headers. + Headers Headers `json:"headers,omitempty"` + + // HeadersSent indicates whether or not headers were sent + // to the client. + HeadersSent *bool `json:"headers_sent,omitempty"` + + // Finished indicates whether or not the response was finished. + Finished *bool `json:"finished,omitempty"` +} + +// Time is a timestamp, formatted as a number of microseconds since January 1, 1970 UTC. +type Time time.Time + +// TraceID holds a 128-bit trace ID. +type TraceID [16]byte + +// SpanID holds a 64-bit span ID. Despite its name, this is used for +// both spans and transactions. +type SpanID [8]byte + +// Metrics holds a set of metric samples, with an optional set of labels. +type Metrics struct { + // Timestamp holds the time at which the metric samples were taken. + Timestamp Time `json:"timestamp"` + + // Transaction optionally holds the name and type of transactions + // with which these metrics are associated. + Transaction MetricsTransaction `json:"transaction,omitempty"` + + // Span optionally holds the type and subtype of the spans with + // which these metrics are associated. + Span MetricsSpan `json:"span,omitempty"` + + // Labels holds a set of labels associated with the metrics. + // The labels apply uniformly to all metric samples in the set. + // + // NOTE(axw) the schema calls the field "tags", but we use + // "labels" for agent-internal consistency. Labels aligns better + // with the common schema, anyway. + Labels StringMap `json:"tags,omitempty"` + + // Samples holds a map of metric samples, keyed by metric name. + Samples map[string]Metric `json:"samples"` +} + +// MetricsTransaction holds transaction identifiers for metrics. +type MetricsTransaction struct { + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` +} + +// MetricsSpan holds span identifiers for metrics. +type MetricsSpan struct { + Type string `json:"type,omitempty"` + Subtype string `json:"subtype,omitempty"` +} + +// Metric holds metric values. +type Metric struct { + // Value holds the metric value. + Value float64 `json:"value"` +} diff --git a/vendor/go.elastic.co/apm/modelwriter.go b/vendor/go.elastic.co/apm/modelwriter.go new file mode 100644 index 00000000000..e78d9be8f50 --- /dev/null +++ b/vendor/go.elastic.co/apm/modelwriter.go @@ -0,0 +1,267 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "go.elastic.co/apm/internal/ringbuffer" + "go.elastic.co/apm/model" + "go.elastic.co/apm/stacktrace" + "go.elastic.co/fastjson" +) + +const ( + transactionBlockTag ringbuffer.BlockTag = iota + 1 + spanBlockTag + errorBlockTag + metricsBlockTag +) + +// notSampled is used as the pointee for the model.Transaction.Sampled field +// of non-sampled transactions. +var notSampled = false + +type modelWriter struct { + buffer *ringbuffer.Buffer + metricsBuffer *ringbuffer.Buffer + cfg *tracerConfig + stats *TracerStats + json fastjson.Writer + modelStacktrace []model.StacktraceFrame +} + +// writeTransaction encodes tx as JSON to the buffer, and then resets tx. +func (w *modelWriter) writeTransaction(tx *Transaction, td *TransactionData) { + var modelTx model.Transaction + w.buildModelTransaction(&modelTx, tx, td) + w.json.RawString(`{"transaction":`) + modelTx.MarshalFastJSON(&w.json) + w.json.RawByte('}') + w.buffer.WriteBlock(w.json.Bytes(), transactionBlockTag) + w.json.Reset() + td.reset(tx.tracer) +} + +// writeSpan encodes s as JSON to the buffer, and then resets s. +func (w *modelWriter) writeSpan(s *Span, sd *SpanData) { + var modelSpan model.Span + w.buildModelSpan(&modelSpan, s, sd) + w.json.RawString(`{"span":`) + modelSpan.MarshalFastJSON(&w.json) + w.json.RawByte('}') + w.buffer.WriteBlock(w.json.Bytes(), spanBlockTag) + w.json.Reset() + sd.reset(s.tracer) +} + +// writeError encodes e as JSON to the buffer, and then resets e. +func (w *modelWriter) writeError(e *ErrorData) { + var modelError model.Error + w.buildModelError(&modelError, e) + w.json.RawString(`{"error":`) + modelError.MarshalFastJSON(&w.json) + w.json.RawByte('}') + w.buffer.WriteBlock(w.json.Bytes(), errorBlockTag) + w.json.Reset() + e.reset() +} + +// writeMetrics encodes m as JSON to the w.metricsBuffer, and then resets m. +// +// Note that we do not write metrics to the main ring buffer (w.buffer), as +// periodic metrics would be evicted by transactions/spans in a busy system. +func (w *modelWriter) writeMetrics(m *Metrics) { + for _, m := range m.transactionGroupMetrics { + w.json.RawString(`{"metricset":`) + m.MarshalFastJSON(&w.json) + w.json.RawString("}") + w.metricsBuffer.WriteBlock(w.json.Bytes(), metricsBlockTag) + w.json.Reset() + } + for _, m := range m.metrics { + w.json.RawString(`{"metricset":`) + m.MarshalFastJSON(&w.json) + w.json.RawString("}") + w.metricsBuffer.WriteBlock(w.json.Bytes(), metricsBlockTag) + w.json.Reset() + } + m.reset() +} + +func (w *modelWriter) buildModelTransaction(out *model.Transaction, tx *Transaction, td *TransactionData) { + out.ID = model.SpanID(tx.traceContext.Span) + out.TraceID = model.TraceID(tx.traceContext.Trace) + sampled := tx.traceContext.Options.Recorded() + if !sampled { + out.Sampled = ¬Sampled + } + + out.ParentID = model.SpanID(td.parentSpan) + out.Name = truncateString(td.Name) + out.Type = truncateString(td.Type) + out.Result = truncateString(td.Result) + out.Timestamp = model.Time(td.timestamp.UTC()) + out.Duration = td.Duration.Seconds() * 1000 + out.SpanCount.Started = td.spansCreated + out.SpanCount.Dropped = td.spansDropped + if sampled { + out.Context = td.Context.build() + } + + if len(w.cfg.sanitizedFieldNames) != 0 && out.Context != nil { + if out.Context.Request != nil { + sanitizeRequest(out.Context.Request, w.cfg.sanitizedFieldNames) + } + if out.Context.Response != nil { + sanitizeResponse(out.Context.Response, w.cfg.sanitizedFieldNames) + } + } +} + +func (w *modelWriter) buildModelSpan(out *model.Span, span *Span, sd *SpanData) { + w.modelStacktrace = w.modelStacktrace[:0] + out.ID = model.SpanID(span.traceContext.Span) + out.TraceID = model.TraceID(span.traceContext.Trace) + out.TransactionID = model.SpanID(span.transactionID) + + out.ParentID = model.SpanID(sd.parentID) + out.Name = truncateString(sd.Name) + out.Type = truncateString(sd.Type) + out.Subtype = truncateString(sd.Subtype) + out.Action = truncateString(sd.Action) + out.Timestamp = model.Time(sd.timestamp.UTC()) + out.Duration = sd.Duration.Seconds() * 1000 + out.Context = sd.Context.build() + + // Copy the span type to context.destination.service.type. + if out.Context != nil && out.Context.Destination != nil && out.Context.Destination.Service != nil { + out.Context.Destination.Service.Type = out.Type + } + + w.modelStacktrace = appendModelStacktraceFrames(w.modelStacktrace, sd.stacktrace) + out.Stacktrace = w.modelStacktrace + w.setStacktraceContext(out.Stacktrace) +} + +func (w *modelWriter) buildModelError(out *model.Error, e *ErrorData) { + out.ID = model.TraceID(e.ID) + out.TraceID = model.TraceID(e.TraceID) + out.ParentID = model.SpanID(e.ParentID) + out.TransactionID = model.SpanID(e.TransactionID) + out.Timestamp = model.Time(e.Timestamp.UTC()) + out.Context = e.Context.build() + out.Culprit = e.Culprit + + if !e.TransactionID.isZero() { + out.Transaction.Sampled = &e.transactionSampled + if e.transactionSampled { + out.Transaction.Type = e.transactionType + } + } + + // Create model stacktrace frames, and set the context. + w.modelStacktrace = w.modelStacktrace[:0] + var appendModelErrorStacktraceFrames func(exception *exceptionData) + appendModelErrorStacktraceFrames = func(exception *exceptionData) { + if len(exception.stacktrace) != 0 { + w.modelStacktrace = appendModelStacktraceFrames(w.modelStacktrace, exception.stacktrace) + } + for _, cause := range exception.cause { + appendModelErrorStacktraceFrames(&cause) + } + } + appendModelErrorStacktraceFrames(&e.exception) + if len(e.logStacktrace) != 0 { + w.modelStacktrace = appendModelStacktraceFrames(w.modelStacktrace, e.logStacktrace) + } + w.setStacktraceContext(w.modelStacktrace) + + var modelStacktraceOffset int + if e.exception.message != "" { + var buildException func(exception *exceptionData) model.Exception + culprit := e.Culprit + buildException = func(exception *exceptionData) model.Exception { + out := model.Exception{ + Message: exception.message, + Code: model.ExceptionCode{ + String: exception.Code.String, + Number: exception.Code.Number, + }, + Type: exception.Type.Name, + Module: exception.Type.PackagePath, + Handled: e.Handled, + } + if n := len(exception.stacktrace); n != 0 { + out.Stacktrace = w.modelStacktrace[modelStacktraceOffset : modelStacktraceOffset+n] + modelStacktraceOffset += n + } + if len(exception.attrs) != 0 { + out.Attributes = exception.attrs + } + if n := len(exception.cause); n > 0 { + out.Cause = make([]model.Exception, n) + for i := range exception.cause { + out.Cause[i] = buildException(&exception.cause[i]) + } + } + if culprit == "" { + culprit = stacktraceCulprit(out.Stacktrace) + } + return out + } + out.Exception = buildException(&e.exception) + out.Culprit = culprit + } + if e.log.Message != "" { + out.Log = model.Log{ + Message: e.log.Message, + Level: e.log.Level, + LoggerName: e.log.LoggerName, + ParamMessage: e.log.MessageFormat, + } + if n := len(e.logStacktrace); n != 0 { + out.Log.Stacktrace = w.modelStacktrace[modelStacktraceOffset : modelStacktraceOffset+n] + modelStacktraceOffset += n + if out.Culprit == "" { + out.Culprit = stacktraceCulprit(out.Log.Stacktrace) + } + } + } + out.Culprit = truncateString(out.Culprit) +} + +func stacktraceCulprit(frames []model.StacktraceFrame) string { + for _, frame := range frames { + if !frame.LibraryFrame { + return frame.Function + } + } + return "" +} + +func (w *modelWriter) setStacktraceContext(stack []model.StacktraceFrame) { + if w.cfg.contextSetter == nil || len(stack) == 0 { + return + } + err := stacktrace.SetContext(w.cfg.contextSetter, stack, w.cfg.preContext, w.cfg.postContext) + if err != nil { + if w.cfg.logger != nil { + w.cfg.logger.Debugf("setting context failed: %v", err) + } + w.stats.Errors.SetContext++ + } +} diff --git a/vendor/go.elastic.co/apm/module/apmelasticsearch/LICENSE b/vendor/go.elastic.co/apm/module/apmelasticsearch/LICENSE new file mode 100644 index 00000000000..b1a731fb5a3 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmelasticsearch/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Elasticsearch BV + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/go.elastic.co/apm/module/apmelasticsearch/client.go b/vendor/go.elastic.co/apm/module/apmelasticsearch/client.go new file mode 100644 index 00000000000..9222c732e8b --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmelasticsearch/client.go @@ -0,0 +1,239 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmelasticsearch + +import ( + "bytes" + "compress/gzip" + "io" + "io/ioutil" + "net/http" + "net/url" + "path" + "sync/atomic" + "unsafe" + + "go.elastic.co/apm" + "go.elastic.co/apm/module/apmhttp" +) + +// WrapRoundTripper returns an http.RoundTripper wrapping r, reporting each +// request as a span to Elastic APM, if the request's context contains a +// sampled transaction. +// +// If r is nil, then http.DefaultTransport is wrapped. +func WrapRoundTripper(r http.RoundTripper, o ...ClientOption) http.RoundTripper { + if r == nil { + r = http.DefaultTransport + } + rt := &roundTripper{r: r} + for _, o := range o { + o(rt) + } + return rt +} + +type roundTripper struct { + r http.RoundTripper +} + +// RoundTrip delegates to r.r, emitting a span if req's context contains a transaction. +// +// If req.URL.Path corresponds to a search request, then RoundTrip will attempt to extract +// the search query to use as the span context's "database statement". If the query is +// passed in as a query parameter (i.e. "/_search?q=foo:bar"), then that will be used; +// otherwise, the request body will be read. In the latter case, req.GetBody is used +// if defined, otherwise we read req.Body, preserving its contents for the underlying +// RoundTripper. If the request body is gzip-encoded, it will be decoded. +func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + ctx := req.Context() + tx := apm.TransactionFromContext(ctx) + if tx == nil || !tx.Sampled() { + return r.r.RoundTrip(req) + } + + name := requestName(req) + span := tx.StartSpan(name, "db.elasticsearch", apm.SpanFromContext(ctx)) + if span.Dropped() { + span.End() + return r.r.RoundTrip(req) + } + + statement, req := captureSearchStatement(req) + username, _, _ := req.BasicAuth() + ctx = apm.ContextWithSpan(ctx, span) + req = apmhttp.RequestWithContext(ctx, req) + span.Context.SetHTTPRequest(req) + span.Context.SetDestinationService(apm.DestinationServiceSpanContext{ + Name: "elasticsearch", + Resource: "elasticsearch", + }) + span.Context.SetDatabase(apm.DatabaseSpanContext{ + Type: "elasticsearch", + Statement: statement, + User: username, + }) + + resp, err := r.r.RoundTrip(req) + if err != nil { + span.End() + } else { + span.Context.SetHTTPStatusCode(resp.StatusCode) + resp.Body = &responseBody{span: span, body: resp.Body} + } + return resp, err +} + +type responseBody struct { + span *apm.Span + body io.ReadCloser +} + +// Close closes the response body, and ends the span if it hasn't already been ended. +func (b *responseBody) Close() error { + b.endSpan() + return b.body.Close() +} + +// Read reads from the response body, and ends the span when io.EOF is returend if +// the span hasn't already been ended. +func (b *responseBody) Read(p []byte) (n int, err error) { + n, err = b.body.Read(p) + if err == io.EOF { + b.endSpan() + } + return n, err +} + +func (b *responseBody) endSpan() { + addr := (*unsafe.Pointer)(unsafe.Pointer(&b.span)) + if old := atomic.SwapPointer(addr, nil); old != nil { + (*apm.Span)(old).End() + } +} + +// ClientOption sets options for tracing client requests. +type ClientOption func(*roundTripper) + +// captureSearchStatement captures the search URI query or request body. +// +// If the request must be modified (i.e. because the body must be read), +// then captureSearchStatement returns a new *http.Request to be passed +// to the underlying http.RoundTripper. Otherwise, req is returned. +func captureSearchStatement(req *http.Request) (string, *http.Request) { + if !isSearchURL(req.URL) { + return "", req + } + + // If "q" is in query params, use that for statement. + if req.URL.RawQuery != "" { + query := req.URL.Query() + if statement := query.Get("q"); statement != "" { + return statement, req + } + } + if req.Body == nil || req.Body == http.NoBody { + return "", req + } + + var bodyBuf bytes.Buffer + if req.GetBody != nil { + // req.GetBody is defined, so we can read a copy of the + // request body instead of messing with the original request + // body. + body, err := req.GetBody() + if err != nil { + return "", req + } + if _, err := bodyBuf.ReadFrom(limitedBody(body, req.ContentLength)); err != nil { + body.Close() + return "", req + } + if err := body.Close(); err != nil { + return "", req + } + } else { + type readCloser struct { + io.Reader + io.Closer + } + newBody := &readCloser{Closer: req.Body} + reqCopy := *req + reqCopy.Body = newBody + if _, err := bodyBuf.ReadFrom(limitedBody(req.Body, req.ContentLength)); err != nil { + // Continue with the request, ensuring that req.Body returns + // the same content and error, but don't use the consumed body + // for the statement. + newBody.Reader = io.MultiReader(bytes.NewReader(bodyBuf.Bytes()), errorReader{err: err}) + return "", &reqCopy + } + newBody.Reader = io.MultiReader(bytes.NewReader(bodyBuf.Bytes()), req.Body) + req = &reqCopy + } + + var statement string + if req.Header.Get("Content-Encoding") == "gzip" { + if r, err := gzip.NewReader(&bodyBuf); err == nil { + if content, err := ioutil.ReadAll(r); err == nil { + statement = string(content) + } + } + } else { + statement = bodyBuf.String() + } + return statement, req +} + +func isSearchURL(url *url.URL) bool { + switch dir, file := path.Split(url.Path); file { + case "_search", "_msearch", "_rollup_search": + return true + case "template": + if dir == "" { + return false + } + switch _, file := path.Split(dir[:len(dir)-1]); file { + case "_search", "_msearch": + // ".../_search/template" or ".../_msearch/template" + return true + } + } + return false +} + +func limitedBody(r io.Reader, n int64) io.Reader { + // maxLimit is the maximum size of the request body that we'll read, + // set to 10000 to match the maximum length of the "db.statement" + // span context field. + const maxLimit = 10000 + if n <= 0 { + return r + } + if n > maxLimit { + n = maxLimit + } + return &io.LimitedReader{R: r, N: n} +} + +type errorReader struct { + err error +} + +func (r errorReader) Read(p []byte) (int, error) { + return 0, r.err +} diff --git a/vendor/go.elastic.co/apm/module/apmelasticsearch/doc.go b/vendor/go.elastic.co/apm/module/apmelasticsearch/doc.go new file mode 100644 index 00000000000..066dd0b9030 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmelasticsearch/doc.go @@ -0,0 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package apmelasticsearch provides support for tracing the +// HTTP transport layer of Elasticsearch clients. +package apmelasticsearch diff --git a/vendor/go.elastic.co/apm/module/apmelasticsearch/go.mod b/vendor/go.elastic.co/apm/module/apmelasticsearch/go.mod new file mode 100644 index 00000000000..4df7d8d7d9b --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmelasticsearch/go.mod @@ -0,0 +1,14 @@ +module go.elastic.co/apm/module/apmelasticsearch + +require ( + github.com/stretchr/testify v1.4.0 + go.elastic.co/apm v1.7.2 + go.elastic.co/apm/module/apmhttp v1.7.2 + golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 +) + +replace go.elastic.co/apm => ../.. + +replace go.elastic.co/apm/module/apmhttp => ../apmhttp + +go 1.13 diff --git a/vendor/go.elastic.co/apm/module/apmelasticsearch/go.sum b/vendor/go.elastic.co/apm/module/apmelasticsearch/go.sum new file mode 100644 index 00000000000..1976184453d --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmelasticsearch/go.sum @@ -0,0 +1,62 @@ +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= +github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elastic/go-sysinfo v1.1.1 h1:ZVlaLDyhVkDfjwPGU55CQRCRolNpc7P0BbyhhQZQmMI= +github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= +github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY= +github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= +github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.elastic.co/fastjson v1.0.0 h1:ooXV/ABvf+tBul26jcVViPT3sBir0PvXgibYB1IQQzg= +go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= diff --git a/vendor/go.elastic.co/apm/module/apmelasticsearch/requestname.go b/vendor/go.elastic.co/apm/module/apmelasticsearch/requestname.go new file mode 100644 index 00000000000..5dbb7d7be5d --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmelasticsearch/requestname.go @@ -0,0 +1,39 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build go1.10 + +package apmelasticsearch + +import ( + "net/http" + "strings" +) + +func requestName(req *http.Request) string { + const prefix = "Elasticsearch:" + path := strings.TrimLeft(req.URL.Path, "/") + + var b strings.Builder + b.Grow(len(prefix) + 1 + len(req.Method) + 1 + len(path)) + b.WriteString(prefix) + b.WriteRune(' ') + b.WriteString(req.Method) + b.WriteRune(' ') + b.WriteString(path) + return b.String() +} diff --git a/vendor/go.elastic.co/apm/module/apmelasticsearch/requestname_go19.go b/vendor/go.elastic.co/apm/module/apmelasticsearch/requestname_go19.go new file mode 100644 index 00000000000..14c3bc697bb --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmelasticsearch/requestname_go19.go @@ -0,0 +1,30 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build !go1.10 + +package apmelasticsearch + +import ( + "fmt" + "net/http" + "strings" +) + +func requestName(req *http.Request) string { + return fmt.Sprintf("Elasticsearch: %s %s", req.Method, strings.TrimLeft(req.URL.Path, "/")) +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/LICENSE b/vendor/go.elastic.co/apm/module/apmhttp/LICENSE new file mode 100644 index 00000000000..b1a731fb5a3 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Elasticsearch BV + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/go.elastic.co/apm/module/apmhttp/client.go b/vendor/go.elastic.co/apm/module/apmhttp/client.go new file mode 100644 index 00000000000..2d0df4034c7 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/client.go @@ -0,0 +1,200 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttp + +import ( + "io" + "net/http" + "sync/atomic" + "unsafe" + + "go.elastic.co/apm" +) + +// WrapClient returns a new *http.Client with all fields copied +// across, and the Transport field wrapped with WrapRoundTripper +// such that client requests are reported as spans to Elastic APM +// if their context contains a sampled transaction. +// +// Spans are started just before the request is sent, and ended +// immediately if the request returned an error (e.g. due to socket +// timeout, but not a valid response with a non-200 status code), +// or otherwise when the response body is fully consumed or closed. +// +// If c is nil, then http.DefaultClient is wrapped. +func WrapClient(c *http.Client, o ...ClientOption) *http.Client { + if c == nil { + c = http.DefaultClient + } + copied := *c + copied.Transport = WrapRoundTripper(copied.Transport, o...) + return &copied +} + +// WrapRoundTripper returns an http.RoundTripper wrapping r, reporting each +// request as a span to Elastic APM, if the request's context contains a +// sampled transaction. +// +// If r is nil, then http.DefaultTransport is wrapped. +func WrapRoundTripper(r http.RoundTripper, o ...ClientOption) http.RoundTripper { + if r == nil { + r = http.DefaultTransport + } + rt := &roundTripper{ + r: r, + requestName: ClientRequestName, + requestIgnorer: IgnoreNone, + } + for _, o := range o { + o(rt) + } + return rt +} + +type roundTripper struct { + r http.RoundTripper + requestName RequestNameFunc + requestIgnorer RequestIgnorerFunc +} + +// RoundTrip delegates to r.r, emitting a span if req's context +// contains a transaction. +func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + if r.requestIgnorer(req) { + return r.r.RoundTrip(req) + } + ctx := req.Context() + tx := apm.TransactionFromContext(ctx) + if tx == nil { + return r.r.RoundTrip(req) + } + + // RoundTrip is not supposed to mutate req, so copy req + // and set the trace-context headers only in the copy. + reqCopy := *req + reqCopy.Header = make(http.Header, len(req.Header)) + for k, v := range req.Header { + reqCopy.Header[k] = v + } + req = &reqCopy + + propagateLegacyHeader := tx.ShouldPropagateLegacyHeader() + traceContext := tx.TraceContext() + if !traceContext.Options.Recorded() { + r.setHeaders(req, traceContext, propagateLegacyHeader) + return r.r.RoundTrip(req) + } + + name := r.requestName(req) + span := tx.StartSpan(name, "external.http", apm.SpanFromContext(ctx)) + if !span.Dropped() { + traceContext = span.TraceContext() + ctx = apm.ContextWithSpan(ctx, span) + req = RequestWithContext(ctx, req) + span.Context.SetHTTPRequest(req) + } else { + span.End() + span = nil + } + + r.setHeaders(req, traceContext, propagateLegacyHeader) + resp, err := r.r.RoundTrip(req) + if span != nil { + if err != nil { + span.End() + } else { + span.Context.SetHTTPStatusCode(resp.StatusCode) + resp.Body = &responseBody{span: span, body: resp.Body} + } + } + return resp, err +} + +func (r *roundTripper) setHeaders(req *http.Request, traceContext apm.TraceContext, propagateLegacyHeader bool) { + headerValue := FormatTraceparentHeader(traceContext) + if propagateLegacyHeader { + req.Header.Set(ElasticTraceparentHeader, headerValue) + } + req.Header.Set(W3CTraceparentHeader, headerValue) + if tracestate := traceContext.State.String(); tracestate != "" { + req.Header.Set(TracestateHeader, tracestate) + } +} + +// CloseIdleConnections calls r.r.CloseIdleConnections if the method exists. +func (r *roundTripper) CloseIdleConnections() { + type closeIdler interface { + CloseIdleConnections() + } + if r, ok := r.r.(closeIdler); ok { + r.CloseIdleConnections() + } +} + +// CancelRequest calls r.r.CancelRequest(req) if the method exists. +func (r *roundTripper) CancelRequest(req *http.Request) { + type cancelRequester interface { + CancelRequest(*http.Request) + } + if r, ok := r.r.(cancelRequester); ok { + r.CancelRequest(req) + } +} + +type responseBody struct { + span *apm.Span + body io.ReadCloser +} + +// Close closes the response body, and ends the span if it hasn't already been ended. +func (b *responseBody) Close() error { + b.endSpan() + return b.body.Close() +} + +// Read reads from the response body, and ends the span when io.EOF is returend if +// the span hasn't already been ended. +func (b *responseBody) Read(p []byte) (n int, err error) { + n, err = b.body.Read(p) + if err == io.EOF { + b.endSpan() + } + return n, err +} + +func (b *responseBody) endSpan() { + addr := (*unsafe.Pointer)(unsafe.Pointer(&b.span)) + if old := atomic.SwapPointer(addr, nil); old != nil { + (*apm.Span)(old).End() + } +} + +// ClientOption sets options for tracing client requests. +type ClientOption func(*roundTripper) + +// WithClientRequestName returns a ClientOption which sets r as the function +// to use to obtain the span name for the given http request. +func WithClientRequestName(r RequestNameFunc) ClientOption { + if r == nil { + panic("r == nil") + } + + return ClientOption(func(rt *roundTripper) { + rt.requestName = r + }) +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/context.go b/vendor/go.elastic.co/apm/module/apmhttp/context.go new file mode 100644 index 00000000000..00c450eba08 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/context.go @@ -0,0 +1,40 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttp + +import ( + "fmt" +) + +var standardStatusCodeResults = [...]string{ + "HTTP 1xx", + "HTTP 2xx", + "HTTP 3xx", + "HTTP 4xx", + "HTTP 5xx", +} + +// StatusCodeResult returns the transaction result value to use for the given +// status code. +func StatusCodeResult(statusCode int) string { + switch i := statusCode / 100; i { + case 1, 2, 3, 4, 5: + return standardStatusCodeResults[i-1] + } + return fmt.Sprintf("HTTP %d", statusCode) +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/doc.go b/vendor/go.elastic.co/apm/module/apmhttp/doc.go new file mode 100644 index 00000000000..659281badcd --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/doc.go @@ -0,0 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package apmhttp provides a tracing middleware http.Handler for +// servers, and a tracing http.RoundTripper for clients. +package apmhttp diff --git a/vendor/go.elastic.co/apm/module/apmhttp/go.mod b/vendor/go.elastic.co/apm/module/apmhttp/go.mod new file mode 100644 index 00000000000..70240cdd353 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/go.mod @@ -0,0 +1,13 @@ +module go.elastic.co/apm/module/apmhttp + +require ( + github.com/pkg/errors v0.8.1 + github.com/stretchr/testify v1.4.0 + go.elastic.co/apm v1.7.2 + golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 + golang.org/x/text v0.3.2 // indirect +) + +replace go.elastic.co/apm => ../.. + +go 1.13 diff --git a/vendor/go.elastic.co/apm/module/apmhttp/go.sum b/vendor/go.elastic.co/apm/module/apmhttp/go.sum new file mode 100644 index 00000000000..1976184453d --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/go.sum @@ -0,0 +1,62 @@ +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= +github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elastic/go-sysinfo v1.1.1 h1:ZVlaLDyhVkDfjwPGU55CQRCRolNpc7P0BbyhhQZQmMI= +github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= +github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY= +github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= +github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +go.elastic.co/fastjson v1.0.0 h1:ooXV/ABvf+tBul26jcVViPT3sBir0PvXgibYB1IQQzg= +go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= diff --git a/vendor/go.elastic.co/apm/module/apmhttp/handler.go b/vendor/go.elastic.co/apm/module/apmhttp/handler.go new file mode 100644 index 00000000000..8aecf24ba48 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/handler.go @@ -0,0 +1,330 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttp + +import ( + "context" + "net/http" + + "go.elastic.co/apm" +) + +// Wrap returns an http.Handler wrapping h, reporting each request as +// a transaction to Elastic APM. +// +// By default, the returned Handler will use apm.DefaultTracer. +// Use WithTracer to specify an alternative tracer. +// +// By default, the returned Handler will recover panics, reporting +// them to the configured tracer. To override this behaviour, use +// WithRecovery. +func Wrap(h http.Handler, o ...ServerOption) http.Handler { + if h == nil { + panic("h == nil") + } + handler := &handler{ + handler: h, + tracer: apm.DefaultTracer, + requestName: ServerRequestName, + requestIgnorer: DefaultServerRequestIgnorer(), + } + for _, o := range o { + o(handler) + } + if handler.recovery == nil { + handler.recovery = NewTraceRecovery(handler.tracer) + } + return handler +} + +// handler wraps an http.Handler, reporting a new transaction for each request. +// +// The http.Request's context will be updated with the transaction. +type handler struct { + handler http.Handler + tracer *apm.Tracer + recovery RecoveryFunc + panicPropagation bool + requestName RequestNameFunc + requestIgnorer RequestIgnorerFunc +} + +// ServeHTTP delegates to h.Handler, tracing the transaction with +// h.Tracer, or apm.DefaultTracer if h.Tracer is nil. +func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if !h.tracer.Active() || h.requestIgnorer(req) { + h.handler.ServeHTTP(w, req) + return + } + tx, req := StartTransaction(h.tracer, h.requestName(req), req) + defer tx.End() + + body := h.tracer.CaptureHTTPRequestBody(req) + w, resp := WrapResponseWriter(w) + defer func() { + if v := recover(); v != nil { + if h.panicPropagation { + defer panic(v) + // 500 status code will be set only for APM transaction + // to allow other middleware to choose a different response code + if resp.StatusCode == 0 { + resp.StatusCode = http.StatusInternalServerError + } + } else if resp.StatusCode == 0 { + w.WriteHeader(http.StatusInternalServerError) + } + h.recovery(w, req, resp, body, tx, v) + } + SetTransactionContext(tx, req, resp, body) + body.Discard() + }() + h.handler.ServeHTTP(w, req) + if resp.StatusCode == 0 { + resp.StatusCode = http.StatusOK + } +} + +// StartTransaction returns a new Transaction with name, +// created with tracer, and taking trace context from req. +// +// If the transaction is not ignored, the request will be +// returned with the transaction added to its context. +func StartTransaction(tracer *apm.Tracer, name string, req *http.Request) (*apm.Transaction, *http.Request) { + traceContext, ok := getRequestTraceparent(req, ElasticTraceparentHeader) + if !ok { + traceContext, ok = getRequestTraceparent(req, W3CTraceparentHeader) + } + if ok { + traceContext.State, _ = ParseTracestateHeader(req.Header[TracestateHeader]...) + } + tx := tracer.StartTransactionOptions(name, "request", apm.TransactionOptions{TraceContext: traceContext}) + ctx := apm.ContextWithTransaction(req.Context(), tx) + req = RequestWithContext(ctx, req) + return tx, req +} + +func getRequestTraceparent(req *http.Request, header string) (apm.TraceContext, bool) { + if values := req.Header[header]; len(values) == 1 && values[0] != "" { + if c, err := ParseTraceparentHeader(values[0]); err == nil { + return c, true + } + } + return apm.TraceContext{}, false +} + +// SetTransactionContext sets tx.Result and, if the transaction is being +// sampled, sets tx.Context with information from req, resp, and body. +func SetTransactionContext(tx *apm.Transaction, req *http.Request, resp *Response, body *apm.BodyCapturer) { + tx.Result = StatusCodeResult(resp.StatusCode) + if !tx.Sampled() { + return + } + SetContext(&tx.Context, req, resp, body) +} + +// SetContext sets the context for a transaction or error using information +// from req, resp, and body. +func SetContext(ctx *apm.Context, req *http.Request, resp *Response, body *apm.BodyCapturer) { + ctx.SetHTTPRequest(req) + ctx.SetHTTPRequestBody(body) + ctx.SetHTTPStatusCode(resp.StatusCode) + ctx.SetHTTPResponseHeaders(resp.Headers) +} + +// WrapResponseWriter wraps an http.ResponseWriter and returns the wrapped +// value along with a *Response which will be filled in when the handler +// is called. The *Response value must not be inspected until after the +// request has been handled, to avoid data races. If neither of the +// ResponseWriter's Write or WriteHeader methods are called, then the +// response's StatusCode field will be zero. +// +// The returned http.ResponseWriter implements http.Pusher and http.Hijacker +// if and only if the provided http.ResponseWriter does. +func WrapResponseWriter(w http.ResponseWriter) (http.ResponseWriter, *Response) { + rw := responseWriter{ + ResponseWriter: w, + resp: Response{ + Headers: w.Header(), + }, + } + h, _ := w.(http.Hijacker) + p, _ := w.(http.Pusher) + switch { + case h != nil && p != nil: + rwhp := &responseWriterHijackerPusher{ + responseWriter: rw, + Hijacker: h, + Pusher: p, + } + return rwhp, &rwhp.resp + case h != nil: + rwh := &responseWriterHijacker{ + responseWriter: rw, + Hijacker: h, + } + return rwh, &rwh.resp + case p != nil: + rwp := &responseWriterPusher{ + responseWriter: rw, + Pusher: p, + } + return rwp, &rwp.resp + } + return &rw, &rw.resp +} + +// Response records details of the HTTP response. +type Response struct { + // StatusCode records the HTTP status code set via WriteHeader. + StatusCode int + + // Headers holds the headers set in the ResponseWriter. + Headers http.Header +} + +type responseWriter struct { + http.ResponseWriter + resp Response +} + +// WriteHeader sets w.resp.StatusCode and calls through to the embedded +// ResponseWriter. +func (w *responseWriter) WriteHeader(statusCode int) { + w.ResponseWriter.WriteHeader(statusCode) + w.resp.StatusCode = statusCode +} + +// Write calls through to the embedded ResponseWriter, setting +// w.resp.StatusCode to http.StatusOK if WriteHeader has not already +// been called. +func (w *responseWriter) Write(data []byte) (int, error) { + n, err := w.ResponseWriter.Write(data) + if w.resp.StatusCode == 0 { + w.resp.StatusCode = http.StatusOK + } + return n, err +} + +// CloseNotify returns w.closeNotify() if w.closeNotify is non-nil, +// otherwise it returns nil. +func (w *responseWriter) CloseNotify() <-chan bool { + if closeNotifier, ok := w.ResponseWriter.(http.CloseNotifier); ok { + return closeNotifier.CloseNotify() + } + return nil +} + +// Flush calls w.flush() if w.flush is non-nil, otherwise +// it does nothing. +func (w *responseWriter) Flush() { + if flusher, ok := w.ResponseWriter.(http.Flusher); ok { + flusher.Flush() + } +} + +type responseWriterHijacker struct { + responseWriter + http.Hijacker +} + +type responseWriterPusher struct { + responseWriter + http.Pusher +} + +type responseWriterHijackerPusher struct { + responseWriter + http.Hijacker + http.Pusher +} + +// ServerOption sets options for tracing server requests. +type ServerOption func(*handler) + +// WithTracer returns a ServerOption which sets t as the tracer +// to use for tracing server requests. +func WithTracer(t *apm.Tracer) ServerOption { + if t == nil { + panic("t == nil") + } + return func(h *handler) { + h.tracer = t + } +} + +// WithRecovery returns a ServerOption which sets r as the recovery +// function to use for tracing server requests. +func WithRecovery(r RecoveryFunc) ServerOption { + if r == nil { + panic("r == nil") + } + return func(h *handler) { + h.recovery = r + } +} + +// WithPanicPropagation returns a ServerOption which enable panic propagation. +// Any panic will be recovered and recorded as an error in a transaction, then +// panic will be caused again. +func WithPanicPropagation() ServerOption { + return func(h *handler) { + h.panicPropagation = true + } +} + +// RequestNameFunc is the type of a function for use in +// WithServerRequestName. +type RequestNameFunc func(*http.Request) string + +// WithServerRequestName returns a ServerOption which sets r as the function +// to use to obtain the transaction name for the given server request. +func WithServerRequestName(r RequestNameFunc) ServerOption { + if r == nil { + panic("r == nil") + } + return func(h *handler) { + h.requestName = r + } +} + +// RequestIgnorerFunc is the type of a function for use in +// WithServerRequestIgnorer. +type RequestIgnorerFunc func(*http.Request) bool + +// WithServerRequestIgnorer returns a ServerOption which sets r as the +// function to use to determine whether or not a server request should +// be ignored. If r is nil, all requests will be reported. +func WithServerRequestIgnorer(r RequestIgnorerFunc) ServerOption { + if r == nil { + r = IgnoreNone + } + return func(h *handler) { + h.requestIgnorer = r + } +} + +// RequestWithContext is equivalent to req.WithContext, except that the URL +// pointer is copied, rather than the contents. +func RequestWithContext(ctx context.Context, req *http.Request) *http.Request { + url := req.URL + req.URL = nil + reqCopy := req.WithContext(ctx) + reqCopy.URL = url + req.URL = url + return reqCopy +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/ignorer.go b/vendor/go.elastic.co/apm/module/apmhttp/ignorer.go new file mode 100644 index 00000000000..6ec56dd50f3 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/ignorer.go @@ -0,0 +1,81 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttp + +import ( + "net/http" + "regexp" + "sync" + + "go.elastic.co/apm/internal/configutil" + "go.elastic.co/apm/internal/wildcard" +) + +const ( + envIgnoreURLs = "ELASTIC_APM_IGNORE_URLS" +) + +var ( + defaultServerRequestIgnorerOnce sync.Once + defaultServerRequestIgnorer RequestIgnorerFunc = IgnoreNone +) + +// DefaultServerRequestIgnorer returns the default RequestIgnorer to use in +// handlers. If ELASTIC_APM_IGNORE_URLS is set, it will be treated as a +// comma-separated list of wildcard patterns; requests that match any of the +// patterns will be ignored. +func DefaultServerRequestIgnorer() RequestIgnorerFunc { + defaultServerRequestIgnorerOnce.Do(func() { + matchers := configutil.ParseWildcardPatternsEnv(envIgnoreURLs, nil) + if len(matchers) != 0 { + defaultServerRequestIgnorer = NewWildcardPatternsRequestIgnorer(matchers) + } + }) + return defaultServerRequestIgnorer +} + +// NewRegexpRequestIgnorer returns a RequestIgnorerFunc which matches requests' +// URLs against re. Note that for server requests, typically only Path and +// possibly RawQuery will be set, so the regular expression should take this +// into account. +func NewRegexpRequestIgnorer(re *regexp.Regexp) RequestIgnorerFunc { + if re == nil { + panic("re == nil") + } + return func(r *http.Request) bool { + return re.MatchString(r.URL.String()) + } +} + +// NewWildcardPatternsRequestIgnorer returns a RequestIgnorerFunc which matches +// requests' URLs against any of the matchers. Note that for server requests, +// typically only Path and possibly RawQuery will be set, so the wildcard patterns +// should take this into account. +func NewWildcardPatternsRequestIgnorer(matchers wildcard.Matchers) RequestIgnorerFunc { + if len(matchers) == 0 { + panic("len(matchers) == 0") + } + return func(r *http.Request) bool { + return matchers.MatchAny(r.URL.String()) + } +} + +// IgnoreNone is a RequestIgnorerFunc which ignores no requests. +func IgnoreNone(*http.Request) bool { + return false +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/recovery.go b/vendor/go.elastic.co/apm/module/apmhttp/recovery.go new file mode 100644 index 00000000000..988769c1456 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/recovery.go @@ -0,0 +1,60 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttp + +import ( + "net/http" + + "go.elastic.co/apm" +) + +// RecoveryFunc is the type of a function for use in WithRecovery. +type RecoveryFunc func( + w http.ResponseWriter, + req *http.Request, + resp *Response, + body *apm.BodyCapturer, + tx *apm.Transaction, + recovered interface{}, +) + +// NewTraceRecovery returns a RecoveryFunc for use in WithRecovery. +// +// The returned RecoveryFunc will report recovered error to Elastic APM +// using the given Tracer, or apm.DefaultTracer if t is nil. The +// error will be linked to the given transaction. +// +// If headers have not already been written, a 500 response will be sent. +func NewTraceRecovery(t *apm.Tracer) RecoveryFunc { + if t == nil { + t = apm.DefaultTracer + } + return func( + w http.ResponseWriter, + req *http.Request, + resp *Response, + body *apm.BodyCapturer, + tx *apm.Transaction, + recovered interface{}, + ) { + e := t.Recovered(recovered) + e.SetTransaction(tx) + SetContext(&e.Context, req, resp, body) + e.Send() + } +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/requestname.go b/vendor/go.elastic.co/apm/module/apmhttp/requestname.go new file mode 100644 index 00000000000..877aac15306 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/requestname.go @@ -0,0 +1,56 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build go1.10 + +package apmhttp + +import ( + "net/http" + "strings" +) + +// UnknownRouteRequestName returns the transaction name for the server request, req, +// when the route could not be determined. +func UnknownRouteRequestName(req *http.Request) string { + const suffix = " unknown route" + var b strings.Builder + b.Grow(len(req.Method) + len(suffix)) + b.WriteString(req.Method) + b.WriteString(suffix) + return b.String() +} + +// ServerRequestName returns the transaction name for the server request, req. +func ServerRequestName(req *http.Request) string { + var b strings.Builder + b.Grow(len(req.Method) + len(req.URL.Path) + 1) + b.WriteString(req.Method) + b.WriteByte(' ') + b.WriteString(req.URL.Path) + return b.String() +} + +// ClientRequestName returns the span name for the client request, req. +func ClientRequestName(req *http.Request) string { + var b strings.Builder + b.Grow(len(req.Method) + len(req.URL.Host) + 1) + b.WriteString(req.Method) + b.WriteByte(' ') + b.WriteString(req.URL.Host) + return b.String() +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/requestname_go19.go b/vendor/go.elastic.co/apm/module/apmhttp/requestname_go19.go new file mode 100644 index 00000000000..2a84ec75955 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/requestname_go19.go @@ -0,0 +1,46 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build !go1.10 + +package apmhttp + +import "net/http" + +// UnknownRouteRequestName returns the transaction name for the server request, req, +// when the route could not be determined. +func UnknownRouteRequestName(req *http.Request) string { + return req.Method + " unknown route" +} + +// ServerRequestName returns the transaction name for the server request, req. +func ServerRequestName(req *http.Request) string { + buf := make([]byte, len(req.Method)+len(req.URL.Path)+1) + n := copy(buf, req.Method) + buf[n] = ' ' + copy(buf[n+1:], req.URL.Path) + return string(buf) +} + +// ClientRequestName returns the span name for the client request, req. +func ClientRequestName(req *http.Request) string { + buf := make([]byte, len(req.Method)+len(req.URL.Host)+1) + n := copy(buf, req.Method) + buf[n] = ' ' + copy(buf[n+1:], req.URL.Host) + return string(buf) +} diff --git a/vendor/go.elastic.co/apm/module/apmhttp/traceheaders.go b/vendor/go.elastic.co/apm/module/apmhttp/traceheaders.go new file mode 100644 index 00000000000..8a00a70db30 --- /dev/null +++ b/vendor/go.elastic.co/apm/module/apmhttp/traceheaders.go @@ -0,0 +1,168 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmhttp + +import ( + "encoding/hex" + "fmt" + "strings" + + "github.com/pkg/errors" + + "go.elastic.co/apm" +) + +const ( + // TraceparentHeader is the HTTP header for trace propagation. + // + // For backwards compatibility, this is currently an alias for + // for ElasticTraceparentHeader, but the more specific constants + // below should be preferred. In a future version this will be + // replaced by the standard W3C header. + TraceparentHeader = ElasticTraceparentHeader + + // ElasticTraceparentHeader is the legacy HTTP header for trace propagation, + // maintained for backwards compatibility with older agents. + ElasticTraceparentHeader = "Elastic-Apm-Traceparent" + + // W3CTraceparentHeader is the standard W3C Trace-Context HTTP + // header for trace propagation. + W3CTraceparentHeader = "Traceparent" + + // TracestateHeader is the standard W3C Trace-Context HTTP header + // for vendor-specific trace propagation. + TracestateHeader = "Tracestate" +) + +// FormatTraceparentHeader formats the given trace context as a +// traceparent header. +func FormatTraceparentHeader(c apm.TraceContext) string { + const version = 0 + return fmt.Sprintf("%02x-%032x-%016x-%02x", 0, c.Trace[:], c.Span[:], c.Options) +} + +// ParseTraceparentHeader parses the given header, which is expected to be in +// the W3C Trace-Context traceparent format according to W3C Editor's Draft 23 May 2018: +// https://w3c.github.io/trace-context/#traceparent-field +// +// Note that the returned TraceContext's Trace and Span fields are not necessarily +// valid. The caller must decide whether or not it wishes to disregard invalid +// trace/span IDs, and validate them as required using their provided Validate +// methods. +// +// The returned TraceContext's TraceState field will be the empty value. Use +// ParseTracestateHeader to parse that separately. +func ParseTraceparentHeader(h string) (apm.TraceContext, error) { + var out apm.TraceContext + if len(h) < 3 || h[2] != '-' { + return out, errors.Errorf("invalid traceparent header %q", h) + } + var version byte + if !strings.HasPrefix(h, "00") { + decoded, err := hex.DecodeString(h[:2]) + if err != nil { + return out, errors.Wrap(err, "error decoding traceparent header version") + } + version = decoded[0] + } + h = h[3:] + + switch version { + case 255: + // "Version 255 is invalid." + return out, errors.Errorf("traceparent header version 255 is forbidden") + default: + // "If higher version is detected - implementation SHOULD try to parse it." + fallthrough + case 0: + // Version 00: + // + // version-format = trace-id "-" span-id "-" trace-options + // trace-id = 32HEXDIG + // span-id = 16HEXDIG + // trace-options = 2HEXDIG + const ( + traceIDEnd = 32 + spanIDStart = traceIDEnd + 1 + spanIDEnd = spanIDStart + 16 + traceOptionsStart = spanIDEnd + 1 + traceOptionsEnd = traceOptionsStart + 2 + ) + switch { + case len(h) < traceOptionsEnd, + h[traceIDEnd] != '-', + h[spanIDEnd] != '-', + version == 0 && len(h) != traceOptionsEnd, + version > 0 && len(h) > traceOptionsEnd && h[traceOptionsEnd] != '-': + return out, errors.Errorf("invalid version %d traceparent header %q", version, h) + } + if _, err := hex.Decode(out.Trace[:], []byte(h[:traceIDEnd])); err != nil { + return out, errors.Wrapf(err, "error decoding trace-id for version %d", version) + } + if err := out.Trace.Validate(); err != nil { + return out, errors.Wrap(err, "invalid trace-id") + } + if _, err := hex.Decode(out.Span[:], []byte(h[spanIDStart:spanIDEnd])); err != nil { + return out, errors.Wrapf(err, "error decoding span-id for version %d", version) + } + if err := out.Span.Validate(); err != nil { + return out, errors.Wrap(err, "invalid span-id") + } + var traceOptions [1]byte + if _, err := hex.Decode(traceOptions[:], []byte(h[traceOptionsStart:traceOptionsEnd])); err != nil { + return out, errors.Wrapf(err, "error decoding trace-options for version %d", version) + } + out.Options = apm.TraceOptions(traceOptions[0]) + return out, nil + } +} + +// ParseTracestateHeader parses the given header, which is expected to be in the +// W3C Trace-Context tracestate format according to W3C Editor's Draft 18 Nov 2019: +// https://w3c.github.io/trace-context/#tracestate-header +// +// Note that the returned TraceState is not necessarily valid. The caller must +// decide whether or not it wishes to disregard invalid tracestate entries, and +// validate them as required using their provided Validate methods. +// +// Multiple header values may be presented, in which case they will be treated as +// if they are concatenated together with commas. +func ParseTracestateHeader(h ...string) (apm.TraceState, error) { + var entries []apm.TraceStateEntry + for _, h := range h { + for { + h = strings.TrimSpace(h) + if h == "" { + break + } + kv := h + if comma := strings.IndexRune(h, ','); comma != -1 { + kv = strings.TrimSpace(h[:comma]) + h = h[comma+1:] + } else { + h = "" + } + equal := strings.IndexRune(kv, '=') + if equal == -1 { + return apm.TraceState{}, errors.New("missing '=' in tracestate entry") + } + entries = append(entries, apm.TraceStateEntry{Key: kv[:equal], Value: kv[equal+1:]}) + } + } + return apm.NewTraceState(entries...), nil +} diff --git a/vendor/go.elastic.co/apm/profiling.go b/vendor/go.elastic.co/apm/profiling.go new file mode 100644 index 00000000000..7cc3fe9affe --- /dev/null +++ b/vendor/go.elastic.co/apm/profiling.go @@ -0,0 +1,164 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "bytes" + "context" + "io" + "runtime/pprof" + "time" + + "github.com/pkg/errors" +) + +type profilingState struct { + profileType string + profileStart func(io.Writer) error + profileStop func() + sender profileSender + + interval time.Duration + duration time.Duration // not relevant to all profiles + + timer *time.Timer + timerStart time.Time + buf bytes.Buffer + finished chan struct{} +} + +// newCPUProfilingState calls newProfilingState with the +// profiler type set to "cpu", and using pprof.StartCPUProfile +// and pprof.StopCPUProfile. +func newCPUProfilingState(sender profileSender) *profilingState { + return newProfilingState("cpu", pprof.StartCPUProfile, pprof.StopCPUProfile, sender) +} + +// newHeapProfilingState calls newProfilingState with the +// profiler type set to "heap", and using pprof.Lookup("heap").WriteTo(writer, 0). +func newHeapProfilingState(sender profileSender) *profilingState { + return newLookupProfilingState("heap", sender) +} + +func newLookupProfilingState(name string, sender profileSender) *profilingState { + profileStart := func(w io.Writer) error { + profile := pprof.Lookup(name) + if profile == nil { + return errors.Errorf("no profile called %q", name) + } + return profile.WriteTo(w, 0) + } + return newProfilingState("heap", profileStart, func() {}, sender) +} + +// newProfilingState returns a new profilingState, +// with its timer stopped. The timer may be started +// by calling profilingState.updateConfig. +func newProfilingState( + profileType string, + profileStart func(io.Writer) error, + profileStop func(), + sender profileSender, +) *profilingState { + state := &profilingState{ + profileType: profileType, + profileStart: profileStart, + profileStop: profileStop, + sender: sender, + timer: time.NewTimer(0), + finished: make(chan struct{}, 1), + } + if !state.timer.Stop() { + <-state.timer.C + } + return state +} + +func (state *profilingState) updateConfig(interval, duration time.Duration) { + if state.sender == nil { + // No profile sender, no point in starting a timer. + return + } + state.duration = duration + if state.interval == interval { + return + } + if state.timerStart.IsZero() { + state.interval = interval + state.resetTimer() + } + // TODO(axw) handle extending/cutting short running timers once + // it is possible to dynamically control profiling configuration. +} + +func (state *profilingState) resetTimer() { + if state.interval != 0 { + state.timer.Reset(state.interval) + state.timerStart = time.Now() + } else { + state.timerStart = time.Time{} + } +} + +// start spawns a goroutine that will capture a profile, send it using state.sender, +// and finally signal state.finished. +// +// start will return immediately after spawning the goroutine. +func (state *profilingState) start(ctx context.Context, logger Logger, metadata io.Reader) { + // The state.duration field may be updated after the goroutine starts, + // by the caller, so it must be read outside the goroutine. + duration := state.duration + go func() { + defer func() { state.finished <- struct{}{} }() + if err := state.profile(ctx, duration); err != nil { + if logger != nil && ctx.Err() == nil { + logger.Errorf("%s", err) + } + return + } + // TODO(axw) backoff like SendStream requests + if err := state.sender.SendProfile(ctx, metadata, &state.buf); err != nil { + if logger != nil && ctx.Err() == nil { + logger.Errorf("failed to send %s profile: %s", state.profileType, err) + } + } + }() +} + +func (state *profilingState) profile(ctx context.Context, duration time.Duration) error { + state.buf.Reset() + if err := state.profileStart(&state.buf); err != nil { + return errors.Wrapf(err, "failed to start %s profile", state.profileType) + } + defer state.profileStop() + + if duration > 0 { + timer := time.NewTimer(duration) + defer timer.Stop() + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + } + } + return nil +} + +type profileSender interface { + SendProfile(ctx context.Context, metadata io.Reader, profile ...io.Reader) error +} diff --git a/vendor/go.elastic.co/apm/sampler.go b/vendor/go.elastic.co/apm/sampler.go new file mode 100644 index 00000000000..3cf4591c646 --- /dev/null +++ b/vendor/go.elastic.co/apm/sampler.go @@ -0,0 +1,66 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "encoding/binary" + "math" + "math/big" + + "github.com/pkg/errors" +) + +// Sampler provides a means of sampling transactions. +type Sampler interface { + // Sample indicates whether or not a transaction + // should be sampled. This method will be invoked + // by calls to Tracer.StartTransaction for the root + // of a trace, so it must be goroutine-safe, and + // should avoid synchronization as far as possible. + Sample(TraceContext) bool +} + +// NewRatioSampler returns a new Sampler with the given ratio +// +// A ratio of 1.0 samples 100% of transactions, a ratio of 0.5 +// samples ~50%, and so on. If the ratio provided does not lie +// within the range [0,1.0], NewRatioSampler will panic. +// +// The returned Sampler bases its decision on the value of the +// transaction ID, so there is no synchronization involved. +func NewRatioSampler(r float64) Sampler { + if r < 0 || r > 1.0 { + panic(errors.Errorf("ratio %v out of range [0,1.0]", r)) + } + var x big.Float + x.SetUint64(math.MaxUint64) + x.Mul(&x, big.NewFloat(r)) + ceil, _ := x.Uint64() + return ratioSampler{ceil} +} + +type ratioSampler struct { + ceil uint64 +} + +// Sample samples the transaction according to the configured +// ratio and pseudo-random source. +func (s ratioSampler) Sample(c TraceContext) bool { + v := binary.BigEndian.Uint64(c.Span[:]) + return v > 0 && v-1 < s.ceil +} diff --git a/vendor/go.elastic.co/apm/sanitizer.go b/vendor/go.elastic.co/apm/sanitizer.go new file mode 100644 index 00000000000..7f9014840ec --- /dev/null +++ b/vendor/go.elastic.co/apm/sanitizer.go @@ -0,0 +1,66 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "go.elastic.co/apm/internal/wildcard" + "go.elastic.co/apm/model" +) + +const redacted = "[REDACTED]" + +// sanitizeRequest sanitizes HTTP request data, redacting the +// values of cookies, headers and forms whose corresponding keys +// match any of the given wildcard patterns. +func sanitizeRequest(r *model.Request, matchers wildcard.Matchers) { + for _, c := range r.Cookies { + if !matchers.MatchAny(c.Name) { + continue + } + c.Value = redacted + } + sanitizeHeaders(r.Headers, matchers) + if r.Body != nil && r.Body.Form != nil { + for key, values := range r.Body.Form { + if !matchers.MatchAny(key) { + continue + } + for i := range values { + values[i] = redacted + } + } + } +} + +// sanitizeResponse sanitizes HTTP response data, redacting +// the values of response headers whose corresponding keys +// match any of the given wildcard patterns. +func sanitizeResponse(r *model.Response, matchers wildcard.Matchers) { + sanitizeHeaders(r.Headers, matchers) +} + +func sanitizeHeaders(headers model.Headers, matchers wildcard.Matchers) { + for i := range headers { + h := &headers[i] + if !matchers.MatchAny(h.Key) || len(h.Values) == 0 { + continue + } + h.Values = h.Values[:1] + h.Values[0] = redacted + } +} diff --git a/vendor/go.elastic.co/apm/span.go b/vendor/go.elastic.co/apm/span.go new file mode 100644 index 00000000000..85b244c1db9 --- /dev/null +++ b/vendor/go.elastic.co/apm/span.go @@ -0,0 +1,415 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + cryptorand "crypto/rand" + "encoding/binary" + "strings" + "sync" + "time" + + "go.elastic.co/apm/stacktrace" +) + +// droppedSpanDataPool holds *SpanData which are used when the span is created +// for a nil or non-sampled trace context, without a transaction reference. +// +// Spans started with a non-nil transaction, even if it is non-sampled, are +// always created with the transaction's tracer span pool. +var droppedSpanDataPool sync.Pool + +// StartSpan starts and returns a new Span within the transaction, +// with the specified name, type, and optional parent span, and +// with the start time set to the current time. +// +// StartSpan always returns a non-nil Span, with a non-nil SpanData +// field. Its End method must be called when the span completes. +// +// If the span type contains two dots, they are assumed to separate +// the span type, subtype, and action; a single dot separates span +// type and subtype, and the action will not be set. +// +// StartSpan is equivalent to calling StartSpanOptions with +// SpanOptions.Parent set to the trace context of parent if +// parent is non-nil. +func (tx *Transaction) StartSpan(name, spanType string, parent *Span) *Span { + return tx.StartSpanOptions(name, spanType, SpanOptions{ + parent: parent, + }) +} + +// StartSpanOptions starts and returns a new Span within the transaction, +// with the specified name, type, and options. +// +// StartSpan always returns a non-nil Span. Its End method must be called +// when the span completes. +// +// If the span type contains two dots, they are assumed to separate the +// span type, subtype, and action; a single dot separates span type and +// subtype, and the action will not be set. +func (tx *Transaction) StartSpanOptions(name, spanType string, opts SpanOptions) *Span { + if tx == nil { + return newDroppedSpan() + } + + if opts.Parent == (TraceContext{}) { + if opts.parent != nil { + opts.Parent = opts.parent.TraceContext() + } else { + opts.Parent = tx.traceContext + } + } + transactionID := tx.traceContext.Span + + // Prevent tx from being ended while we're starting a span. + tx.mu.RLock() + defer tx.mu.RUnlock() + if tx.ended() { + return tx.tracer.StartSpan(name, spanType, transactionID, opts) + } + + // Calculate the span time relative to the transaction timestamp so + // that wall-clock adjustments occurring after the transaction start + // don't affect the span timestamp. + if opts.Start.IsZero() { + opts.Start = tx.timestamp.Add(time.Since(tx.timestamp)) + } else { + opts.Start = tx.timestamp.Add(opts.Start.Sub(tx.timestamp)) + } + span := tx.tracer.startSpan(name, spanType, transactionID, opts) + span.tx = tx + span.parent = opts.parent + + // Guard access to spansCreated, spansDropped, rand, and childrenTimer. + tx.TransactionData.mu.Lock() + defer tx.TransactionData.mu.Unlock() + if !span.traceContext.Options.Recorded() { + span.tracer = nil // span is dropped + } else if tx.maxSpans >= 0 && tx.spansCreated >= tx.maxSpans { + span.tracer = nil // span is dropped + tx.spansDropped++ + } else { + if opts.SpanID.Validate() == nil { + span.traceContext.Span = opts.SpanID + } else { + binary.LittleEndian.PutUint64(span.traceContext.Span[:], tx.rand.Uint64()) + } + span.stackFramesMinDuration = tx.spanFramesMinDuration + span.stackTraceLimit = tx.stackTraceLimit + tx.spansCreated++ + } + + if tx.breakdownMetricsEnabled { + if span.parent != nil { + span.parent.mu.Lock() + defer span.parent.mu.Unlock() + if !span.parent.ended() { + span.parent.childrenTimer.childStarted(span.timestamp) + } + } else { + tx.childrenTimer.childStarted(span.timestamp) + } + } + return span +} + +// StartSpan returns a new Span with the specified name, type, transaction ID, +// and options. The parent transaction context and transaction IDs must have +// valid, non-zero values, or else the span will be dropped. +// +// In most cases, you should use Transaction.StartSpan or Transaction.StartSpanOptions. +// This method is provided for corner-cases, such as starting a span after the +// containing transaction's End method has been called. Spans created in this +// way will not have the "max spans" configuration applied, nor will they be +// considered in any transaction's span count. +func (t *Tracer) StartSpan(name, spanType string, transactionID SpanID, opts SpanOptions) *Span { + if opts.Parent.Trace.Validate() != nil || opts.Parent.Span.Validate() != nil || transactionID.Validate() != nil { + return newDroppedSpan() + } + if !opts.Parent.Options.Recorded() { + return newDroppedSpan() + } + var spanID SpanID + if opts.SpanID.Validate() == nil { + spanID = opts.SpanID + } else { + if _, err := cryptorand.Read(spanID[:]); err != nil { + return newDroppedSpan() + } + } + if opts.Start.IsZero() { + opts.Start = time.Now() + } + span := t.startSpan(name, spanType, transactionID, opts) + span.traceContext.Span = spanID + + instrumentationConfig := t.instrumentationConfig() + span.stackFramesMinDuration = instrumentationConfig.spanFramesMinDuration + span.stackTraceLimit = instrumentationConfig.stackTraceLimit + + return span +} + +// SpanOptions holds options for Transaction.StartSpanOptions and Tracer.StartSpan. +type SpanOptions struct { + // Parent, if non-zero, holds the trace context of the parent span. + Parent TraceContext + + // SpanID holds the ID to assign to the span. If this is zero, a new ID + // will be generated and used instead. + SpanID SpanID + + // parent, if non-nil, holds the parent span. + // + // This is only used if Parent is zero, and is only available to internal + // callers of Transaction.StartSpanOptions. + parent *Span + + // Start is the start time of the span. If this has the zero value, + // time.Now() will be used instead. + // + // When a span is created using Transaction.StartSpanOptions, the + // span timestamp is internally calculated relative to the transaction + // timestamp. + // + // When Tracer.StartSpan is used, this timestamp should be pre-calculated + // as relative from the transaction start time, i.e. by calculating the + // time elapsed since the transaction started, and adding that to the + // transaction timestamp. Calculating the timstamp in this way will ensure + // monotonicity of events within a transaction. + Start time.Time +} + +func (t *Tracer) startSpan(name, spanType string, transactionID SpanID, opts SpanOptions) *Span { + sd, _ := t.spanDataPool.Get().(*SpanData) + if sd == nil { + sd = &SpanData{Duration: -1} + } + span := &Span{tracer: t, SpanData: sd} + span.Name = name + span.traceContext = opts.Parent + span.parentID = opts.Parent.Span + span.transactionID = transactionID + span.timestamp = opts.Start + span.Type = spanType + if dot := strings.IndexRune(spanType, '.'); dot != -1 { + span.Type = spanType[:dot] + span.Subtype = spanType[dot+1:] + if dot := strings.IndexRune(span.Subtype, '.'); dot != -1 { + span.Subtype, span.Action = span.Subtype[:dot], span.Subtype[dot+1:] + } + } + return span +} + +// newDropped returns a new Span with a non-nil SpanData. +func newDroppedSpan() *Span { + span, _ := droppedSpanDataPool.Get().(*Span) + if span == nil { + span = &Span{SpanData: &SpanData{}} + } + return span +} + +// Span describes an operation within a transaction. +type Span struct { + tracer *Tracer // nil if span is dropped + tx *Transaction + parent *Span + traceContext TraceContext + transactionID SpanID + + mu sync.RWMutex + + // SpanData holds the span data. This field is set to nil when + // the span's End method is called. + *SpanData +} + +// TraceContext returns the span's TraceContext. +func (s *Span) TraceContext() TraceContext { + if s == nil { + return TraceContext{} + } + return s.traceContext +} + +// SetStacktrace sets the stacktrace for the span, +// skipping the first skip number of frames, +// excluding the SetStacktrace function. +func (s *Span) SetStacktrace(skip int) { + if s == nil || s.dropped() { + return + } + s.mu.RLock() + defer s.mu.RUnlock() + if s.ended() { + return + } + s.SpanData.setStacktrace(skip + 1) +} + +// Dropped indicates whether or not the span is dropped, meaning it will not +// be included in any transaction. Spans are dropped by Transaction.StartSpan +// if the transaction is nil, non-sampled, or the transaction's max spans +// limit has been reached. +// +// Dropped may be used to avoid any expensive computation required to set +// the span's context. +func (s *Span) Dropped() bool { + return s == nil || s.dropped() +} + +func (s *Span) dropped() bool { + return s.tracer == nil +} + +// End marks the s as being complete; s must not be used after this. +// +// If s.Duration has not been set, End will set it to the elapsed time +// since the span's start time. +func (s *Span) End() { + s.mu.Lock() + defer s.mu.Unlock() + if s.ended() { + return + } + if s.Duration < 0 { + s.Duration = time.Since(s.timestamp) + } + if s.dropped() { + if s.tx == nil { + droppedSpanDataPool.Put(s.SpanData) + } else { + s.reportSelfTime() + s.reset(s.tx.tracer) + } + s.SpanData = nil + return + } + if len(s.stacktrace) == 0 && s.Duration >= s.stackFramesMinDuration { + s.setStacktrace(1) + } + if s.tx != nil { + s.reportSelfTime() + } + s.enqueue() + s.SpanData = nil +} + +// reportSelfTime reports the span's self-time to its transaction, and informs +// the parent that it has ended in order for the parent to later calculate its +// own self-time. +// +// This must only be called from Span.End, with s.mu.Lock held for writing and +// s.Duration set. +func (s *Span) reportSelfTime() { + endTime := s.timestamp.Add(s.Duration) + + // TODO(axw) try to find a way to not lock the transaction when + // ending every span. We already lock them when starting spans. + s.tx.mu.RLock() + defer s.tx.mu.RUnlock() + if s.tx.ended() || !s.tx.breakdownMetricsEnabled { + return + } + + s.tx.TransactionData.mu.Lock() + defer s.tx.TransactionData.mu.Unlock() + if s.parent != nil { + s.parent.mu.Lock() + if !s.parent.ended() { + s.parent.childrenTimer.childEnded(endTime) + } + s.parent.mu.Unlock() + } else { + s.tx.childrenTimer.childEnded(endTime) + } + s.tx.spanTimings.add(s.Type, s.Subtype, s.Duration-s.childrenTimer.finalDuration(endTime)) +} + +func (s *Span) enqueue() { + event := tracerEvent{eventType: spanEvent} + event.span.Span = s + event.span.SpanData = s.SpanData + select { + case s.tracer.events <- event: + default: + // Enqueuing a span should never block. + s.tracer.statsMu.Lock() + s.tracer.stats.SpansDropped++ + s.tracer.statsMu.Unlock() + s.reset(s.tracer) + } +} + +func (s *Span) ended() bool { + return s.SpanData == nil +} + +// SpanData holds the details for a span, and is embedded inside Span. +// When a span is ended or discarded, its SpanData field will be set +// to nil. +type SpanData struct { + parentID SpanID + stackFramesMinDuration time.Duration + stackTraceLimit int + timestamp time.Time + childrenTimer childrenTimer + + // Name holds the span name, initialized with the value passed to StartSpan. + Name string + + // Type holds the overarching span type, such as "db", and will be initialized + // with the value passed to StartSpan. + Type string + + // Subtype holds the span subtype, such as "mysql". This will initially be empty, + // and can be set after starting the span. + Subtype string + + // Action holds the span action, such as "query". This will initially be empty, + // and can be set after starting the span. + Action string + + // Duration holds the span duration, initialized to -1. + // + // If you do not update Duration, calling Span.End will calculate the + // duration based on the elapsed time since the span's start time. + Duration time.Duration + + // Context describes the context in which span occurs. + Context SpanContext + + stacktrace []stacktrace.Frame +} + +func (s *SpanData) setStacktrace(skip int) { + s.stacktrace = stacktrace.AppendStacktrace(s.stacktrace[:0], skip+1, s.stackTraceLimit) +} + +func (s *SpanData) reset(tracer *Tracer) { + *s = SpanData{ + Context: s.Context, + Duration: -1, + stacktrace: s.stacktrace[:0], + } + s.Context.reset() + tracer.spanDataPool.Put(s) +} diff --git a/vendor/go.elastic.co/apm/spancontext.go b/vendor/go.elastic.co/apm/spancontext.go new file mode 100644 index 00000000000..180fe1ddc34 --- /dev/null +++ b/vendor/go.elastic.co/apm/spancontext.go @@ -0,0 +1,193 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "fmt" + "net/http" + "net/url" + "strings" + + "go.elastic.co/apm/internal/apmhttputil" + "go.elastic.co/apm/model" +) + +// SpanContext provides methods for setting span context. +type SpanContext struct { + model model.SpanContext + destination model.DestinationSpanContext + destinationService model.DestinationServiceSpanContext + databaseRowsAffected int64 + database model.DatabaseSpanContext + http model.HTTPSpanContext +} + +// DatabaseSpanContext holds database span context. +type DatabaseSpanContext struct { + // Instance holds the database instance name. + Instance string + + // Statement holds the statement executed in the span, + // e.g. "SELECT * FROM foo". + Statement string + + // Type holds the database type, e.g. "sql". + Type string + + // User holds the username used for database access. + User string +} + +// DestinationServiceSpanContext holds destination service span span. +type DestinationServiceSpanContext struct { + // Name holds a name for the destination service, which may be used + // for grouping and labeling in service maps. + Name string + + // Resource holds an identifier for a destination service resource, + // such as a message queue. + Resource string +} + +func (c *SpanContext) build() *model.SpanContext { + switch { + case len(c.model.Tags) != 0: + case c.model.Database != nil: + case c.model.HTTP != nil: + case c.model.Destination != nil: + default: + return nil + } + return &c.model +} + +func (c *SpanContext) reset() { + *c = SpanContext{ + model: model.SpanContext{ + Tags: c.model.Tags[:0], + }, + } +} + +// SetTag calls SetLabel(key, value). +// +// SetTag is deprecated, and will be removed in a future major version. +func (c *SpanContext) SetTag(key, value string) { + c.SetLabel(key, value) +} + +// SetLabel sets a label in the context. +// +// Invalid characters ('.', '*', and '"') in the key will be replaced with +// underscores. +// +// If the value is numerical or boolean, then it will be sent to the server +// as a JSON number or boolean; otherwise it will converted to a string, using +// `fmt.Sprint` if necessary. String values longer than 1024 characters will +// be truncated. +func (c *SpanContext) SetLabel(key string, value interface{}) { + // Note that we do not attempt to de-duplicate the keys. + // This is OK, since json.Unmarshal will always take the + // final instance. + c.model.Tags = append(c.model.Tags, model.IfaceMapItem{ + Key: cleanLabelKey(key), + Value: makeLabelValue(value), + }) +} + +// SetDatabase sets the span context for database-related operations. +func (c *SpanContext) SetDatabase(db DatabaseSpanContext) { + c.database = model.DatabaseSpanContext{ + Instance: truncateString(db.Instance), + Statement: truncateLongString(db.Statement), + Type: truncateString(db.Type), + User: truncateString(db.User), + } + c.model.Database = &c.database +} + +// SetDatabaseRowsAffected records the number of rows affected by +// a database operation. +func (c *SpanContext) SetDatabaseRowsAffected(n int64) { + c.databaseRowsAffected = n + c.database.RowsAffected = &c.databaseRowsAffected +} + +// SetHTTPRequest sets the details of the HTTP request in the context. +// +// This function relates to client requests. If the request URL contains +// user info, it will be removed and excluded from the stored URL. +// +// SetHTTPRequest makes implicit calls to SetDestinationAddress and +// SetDestinationService, using details from req.URL. +func (c *SpanContext) SetHTTPRequest(req *http.Request) { + if req.URL == nil { + return + } + c.http.URL = req.URL + c.model.HTTP = &c.http + + addr, port := apmhttputil.DestinationAddr(req) + c.SetDestinationAddress(addr, port) + + destinationServiceURL := url.URL{Scheme: req.URL.Scheme, Host: req.URL.Host} + destinationServiceResource := destinationServiceURL.Host + if port != 0 && port == apmhttputil.SchemeDefaultPort(req.URL.Scheme) { + var hasDefaultPort bool + if n := len(destinationServiceURL.Host); n > 0 && destinationServiceURL.Host[n-1] != ']' { + if i := strings.LastIndexByte(destinationServiceURL.Host, ':'); i != -1 { + // Remove the default port from destination.service.name. + destinationServiceURL.Host = destinationServiceURL.Host[:i] + hasDefaultPort = true + } + } + if !hasDefaultPort { + // Add the default port to destination.service.resource. + destinationServiceResource = fmt.Sprintf("%s:%d", destinationServiceResource, port) + } + } + c.SetDestinationService(DestinationServiceSpanContext{ + Name: destinationServiceURL.String(), + Resource: destinationServiceResource, + }) +} + +// SetHTTPStatusCode records the HTTP response status code. +func (c *SpanContext) SetHTTPStatusCode(statusCode int) { + c.http.StatusCode = statusCode + c.model.HTTP = &c.http +} + +// SetDestinationAddress sets the destination address and port in the context. +// +// SetDestinationAddress has no effect when called when an empty addr. +func (c *SpanContext) SetDestinationAddress(addr string, port int) { + if addr != "" { + c.destination.Address = truncateString(addr) + c.destination.Port = port + c.model.Destination = &c.destination + } +} + +// SetDestinationService sets the destination service info in the context. +func (c *SpanContext) SetDestinationService(service DestinationServiceSpanContext) { + c.destinationService.Name = truncateString(service.Name) + c.destinationService.Resource = truncateString(service.Resource) + c.destination.Service = &c.destinationService + c.model.Destination = &c.destination +} diff --git a/vendor/go.elastic.co/apm/stacktrace.go b/vendor/go.elastic.co/apm/stacktrace.go new file mode 100644 index 00000000000..1fe5cda6158 --- /dev/null +++ b/vendor/go.elastic.co/apm/stacktrace.go @@ -0,0 +1,52 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "path/filepath" + + "go.elastic.co/apm/model" + "go.elastic.co/apm/stacktrace" +) + +func appendModelStacktraceFrames(out []model.StacktraceFrame, in []stacktrace.Frame) []model.StacktraceFrame { + for _, f := range in { + out = append(out, modelStacktraceFrame(f)) + } + return out +} + +func modelStacktraceFrame(in stacktrace.Frame) model.StacktraceFrame { + var abspath string + file := in.File + if file != "" { + if filepath.IsAbs(file) { + abspath = file + } + file = filepath.Base(file) + } + packagePath, function := stacktrace.SplitFunctionName(in.Function) + return model.StacktraceFrame{ + AbsolutePath: abspath, + File: file, + Line: in.Line, + Function: function, + Module: packagePath, + LibraryFrame: stacktrace.IsLibraryPackage(packagePath), + } +} diff --git a/vendor/go.elastic.co/apm/stacktrace/context.go b/vendor/go.elastic.co/apm/stacktrace/context.go new file mode 100644 index 00000000000..b9d292432d7 --- /dev/null +++ b/vendor/go.elastic.co/apm/stacktrace/context.go @@ -0,0 +1,100 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package stacktrace + +import ( + "bufio" + "net/http" + "os" + + "go.elastic.co/apm/model" +) + +// SetContext sets the source context for the given stack frames, +// with the specified number of pre- and post- lines. +func SetContext(setter ContextSetter, frames []model.StacktraceFrame, pre, post int) error { + for i := 0; i < len(frames); i++ { + if err := setter.SetContext(&frames[i], pre, post); err != nil { + return err + } + } + return nil +} + +// ContextSetter is an interface that can be used for setting the source +// context for a stack frame. +type ContextSetter interface { + // SetContext sets the source context for the given stack frame, + // with the specified number of pre- and post- lines. + SetContext(frame *model.StacktraceFrame, pre, post int) error +} + +// FileSystemContextSetter returns a ContextSetter that sets context +// by reading file contents from the provided http.FileSystem. +func FileSystemContextSetter(fs http.FileSystem) ContextSetter { + if fs == nil { + panic("fs is nil") + } + return &fileSystemContextSetter{fs} +} + +type fileSystemContextSetter struct { + http.FileSystem +} + +func (s *fileSystemContextSetter) SetContext(frame *model.StacktraceFrame, pre, post int) error { + if frame.Line <= 0 { + return nil + } + f, err := s.Open(frame.AbsolutePath) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + defer f.Close() + + var lineno int + var line string + preLines := make([]string, 0, pre) + postLines := make([]string, 0, post) + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + lineno++ + if lineno > frame.Line+post { + break + } + switch { + case lineno == frame.Line: + line = scanner.Text() + case lineno < frame.Line && lineno >= frame.Line-pre: + preLines = append(preLines, scanner.Text()) + case lineno > frame.Line && lineno <= frame.Line+post: + postLines = append(postLines, scanner.Text()) + } + } + if err := scanner.Err(); err != nil { + return err + } + frame.ContextLine = line + frame.PreContext = preLines + frame.PostContext = postLines + return nil +} diff --git a/vendor/go.elastic.co/apm/stacktrace/doc.go b/vendor/go.elastic.co/apm/stacktrace/doc.go new file mode 100644 index 00000000000..f8cffa455d8 --- /dev/null +++ b/vendor/go.elastic.co/apm/stacktrace/doc.go @@ -0,0 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package stacktrace provides a simplified stack frame type, +// functions for obtaining stack frames, and related utilities. +package stacktrace diff --git a/vendor/go.elastic.co/apm/stacktrace/frame.go b/vendor/go.elastic.co/apm/stacktrace/frame.go new file mode 100644 index 00000000000..1c5053a2513 --- /dev/null +++ b/vendor/go.elastic.co/apm/stacktrace/frame.go @@ -0,0 +1,34 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package stacktrace + +// Frame describes a stack frame. +type Frame struct { + // File is the filename of the location of the stack frame. + // This may be either the absolute or base name of the file. + File string + + // Line is the 1-based line number of the location of the + // stack frame, or zero if unknown. + Line int + + // Function is the name of the function name for this stack + // frame. This should be package-qualified, and may be split + // using stacktrace.SplitFunctionName. + Function string +} diff --git a/vendor/go.elastic.co/apm/stacktrace/generate_library.bash b/vendor/go.elastic.co/apm/stacktrace/generate_library.bash new file mode 100644 index 00000000000..06bff3cb6ce --- /dev/null +++ b/vendor/go.elastic.co/apm/stacktrace/generate_library.bash @@ -0,0 +1,77 @@ +#!/bin/bash + +set -e + +_PKGS=$(go list -f '{{printf "\t%q,\n" .ImportPath}}' "$@" | grep -v vendor/golang_org) + +cat > library.go < 0 && n <= 10 { + pc = make([]uintptr, n) + pc = pc[:runtime.Callers(skip+1, pc)] + } else { + // n is negative or > 10, allocate space for 10 + // and make repeated calls to runtime.Callers + // until we've got all the frames or reached n. + pc = make([]uintptr, 10) + m := 0 + for { + m += runtime.Callers(skip+m+1, pc[m:]) + if m < len(pc) || m == n { + pc = pc[:m] + break + } + // Extend pc's length, ensuring its length + // extends to its new capacity to minimise + // the number of calls to runtime.Callers. + pc = append(pc, 0) + for len(pc) < cap(pc) { + pc = append(pc, 0) + } + } + } + return AppendCallerFrames(frames, pc, n) +} + +// AppendCallerFrames appends to n frames for the PCs in callers, +// and returns the extended slice. If n is negative, all available +// frames will be added. Multiple frames may exist for the same +// caller/PC in the case of function call inlining. +// +// See RuntimeFrame for information on what details are included. +func AppendCallerFrames(frames []Frame, callers []uintptr, n int) []Frame { + if len(callers) == 0 { + return frames + } + runtimeFrames := runtime.CallersFrames(callers) + for i := 0; n < 0 || i < n; i++ { + runtimeFrame, more := runtimeFrames.Next() + frames = append(frames, RuntimeFrame(runtimeFrame)) + if !more { + break + } + } + return frames +} + +// RuntimeFrame returns a Frame based on the given runtime.Frame. +// +// The resulting Frame will have the file path, package-qualified +// function name, and line number set. The function name can be +// split using SplitFunctionName, and the absolute path of the +// file and its base name can be determined using standard filepath +// functions. +func RuntimeFrame(in runtime.Frame) Frame { + return Frame{ + File: in.File, + Function: in.Function, + Line: in.Line, + } +} + +// SplitFunctionName splits the function name as formatted in +// runtime.Frame.Function, and returns the package path and +// function name components. +func SplitFunctionName(in string) (packagePath, function string) { + function = in + if function == "" { + return "", "" + } + // The last part of a package path will always have "." + // encoded as "%2e", so we can pick off the package path + // by finding the last part of the package path, and then + // the proceeding ".". + // + // Unexported method names may contain the package path. + // In these cases, the method receiver will be enclosed + // in parentheses, so we can treat that as the start of + // the function name. + sep := strings.Index(function, ".(") + if sep >= 0 { + packagePath = unescape(function[:sep]) + function = function[sep+1:] + } else { + offset := 0 + if sep := strings.LastIndex(function, "/"); sep >= 0 { + offset = sep + } + if sep := strings.IndexRune(function[offset+1:], '.'); sep >= 0 { + packagePath = unescape(function[:offset+1+sep]) + function = function[offset+1+sep+1:] + } + } + return packagePath, function +} + +func unescape(s string) string { + var n int + for i := 0; i < len(s); i++ { + if s[i] == '%' { + n++ + } + } + if n == 0 { + return s + } + bytes := make([]byte, 0, len(s)-2*n) + for i := 0; i < len(s); i++ { + b := s[i] + if b == '%' && i+2 < len(s) { + b = fromhex(s[i+1])<<4 | fromhex(s[i+2]) + i += 2 + } + bytes = append(bytes, b) + } + return string(bytes) +} + +func fromhex(b byte) byte { + if b >= 'a' { + return 10 + b - 'a' + } + return b - '0' +} diff --git a/vendor/go.elastic.co/apm/tracecontext.go b/vendor/go.elastic.co/apm/tracecontext.go new file mode 100644 index 00000000000..2983e85d6da --- /dev/null +++ b/vendor/go.elastic.co/apm/tracecontext.go @@ -0,0 +1,263 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "bytes" + "encoding/hex" + "fmt" + "regexp" + "unicode" + + "github.com/pkg/errors" +) + +var ( + errZeroTraceID = errors.New("zero trace-id is invalid") + errZeroSpanID = errors.New("zero span-id is invalid") +) + +// tracestateKeyRegexp holds a regular expression used for validating +// tracestate keys according to the standard rules: +// +// key = lcalpha 0*255( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) +// key = ( lcalpha / DIGIT ) 0*240( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) "@" lcalpha 0*13( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) +// lcalpha = %x61-7A ; a-z +// +// nblkchr is used for defining valid runes for tracestate values. +var ( + tracestateKeyRegexp = regexp.MustCompile(`^[a-z](([a-z0-9_*/-]{0,255})|([a-z0-9_*/-]{0,240}@[a-z][a-z0-9_*/-]{0,13}))$`) + + nblkchr = &unicode.RangeTable{ + R16: []unicode.Range16{ + {0x21, 0x2B, 1}, + {0x2D, 0x3C, 1}, + {0x3E, 0x7E, 1}, + }, + LatinOffset: 3, + } +) + +const ( + traceOptionsRecordedFlag = 0x01 +) + +// TraceContext holds trace context for an incoming or outgoing request. +type TraceContext struct { + // Trace identifies the trace forest. + Trace TraceID + + // Span identifies a span: the parent span if this context + // corresponds to an incoming request, or the current span + // if this is an outgoing request. + Span SpanID + + // Options holds the trace options propagated by the parent. + Options TraceOptions + + // State holds the trace state. + State TraceState +} + +// TraceID identifies a trace forest. +type TraceID [16]byte + +// Validate validates the trace ID. +// This will return non-nil for a zero trace ID. +func (id TraceID) Validate() error { + if id.isZero() { + return errZeroTraceID + } + return nil +} + +func (id TraceID) isZero() bool { + return id == (TraceID{}) +} + +// String returns id encoded as hex. +func (id TraceID) String() string { + text, _ := id.MarshalText() + return string(text) +} + +// MarshalText returns id encoded as hex, satisfying encoding.TextMarshaler. +func (id TraceID) MarshalText() ([]byte, error) { + text := make([]byte, hex.EncodedLen(len(id))) + hex.Encode(text, id[:]) + return text, nil +} + +// SpanID identifies a span within a trace. +type SpanID [8]byte + +// Validate validates the span ID. +// This will return non-nil for a zero span ID. +func (id SpanID) Validate() error { + if id.isZero() { + return errZeroSpanID + } + return nil +} + +func (id SpanID) isZero() bool { + return id == SpanID{} +} + +// String returns id encoded as hex. +func (id SpanID) String() string { + text, _ := id.MarshalText() + return string(text) +} + +// MarshalText returns id encoded as hex, satisfying encoding.TextMarshaler. +func (id SpanID) MarshalText() ([]byte, error) { + text := make([]byte, hex.EncodedLen(len(id))) + hex.Encode(text, id[:]) + return text, nil +} + +// TraceOptions describes the options for a trace. +type TraceOptions uint8 + +// Recorded reports whether or not the transaction/span may have been (or may be) recorded. +func (o TraceOptions) Recorded() bool { + return (o & traceOptionsRecordedFlag) == traceOptionsRecordedFlag +} + +// WithRecorded changes the "recorded" flag, and returns the new options +// without modifying the original value. +func (o TraceOptions) WithRecorded(recorded bool) TraceOptions { + if recorded { + return o | traceOptionsRecordedFlag + } + return o & (0xFF ^ traceOptionsRecordedFlag) +} + +// TraceState holds vendor-specific state for a trace. +type TraceState struct { + head *TraceStateEntry +} + +// NewTraceState returns a TraceState based on entries. +func NewTraceState(entries ...TraceStateEntry) TraceState { + out := TraceState{} + var last *TraceStateEntry + for _, e := range entries { + e := e // copy + if last == nil { + out.head = &e + } else { + last.next = &e + } + last = &e + } + return out +} + +// String returns s as a comma-separated list of key-value pairs. +func (s TraceState) String() string { + if s.head == nil { + return "" + } + var buf bytes.Buffer + s.head.writeBuf(&buf) + for e := s.head.next; e != nil; e = e.next { + buf.WriteByte(',') + e.writeBuf(&buf) + } + return buf.String() +} + +// Validate validates the trace state. +// +// This will return non-nil if any entries are invalid, +// if there are too many entries, or if an entry key is +// repeated. +func (s TraceState) Validate() error { + if s.head == nil { + return nil + } + recorded := make(map[string]int) + var i int + for e := s.head; e != nil; e = e.next { + if i == 32 { + return errors.New("tracestate contains more than the maximum allowed number of entries, 32") + } + if err := e.Validate(); err != nil { + return errors.Wrapf(err, "invalid tracestate entry at position %d", i) + } + if prev, ok := recorded[e.Key]; ok { + return fmt.Errorf("duplicate tracestate key %q at positions %d and %d", e.Key, prev, i) + } + recorded[e.Key] = i + i++ + } + return nil +} + +// TraceStateEntry holds a trace state entry: a key/value pair +// representing state for a vendor. +type TraceStateEntry struct { + next *TraceStateEntry + + // Key holds a vendor (and optionally, tenant) ID. + Key string + + // Value holds a string representing trace state. + Value string +} + +func (e *TraceStateEntry) writeBuf(buf *bytes.Buffer) { + buf.WriteString(e.Key) + buf.WriteByte('=') + buf.WriteString(e.Value) +} + +// Validate validates the trace state entry. +// +// This will return non-nil if either the key or value is invalid. +func (e *TraceStateEntry) Validate() error { + if !tracestateKeyRegexp.MatchString(e.Key) { + return fmt.Errorf("invalid key %q", e.Key) + } + if err := e.validateValue(); err != nil { + return errors.Wrapf(err, "invalid value for key %q", e.Key) + } + return nil +} + +func (e *TraceStateEntry) validateValue() error { + if e.Value == "" { + return errors.New("value is empty") + } + runes := []rune(e.Value) + n := len(runes) + if n > 256 { + return errors.Errorf("value contains %d characters, maximum allowed is 256", n) + } + if !unicode.In(runes[n-1], nblkchr) { + return errors.Errorf("value contains invalid character %q", runes[n-1]) + } + for _, r := range runes[:n-1] { + if r != 0x20 && !unicode.In(r, nblkchr) { + return errors.Errorf("value contains invalid character %q", r) + } + } + return nil +} diff --git a/vendor/go.elastic.co/apm/tracer.go b/vendor/go.elastic.co/apm/tracer.go new file mode 100644 index 00000000000..3170e2f226b --- /dev/null +++ b/vendor/go.elastic.co/apm/tracer.go @@ -0,0 +1,1170 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "bytes" + "compress/zlib" + "context" + "io" + "log" + "math/rand" + "sync" + "sync/atomic" + "time" + + "go.elastic.co/apm/apmconfig" + "go.elastic.co/apm/internal/apmlog" + "go.elastic.co/apm/internal/configutil" + "go.elastic.co/apm/internal/iochan" + "go.elastic.co/apm/internal/ringbuffer" + "go.elastic.co/apm/internal/wildcard" + "go.elastic.co/apm/model" + "go.elastic.co/apm/stacktrace" + "go.elastic.co/apm/transport" + "go.elastic.co/fastjson" +) + +const ( + defaultPreContext = 3 + defaultPostContext = 3 + gracePeriodJitter = 0.1 // +/- 10% + tracerEventChannelCap = 1000 +) + +var ( + // DefaultTracer is the default global Tracer, set at package + // initialization time, configured via environment variables. + // + // This will always be initialized to a non-nil value. If any + // of the environment variables are invalid, the corresponding + // errors will be logged to stderr and the default values will + // be used instead. + DefaultTracer *Tracer +) + +func init() { + var opts TracerOptions + opts.initDefaults(true) + DefaultTracer = newTracer(opts) +} + +// TracerOptions holds initial tracer options, for passing to NewTracerOptions. +type TracerOptions struct { + // ServiceName holds the service name. + // + // If ServiceName is empty, the service name will be defined using the + // ELASTIC_APM_SERVICE_NAME environment variable, or if that is not set, + // the executable name. + ServiceName string + + // ServiceVersion holds the service version. + // + // If ServiceVersion is empty, the service version will be defined using + // the ELASTIC_APM_SERVICE_VERSION environment variable. + ServiceVersion string + + // ServiceEnvironment holds the service environment. + // + // If ServiceEnvironment is empty, the service environment will be defined + // using the ELASTIC_APM_ENVIRONMENT environment variable. + ServiceEnvironment string + + // Transport holds the transport to use for sending events. + // + // If Transport is nil, transport.Default will be used. + // + // If Transport implements apmconfig.Watcher, the tracer will begin watching + // for remote changes immediately. This behaviour can be disabled by setting + // the environment variable ELASTIC_APM_CENTRAL_CONFIG=false. + Transport transport.Transport + + requestDuration time.Duration + metricsInterval time.Duration + maxSpans int + requestSize int + bufferSize int + metricsBufferSize int + sampler Sampler + sanitizedFieldNames wildcard.Matchers + disabledMetrics wildcard.Matchers + captureHeaders bool + captureBody CaptureBodyMode + spanFramesMinDuration time.Duration + stackTraceLimit int + active bool + configWatcher apmconfig.Watcher + breakdownMetrics bool + propagateLegacyHeader bool + profileSender profileSender + cpuProfileInterval time.Duration + cpuProfileDuration time.Duration + heapProfileInterval time.Duration +} + +// initDefaults updates opts with default values. +func (opts *TracerOptions) initDefaults(continueOnError bool) error { + var errs []error + failed := func(err error) bool { + if err == nil { + return false + } + errs = append(errs, err) + return true + } + + requestDuration, err := initialRequestDuration() + if failed(err) { + requestDuration = defaultAPIRequestTime + } + + metricsInterval, err := initialMetricsInterval() + if err != nil { + metricsInterval = defaultMetricsInterval + errs = append(errs, err) + } + + requestSize, err := initialAPIRequestSize() + if err != nil { + requestSize = int(defaultAPIRequestSize) + errs = append(errs, err) + } + + bufferSize, err := initialAPIBufferSize() + if err != nil { + bufferSize = int(defaultAPIBufferSize) + errs = append(errs, err) + } + + metricsBufferSize, err := initialMetricsBufferSize() + if err != nil { + metricsBufferSize = int(defaultMetricsBufferSize) + errs = append(errs, err) + } + + maxSpans, err := initialMaxSpans() + if failed(err) { + maxSpans = defaultMaxSpans + } + + sampler, err := initialSampler() + if failed(err) { + sampler = nil + } + + captureHeaders, err := initialCaptureHeaders() + if failed(err) { + captureHeaders = defaultCaptureHeaders + } + + captureBody, err := initialCaptureBody() + if failed(err) { + captureBody = CaptureBodyOff + } + + spanFramesMinDuration, err := initialSpanFramesMinDuration() + if failed(err) { + spanFramesMinDuration = defaultSpanFramesMinDuration + } + + stackTraceLimit, err := initialStackTraceLimit() + if failed(err) { + stackTraceLimit = defaultStackTraceLimit + } + + active, err := initialActive() + if failed(err) { + active = true + } + + centralConfigEnabled, err := initialCentralConfigEnabled() + if failed(err) { + centralConfigEnabled = true + } + + breakdownMetricsEnabled, err := initialBreakdownMetricsEnabled() + if failed(err) { + breakdownMetricsEnabled = true + } + + propagateLegacyHeader, err := initialUseElasticTraceparentHeader() + if failed(err) { + propagateLegacyHeader = true + } + + cpuProfileInterval, cpuProfileDuration, err := initialCPUProfileIntervalDuration() + if failed(err) { + cpuProfileInterval = 0 + cpuProfileDuration = 0 + } + heapProfileInterval, err := initialHeapProfileInterval() + if failed(err) { + heapProfileInterval = 0 + } + + if opts.ServiceName != "" { + err := validateServiceName(opts.ServiceName) + if failed(err) { + opts.ServiceName = "" + } + } + + if len(errs) != 0 && !continueOnError { + return errs[0] + } + for _, err := range errs { + log.Printf("[apm]: %s", err) + } + + opts.requestDuration = requestDuration + opts.metricsInterval = metricsInterval + opts.requestSize = requestSize + opts.bufferSize = bufferSize + opts.metricsBufferSize = metricsBufferSize + opts.maxSpans = maxSpans + opts.sampler = sampler + opts.sanitizedFieldNames = initialSanitizedFieldNames() + opts.disabledMetrics = initialDisabledMetrics() + opts.breakdownMetrics = breakdownMetricsEnabled + opts.captureHeaders = captureHeaders + opts.captureBody = captureBody + opts.spanFramesMinDuration = spanFramesMinDuration + opts.stackTraceLimit = stackTraceLimit + opts.active = active + opts.propagateLegacyHeader = propagateLegacyHeader + if opts.Transport == nil { + opts.Transport = transport.Default + } + if centralConfigEnabled { + if cw, ok := opts.Transport.(apmconfig.Watcher); ok { + opts.configWatcher = cw + } + } + if ps, ok := opts.Transport.(profileSender); ok { + opts.profileSender = ps + opts.cpuProfileInterval = cpuProfileInterval + opts.cpuProfileDuration = cpuProfileDuration + opts.heapProfileInterval = heapProfileInterval + } + + serviceName, serviceVersion, serviceEnvironment := initialService() + if opts.ServiceName == "" { + opts.ServiceName = serviceName + } + if opts.ServiceVersion == "" { + opts.ServiceVersion = serviceVersion + } + if opts.ServiceEnvironment == "" { + opts.ServiceEnvironment = serviceEnvironment + } + return nil +} + +// Tracer manages the sampling and sending of transactions to +// Elastic APM. +// +// Transactions are buffered until they are flushed (forcibly +// with a Flush call, or when the flush timer expires), or when +// the maximum transaction queue size is reached. Failure to +// send will be periodically retried. Once the queue limit has +// been reached, new transactions will replace older ones in +// the queue. +// +// Errors are sent as soon as possible, but will buffered and +// later sent in bulk if the tracer is busy, or otherwise cannot +// send to the server, e.g. due to network failure. There is +// a limit to the number of errors that will be buffered, and +// once that limit has been reached, new errors will be dropped +// until the queue is drained. +// +// The exported fields be altered or replaced any time up until +// any Tracer methods have been invoked. +type Tracer struct { + Transport transport.Transport + Service struct { + Name string + Version string + Environment string + } + + process *model.Process + system *model.System + + active int32 + bufferSize int + metricsBufferSize int + closing chan struct{} + closed chan struct{} + forceFlush chan chan<- struct{} + forceSendMetrics chan chan<- struct{} + configCommands chan tracerConfigCommand + configWatcher chan apmconfig.Watcher + events chan tracerEvent + breakdownMetrics *breakdownMetrics + profileSender profileSender + + statsMu sync.Mutex + stats TracerStats + + // instrumentationConfig_ must only be accessed and mutated + // using Tracer.instrumentationConfig() and Tracer.setInstrumentationConfig(). + instrumentationConfigInternal *instrumentationConfig + + errorDataPool sync.Pool + spanDataPool sync.Pool + transactionDataPool sync.Pool +} + +// NewTracer returns a new Tracer, using the default transport, +// and with the specified service name and version if specified. +// This is equivalent to calling NewTracerOptions with a +// TracerOptions having ServiceName and ServiceVersion set to +// the provided arguments. +func NewTracer(serviceName, serviceVersion string) (*Tracer, error) { + return NewTracerOptions(TracerOptions{ + ServiceName: serviceName, + ServiceVersion: serviceVersion, + }) +} + +// NewTracerOptions returns a new Tracer using the provided options. +// See TracerOptions for details on the options, and their default +// values. +func NewTracerOptions(opts TracerOptions) (*Tracer, error) { + if err := opts.initDefaults(false); err != nil { + return nil, err + } + return newTracer(opts), nil +} + +func newTracer(opts TracerOptions) *Tracer { + t := &Tracer{ + Transport: opts.Transport, + process: ¤tProcess, + system: &localSystem, + closing: make(chan struct{}), + closed: make(chan struct{}), + forceFlush: make(chan chan<- struct{}), + forceSendMetrics: make(chan chan<- struct{}), + configCommands: make(chan tracerConfigCommand), + configWatcher: make(chan apmconfig.Watcher), + events: make(chan tracerEvent, tracerEventChannelCap), + active: 1, + breakdownMetrics: newBreakdownMetrics(), + bufferSize: opts.bufferSize, + metricsBufferSize: opts.metricsBufferSize, + profileSender: opts.profileSender, + instrumentationConfigInternal: &instrumentationConfig{ + local: make(map[string]func(*instrumentationConfigValues)), + }, + } + t.Service.Name = opts.ServiceName + t.Service.Version = opts.ServiceVersion + t.Service.Environment = opts.ServiceEnvironment + t.breakdownMetrics.enabled = opts.breakdownMetrics + + // Initialise local transaction config. + t.setLocalInstrumentationConfig(envCaptureBody, func(cfg *instrumentationConfigValues) { + cfg.captureBody = opts.captureBody + }) + t.setLocalInstrumentationConfig(envCaptureHeaders, func(cfg *instrumentationConfigValues) { + cfg.captureHeaders = opts.captureHeaders + }) + t.setLocalInstrumentationConfig(envMaxSpans, func(cfg *instrumentationConfigValues) { + cfg.maxSpans = opts.maxSpans + }) + t.setLocalInstrumentationConfig(envTransactionSampleRate, func(cfg *instrumentationConfigValues) { + cfg.sampler = opts.sampler + }) + t.setLocalInstrumentationConfig(envSpanFramesMinDuration, func(cfg *instrumentationConfigValues) { + cfg.spanFramesMinDuration = opts.spanFramesMinDuration + }) + t.setLocalInstrumentationConfig(envStackTraceLimit, func(cfg *instrumentationConfigValues) { + cfg.stackTraceLimit = opts.stackTraceLimit + }) + t.setLocalInstrumentationConfig(envUseElasticTraceparentHeader, func(cfg *instrumentationConfigValues) { + cfg.propagateLegacyHeader = opts.propagateLegacyHeader + }) + + if !opts.active { + t.active = 0 + close(t.closed) + return t + } + + go t.loop() + t.configCommands <- func(cfg *tracerConfig) { + cfg.cpuProfileInterval = opts.cpuProfileInterval + cfg.cpuProfileDuration = opts.cpuProfileDuration + cfg.heapProfileInterval = opts.heapProfileInterval + cfg.metricsInterval = opts.metricsInterval + cfg.requestDuration = opts.requestDuration + cfg.requestSize = opts.requestSize + cfg.sanitizedFieldNames = opts.sanitizedFieldNames + cfg.disabledMetrics = opts.disabledMetrics + cfg.preContext = defaultPreContext + cfg.postContext = defaultPostContext + cfg.metricsGatherers = []MetricsGatherer{newBuiltinMetricsGatherer(t)} + if apmlog.DefaultLogger != nil { + cfg.logger = apmlog.DefaultLogger + } + } + if opts.configWatcher != nil { + t.configWatcher <- opts.configWatcher + } + return t +} + +// tracerConfig holds the tracer's runtime configuration, which may be modified +// by sending a tracerConfigCommand to the tracer's configCommands channel. +type tracerConfig struct { + requestSize int + requestDuration time.Duration + metricsInterval time.Duration + logger WarningLogger + metricsGatherers []MetricsGatherer + contextSetter stacktrace.ContextSetter + preContext, postContext int + sanitizedFieldNames wildcard.Matchers + disabledMetrics wildcard.Matchers + cpuProfileDuration time.Duration + cpuProfileInterval time.Duration + heapProfileInterval time.Duration +} + +type tracerConfigCommand func(*tracerConfig) + +// Close closes the Tracer, preventing transactions from being +// sent to the APM server. +func (t *Tracer) Close() { + select { + case <-t.closing: + default: + close(t.closing) + } + <-t.closed +} + +// Flush waits for the Tracer to flush any transactions and errors it currently +// has queued to the APM server, the tracer is stopped, or the abort channel +// is signaled. +func (t *Tracer) Flush(abort <-chan struct{}) { + flushed := make(chan struct{}, 1) + select { + case t.forceFlush <- flushed: + select { + case <-abort: + case <-flushed: + case <-t.closed: + } + case <-t.closed: + } +} + +// Active reports whether the tracer is active. If the tracer is inactive, +// no transactions or errors will be sent to the Elastic APM server. +func (t *Tracer) Active() bool { + return atomic.LoadInt32(&t.active) == 1 +} + +// SetRequestDuration sets the maximum amount of time to keep a request open +// to the APM server for streaming data before closing the stream and starting +// a new request. +func (t *Tracer) SetRequestDuration(d time.Duration) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.requestDuration = d + }) +} + +// SetMetricsInterval sets the metrics interval -- the amount of time in +// between metrics samples being gathered. +func (t *Tracer) SetMetricsInterval(d time.Duration) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.metricsInterval = d + }) +} + +// SetContextSetter sets the stacktrace.ContextSetter to be used for +// setting stacktrace source context. If nil (which is the initial +// value), no context will be set. +func (t *Tracer) SetContextSetter(setter stacktrace.ContextSetter) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.contextSetter = setter + }) +} + +// SetLogger sets the Logger to be used for logging the operation of +// the tracer. +// +// If logger implements WarningLogger, its Warningf method will be used +// for logging warnings. Otherwise, warnings will logged using Debugf. +// +// The tracer is initialized with a default logger configured with the +// environment variables ELASTIC_APM_LOG_FILE and ELASTIC_APM_LOG_LEVEL. +// Calling SetLogger will replace the default logger. +func (t *Tracer) SetLogger(logger Logger) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.logger = makeWarningLogger(logger) + }) +} + +// SetSanitizedFieldNames sets the wildcard patterns that will be used to +// match cookie and form field names for sanitization. Fields matching any +// of the the supplied patterns will have their values redacted. If +// SetSanitizedFieldNames is called with no arguments, then no fields +// will be redacted. +func (t *Tracer) SetSanitizedFieldNames(patterns ...string) error { + var matchers wildcard.Matchers + if len(patterns) != 0 { + matchers = make(wildcard.Matchers, len(patterns)) + for i, p := range patterns { + matchers[i] = configutil.ParseWildcardPattern(p) + } + } + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.sanitizedFieldNames = matchers + }) + return nil +} + +// RegisterMetricsGatherer registers g for periodic (or forced) metrics +// gathering by t. +// +// RegisterMetricsGatherer returns a function which will deregister g. +// It may safely be called multiple times. +func (t *Tracer) RegisterMetricsGatherer(g MetricsGatherer) func() { + // Wrap g in a pointer-to-struct, so we can safely compare. + wrapped := &struct{ MetricsGatherer }{MetricsGatherer: g} + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.metricsGatherers = append(cfg.metricsGatherers, wrapped) + }) + deregister := func(cfg *tracerConfig) { + for i, g := range cfg.metricsGatherers { + if g != wrapped { + continue + } + cfg.metricsGatherers = append(cfg.metricsGatherers[:i], cfg.metricsGatherers[i+1:]...) + } + } + var once sync.Once + return func() { + once.Do(func() { + t.sendConfigCommand(deregister) + }) + } +} + +// SetConfigWatcher sets w as the config watcher. +// +// By default, the tracer will be configured to use the transport for +// watching config, if the transport implements apmconfig.Watcher. This +// can be overridden by calling SetConfigWatcher. +// +// If w is nil, config watching will be stopped. +// +// Calling SetConfigWatcher will discard any previously observed remote +// config, reverting to local config until a config change from w is +// observed. +func (t *Tracer) SetConfigWatcher(w apmconfig.Watcher) { + select { + case t.configWatcher <- w: + case <-t.closing: + case <-t.closed: + } +} + +func (t *Tracer) sendConfigCommand(cmd tracerConfigCommand) { + select { + case t.configCommands <- cmd: + case <-t.closing: + case <-t.closed: + } +} + +// SetSampler sets the sampler the tracer. +// +// It is valid to pass nil, in which case all transactions will be sampled. +// +// Configuration via Kibana takes precedence over local configuration, so +// if sampling has been configured via Kibana, this call will not have any +// effect until/unless that configuration has been removed. +func (t *Tracer) SetSampler(s Sampler) { + t.setLocalInstrumentationConfig(envTransactionSampleRate, func(cfg *instrumentationConfigValues) { + cfg.sampler = s + }) +} + +// SetMaxSpans sets the maximum number of spans that will be added +// to a transaction before dropping spans. +// +// Passing in zero will disable all spans, while negative values will +// permit an unlimited number of spans. +func (t *Tracer) SetMaxSpans(n int) { + t.setLocalInstrumentationConfig(envMaxSpans, func(cfg *instrumentationConfigValues) { + cfg.maxSpans = n + }) +} + +// SetSpanFramesMinDuration sets the minimum duration for a span after which +// we will capture its stack frames. +func (t *Tracer) SetSpanFramesMinDuration(d time.Duration) { + t.setLocalInstrumentationConfig(envMaxSpans, func(cfg *instrumentationConfigValues) { + cfg.spanFramesMinDuration = d + }) +} + +// SetStackTraceLimit sets the the maximum number of stack frames to collect +// for each stack trace. If limit is negative, then all frames will be collected. +func (t *Tracer) SetStackTraceLimit(limit int) { + t.setLocalInstrumentationConfig(envMaxSpans, func(cfg *instrumentationConfigValues) { + cfg.stackTraceLimit = limit + }) +} + +// SetCaptureHeaders enables or disables capturing of HTTP headers. +func (t *Tracer) SetCaptureHeaders(capture bool) { + t.setLocalInstrumentationConfig(envMaxSpans, func(cfg *instrumentationConfigValues) { + cfg.captureHeaders = capture + }) +} + +// SetCaptureBody sets the HTTP request body capture mode. +func (t *Tracer) SetCaptureBody(mode CaptureBodyMode) { + t.setLocalInstrumentationConfig(envMaxSpans, func(cfg *instrumentationConfigValues) { + cfg.captureBody = mode + }) +} + +// SendMetrics forces the tracer to gather and send metrics immediately, +// blocking until the metrics have been sent or the abort channel is +// signalled. +func (t *Tracer) SendMetrics(abort <-chan struct{}) { + sent := make(chan struct{}, 1) + select { + case t.forceSendMetrics <- sent: + select { + case <-abort: + case <-sent: + case <-t.closed: + } + case <-t.closed: + } +} + +// Stats returns the current TracerStats. This will return the most +// recent values even after the tracer has been closed. +func (t *Tracer) Stats() TracerStats { + t.statsMu.Lock() + stats := t.stats + t.statsMu.Unlock() + return stats +} + +func (t *Tracer) loop() { + ctx, cancelContext := context.WithCancel(context.Background()) + defer cancelContext() + defer close(t.closed) + defer atomic.StoreInt32(&t.active, 0) + + var req iochan.ReadRequest + var requestBuf bytes.Buffer + var metadata []byte + var gracePeriod time.Duration = -1 + var flushed chan<- struct{} + var requestBufTransactions, requestBufSpans, requestBufErrors, requestBufMetricsets uint64 + zlibWriter, _ := zlib.NewWriterLevel(&requestBuf, zlib.BestSpeed) + zlibFlushed := true + zlibClosed := false + iochanReader := iochan.NewReader() + requestBytesRead := 0 + requestActive := false + closeRequest := false + flushRequest := false + requestResult := make(chan error, 1) + requestTimer := time.NewTimer(0) + requestTimerActive := false + if !requestTimer.Stop() { + <-requestTimer.C + } + + // Run another goroutine to perform the blocking requests, + // communicating with the tracer loop to obtain stream data. + sendStreamRequest := make(chan time.Duration) + defer close(sendStreamRequest) + go func() { + jitterRand := rand.New(rand.NewSource(time.Now().UnixNano())) + for gracePeriod := range sendStreamRequest { + if gracePeriod > 0 { + select { + case <-time.After(jitterDuration(gracePeriod, jitterRand, gracePeriodJitter)): + case <-ctx.Done(): + } + } + requestResult <- t.Transport.SendStream(ctx, iochanReader) + } + }() + + var breakdownMetricsLimitWarningLogged bool + var stats TracerStats + var metrics Metrics + var sentMetrics chan<- struct{} + var gatheringMetrics bool + var metricsTimerStart time.Time + metricsBuffer := ringbuffer.New(t.metricsBufferSize) + gatheredMetrics := make(chan struct{}, 1) + metricsTimer := time.NewTimer(0) + if !metricsTimer.Stop() { + <-metricsTimer.C + } + + var lastConfigChange map[string]string + var configChanges <-chan apmconfig.Change + var stopConfigWatcher func() + defer func() { + if stopConfigWatcher != nil { + stopConfigWatcher() + } + }() + + cpuProfilingState := newCPUProfilingState(t.profileSender) + heapProfilingState := newHeapProfilingState(t.profileSender) + + var cfg tracerConfig + buffer := ringbuffer.New(t.bufferSize) + buffer.Evicted = func(h ringbuffer.BlockHeader) { + switch h.Tag { + case errorBlockTag: + stats.ErrorsDropped++ + case spanBlockTag: + stats.SpansDropped++ + case transactionBlockTag: + stats.TransactionsDropped++ + } + } + modelWriter := modelWriter{ + buffer: buffer, + metricsBuffer: metricsBuffer, + cfg: &cfg, + stats: &stats, + } + + for { + var gatherMetrics bool + select { + case <-t.closing: + cancelContext() // informs transport that EOF is expected + iochanReader.CloseRead(io.EOF) + return + case cmd := <-t.configCommands: + oldMetricsInterval := cfg.metricsInterval + cmd(&cfg) + cpuProfilingState.updateConfig(cfg.cpuProfileInterval, cfg.cpuProfileDuration) + heapProfilingState.updateConfig(cfg.heapProfileInterval, 0) + if !gatheringMetrics && cfg.metricsInterval != oldMetricsInterval { + if metricsTimerStart.IsZero() { + if cfg.metricsInterval > 0 { + metricsTimer.Reset(cfg.metricsInterval) + metricsTimerStart = time.Now() + } + } else { + if cfg.metricsInterval <= 0 { + metricsTimerStart = time.Time{} + if !metricsTimer.Stop() { + <-metricsTimer.C + } + } else { + alreadyPassed := time.Since(metricsTimerStart) + if alreadyPassed >= cfg.metricsInterval { + metricsTimer.Reset(0) + } else { + metricsTimer.Reset(cfg.metricsInterval - alreadyPassed) + } + } + } + } + continue + case cw := <-t.configWatcher: + if configChanges != nil { + stopConfigWatcher() + t.updateRemoteConfig(cfg.logger, lastConfigChange, nil) + lastConfigChange = nil + configChanges = nil + } + if cw == nil { + continue + } + var configWatcherContext context.Context + var watchParams apmconfig.WatchParams + watchParams.Service.Name = t.Service.Name + watchParams.Service.Environment = t.Service.Environment + configWatcherContext, stopConfigWatcher = context.WithCancel(ctx) + configChanges = cw.WatchConfig(configWatcherContext, watchParams) + // Silence go vet's "possible context leak" false positive. + // We call a previous stopConfigWatcher before reassigning + // the variable, and we have a defer at the top level of the + // loop method that will call the final stopConfigWatcher + // value on method exit. + _ = stopConfigWatcher + continue + case change, ok := <-configChanges: + if !ok { + configChanges = nil + continue + } + if change.Err != nil { + if cfg.logger != nil { + cfg.logger.Errorf("config request failed: %s", change.Err) + } + } else { + t.updateRemoteConfig(cfg.logger, lastConfigChange, change.Attrs) + lastConfigChange = change.Attrs + } + continue + case event := <-t.events: + switch event.eventType { + case transactionEvent: + if !t.breakdownMetrics.recordTransaction(event.tx.TransactionData) { + if !breakdownMetricsLimitWarningLogged && cfg.logger != nil { + cfg.logger.Warningf("%s", breakdownMetricsLimitWarning) + breakdownMetricsLimitWarningLogged = true + } + } + modelWriter.writeTransaction(event.tx.Transaction, event.tx.TransactionData) + case spanEvent: + modelWriter.writeSpan(event.span.Span, event.span.SpanData) + case errorEvent: + modelWriter.writeError(event.err) + // Flush the buffer to transmit the error immediately. + flushRequest = true + } + case <-requestTimer.C: + requestTimerActive = false + closeRequest = true + case <-metricsTimer.C: + metricsTimerStart = time.Time{} + gatherMetrics = !gatheringMetrics + case sentMetrics = <-t.forceSendMetrics: + if !metricsTimerStart.IsZero() { + if !metricsTimer.Stop() { + <-metricsTimer.C + } + metricsTimerStart = time.Time{} + } + gatherMetrics = !gatheringMetrics + case <-gatheredMetrics: + modelWriter.writeMetrics(&metrics) + gatheringMetrics = false + flushRequest = true + if cfg.metricsInterval > 0 { + metricsTimerStart = time.Now() + metricsTimer.Reset(cfg.metricsInterval) + } + case <-cpuProfilingState.timer.C: + cpuProfilingState.start(ctx, cfg.logger, t.metadataReader()) + case <-cpuProfilingState.finished: + cpuProfilingState.resetTimer() + case <-heapProfilingState.timer.C: + heapProfilingState.start(ctx, cfg.logger, t.metadataReader()) + case <-heapProfilingState.finished: + heapProfilingState.resetTimer() + case flushed = <-t.forceFlush: + // Drain any objects buffered in the channels. + for n := len(t.events); n > 0; n-- { + event := <-t.events + switch event.eventType { + case transactionEvent: + if !t.breakdownMetrics.recordTransaction(event.tx.TransactionData) { + if !breakdownMetricsLimitWarningLogged && cfg.logger != nil { + cfg.logger.Warningf("%s", breakdownMetricsLimitWarning) + breakdownMetricsLimitWarningLogged = true + } + } + modelWriter.writeTransaction(event.tx.Transaction, event.tx.TransactionData) + case spanEvent: + modelWriter.writeSpan(event.span.Span, event.span.SpanData) + case errorEvent: + modelWriter.writeError(event.err) + } + } + if !requestActive && buffer.Len() == 0 && metricsBuffer.Len() == 0 { + flushed <- struct{}{} + continue + } + closeRequest = true + case req = <-iochanReader.C: + case err := <-requestResult: + if err != nil { + stats.Errors.SendStream++ + gracePeriod = nextGracePeriod(gracePeriod) + if cfg.logger != nil { + logf := cfg.logger.Debugf + if err, ok := err.(*transport.HTTPError); ok && err.Response.StatusCode == 404 { + // 404 typically means the server is too old, meaning + // the error is due to a misconfigured environment. + logf = cfg.logger.Errorf + } + logf("request failed: %s (next request in ~%s)", err, gracePeriod) + } + } else { + gracePeriod = -1 // Reset grace period after success. + stats.TransactionsSent += requestBufTransactions + stats.SpansSent += requestBufSpans + stats.ErrorsSent += requestBufErrors + if cfg.logger != nil { + s := func(n uint64) string { + if n != 1 { + return "s" + } + return "" + } + cfg.logger.Debugf( + "sent request with %d transaction%s, %d span%s, %d error%s, %d metricset%s", + requestBufTransactions, s(requestBufTransactions), + requestBufSpans, s(requestBufSpans), + requestBufErrors, s(requestBufErrors), + requestBufMetricsets, s(requestBufMetricsets), + ) + } + } + if !stats.isZero() { + t.statsMu.Lock() + t.stats.accumulate(stats) + t.statsMu.Unlock() + stats = TracerStats{} + } + if sentMetrics != nil && requestBufMetricsets > 0 { + sentMetrics <- struct{}{} + sentMetrics = nil + } + if flushed != nil { + flushed <- struct{}{} + flushed = nil + } + if req.Buf != nil { + // req will be canceled by CloseRead below. + req.Buf = nil + } + iochanReader.CloseRead(io.EOF) + iochanReader = iochan.NewReader() + flushRequest = false + closeRequest = false + requestActive = false + requestBytesRead = 0 + requestBuf.Reset() + requestBufTransactions = 0 + requestBufSpans = 0 + requestBufErrors = 0 + requestBufMetricsets = 0 + if requestTimerActive { + if !requestTimer.Stop() { + <-requestTimer.C + } + requestTimerActive = false + } + } + + if !stats.isZero() { + t.statsMu.Lock() + t.stats.accumulate(stats) + t.statsMu.Unlock() + stats = TracerStats{} + } + + if gatherMetrics { + gatheringMetrics = true + metrics.disabled = cfg.disabledMetrics + t.gatherMetrics(ctx, cfg.metricsGatherers, &metrics, cfg.logger, gatheredMetrics) + if cfg.logger != nil { + cfg.logger.Debugf("gathering metrics") + } + } + + if !requestActive { + if buffer.Len() == 0 && metricsBuffer.Len() == 0 { + continue + } + sendStreamRequest <- gracePeriod + if metadata == nil { + metadata = t.jsonRequestMetadata() + } + zlibWriter.Reset(&requestBuf) + zlibWriter.Write(metadata) + zlibFlushed = false + zlibClosed = false + requestActive = true + requestTimer.Reset(cfg.requestDuration) + requestTimerActive = true + } + + if !closeRequest || !zlibClosed { + for requestBytesRead+requestBuf.Len() < cfg.requestSize { + if metricsBuffer.Len() > 0 { + if _, _, err := metricsBuffer.WriteBlockTo(zlibWriter); err == nil { + requestBufMetricsets++ + zlibWriter.Write([]byte("\n")) + zlibFlushed = false + if sentMetrics != nil { + // SendMetrics was called: close the request + // off so we can inform the user when the + // metrics have been processed. + closeRequest = true + } + } + continue + } + if buffer.Len() == 0 { + break + } + if h, _, err := buffer.WriteBlockTo(zlibWriter); err == nil { + switch h.Tag { + case transactionBlockTag: + requestBufTransactions++ + case spanBlockTag: + requestBufSpans++ + case errorBlockTag: + requestBufErrors++ + } + zlibWriter.Write([]byte("\n")) + zlibFlushed = false + } + } + if !closeRequest { + closeRequest = requestBytesRead+requestBuf.Len() >= cfg.requestSize + } + } + if closeRequest { + if !zlibClosed { + zlibWriter.Close() + zlibClosed = true + } + } else if flushRequest && !zlibFlushed { + zlibWriter.Flush() + flushRequest = false + zlibFlushed = true + } + + if req.Buf == nil || requestBuf.Len() == 0 { + continue + } + const zlibHeaderLen = 2 + if requestBytesRead+requestBuf.Len() > zlibHeaderLen { + n, err := requestBuf.Read(req.Buf) + if closeRequest && err == nil && requestBuf.Len() == 0 { + err = io.EOF + } + req.Respond(n, err) + req.Buf = nil + if n > 0 { + requestBytesRead += n + } + } + } +} + +// jsonRequestMetadata returns a JSON-encoded metadata object that features +// at the head of every request body. This is called exactly once, when the +// first request is made. +func (t *Tracer) jsonRequestMetadata() []byte { + var json fastjson.Writer + json.RawString(`{"metadata":`) + t.encodeRequestMetadata(&json) + json.RawString("}\n") + return json.Bytes() +} + +// metadataReader returns an io.Reader that holds the JSON-encoded metadata, +// suitable for including in a profile request. +func (t *Tracer) metadataReader() io.Reader { + var metadata fastjson.Writer + t.encodeRequestMetadata(&metadata) + return bytes.NewReader(metadata.Bytes()) +} + +func (t *Tracer) encodeRequestMetadata(json *fastjson.Writer) { + service := makeService(t.Service.Name, t.Service.Version, t.Service.Environment) + json.RawString(`{"system":`) + t.system.MarshalFastJSON(json) + json.RawString(`,"process":`) + t.process.MarshalFastJSON(json) + json.RawString(`,"service":`) + service.MarshalFastJSON(json) + if len(globalLabels) > 0 { + json.RawString(`,"labels":`) + globalLabels.MarshalFastJSON(json) + } + json.RawByte('}') +} + +// gatherMetrics gathers metrics from each of the registered +// metrics gatherers. Once all gatherers have returned, a value +// will be sent on the "gathered" channel. +func (t *Tracer) gatherMetrics(ctx context.Context, gatherers []MetricsGatherer, m *Metrics, l Logger, gathered chan<- struct{}) { + timestamp := model.Time(time.Now().UTC()) + var group sync.WaitGroup + for _, g := range gatherers { + group.Add(1) + go func(g MetricsGatherer) { + defer group.Done() + gatherMetrics(ctx, g, m, l) + }(g) + } + go func() { + group.Wait() + for _, m := range m.transactionGroupMetrics { + m.Timestamp = timestamp + } + for _, m := range m.metrics { + m.Timestamp = timestamp + } + gathered <- struct{}{} + }() +} + +type tracerEventType int + +const ( + transactionEvent tracerEventType = iota + spanEvent + errorEvent +) + +type tracerEvent struct { + eventType tracerEventType + + // err is set only if eventType == errorEvent. + err *ErrorData + + // tx is set only if eventType == transactionEvent. + tx struct { + *Transaction + // Transaction.TransactionData is nil at the + // point tracerEvent is created (to signify + // that the transaction is ended), so we pass + // it along side. + *TransactionData + } + + // span is set only if eventType == spanEvent. + span struct { + *Span + // Span.SpanData is nil at the point tracerEvent + // is created (to signify that the span is ended), + // so we pass it along side. + *SpanData + } +} diff --git a/vendor/go.elastic.co/apm/tracer_stats.go b/vendor/go.elastic.co/apm/tracer_stats.go new file mode 100644 index 00000000000..6e1436e8598 --- /dev/null +++ b/vendor/go.elastic.co/apm/tracer_stats.go @@ -0,0 +1,52 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +// TracerStats holds statistics for a Tracer. +type TracerStats struct { + Errors TracerStatsErrors + ErrorsSent uint64 + ErrorsDropped uint64 + TransactionsSent uint64 + TransactionsDropped uint64 + SpansSent uint64 + SpansDropped uint64 +} + +// TracerStatsErrors holds error statistics for a Tracer. +type TracerStatsErrors struct { + SetContext uint64 + SendStream uint64 +} + +func (s TracerStats) isZero() bool { + return s == TracerStats{} +} + +// accumulate updates the stats by accumulating them with +// the values in rhs. +func (s *TracerStats) accumulate(rhs TracerStats) { + s.Errors.SetContext += rhs.Errors.SetContext + s.Errors.SendStream += rhs.Errors.SendStream + s.ErrorsSent += rhs.ErrorsSent + s.ErrorsDropped += rhs.ErrorsDropped + s.SpansSent += rhs.SpansSent + s.SpansDropped += rhs.SpansDropped + s.TransactionsSent += rhs.TransactionsSent + s.TransactionsDropped += rhs.TransactionsDropped +} diff --git a/vendor/go.elastic.co/apm/transaction.go b/vendor/go.elastic.co/apm/transaction.go new file mode 100644 index 00000000000..1e2ce56e9d5 --- /dev/null +++ b/vendor/go.elastic.co/apm/transaction.go @@ -0,0 +1,324 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + cryptorand "crypto/rand" + "encoding/binary" + "math/rand" + "sync" + "time" +) + +// StartTransaction returns a new Transaction with the specified +// name and type, and with the start time set to the current time. +// This is equivalent to calling StartTransactionOptions with a +// zero TransactionOptions. +func (t *Tracer) StartTransaction(name, transactionType string) *Transaction { + return t.StartTransactionOptions(name, transactionType, TransactionOptions{}) +} + +// StartTransactionOptions returns a new Transaction with the +// specified name, type, and options. +func (t *Tracer) StartTransactionOptions(name, transactionType string, opts TransactionOptions) *Transaction { + td, _ := t.transactionDataPool.Get().(*TransactionData) + if td == nil { + td = &TransactionData{ + Duration: -1, + Context: Context{ + captureBodyMask: CaptureBodyTransactions, + }, + spanTimings: make(spanTimingsMap), + } + var seed int64 + if err := binary.Read(cryptorand.Reader, binary.LittleEndian, &seed); err != nil { + seed = time.Now().UnixNano() + } + td.rand = rand.New(rand.NewSource(seed)) + } + tx := &Transaction{tracer: t, TransactionData: td} + + tx.Name = name + tx.Type = transactionType + + var root bool + if opts.TraceContext.Trace.Validate() == nil { + tx.traceContext.Trace = opts.TraceContext.Trace + tx.traceContext.Options = opts.TraceContext.Options + if opts.TraceContext.Span.Validate() == nil { + tx.parentSpan = opts.TraceContext.Span + } + if opts.TransactionID.Validate() == nil { + tx.traceContext.Span = opts.TransactionID + } else { + binary.LittleEndian.PutUint64(tx.traceContext.Span[:], tx.rand.Uint64()) + } + if opts.TraceContext.State.Validate() == nil { + tx.traceContext.State = opts.TraceContext.State + } + } else { + // Start a new trace. We reuse the trace ID for the root transaction's ID + // if one is not specified in the options. + root = true + binary.LittleEndian.PutUint64(tx.traceContext.Trace[:8], tx.rand.Uint64()) + binary.LittleEndian.PutUint64(tx.traceContext.Trace[8:], tx.rand.Uint64()) + if opts.TransactionID.Validate() == nil { + tx.traceContext.Span = opts.TransactionID + } else { + copy(tx.traceContext.Span[:], tx.traceContext.Trace[:]) + } + } + + // Take a snapshot of config that should apply to all spans within the + // transaction. + instrumentationConfig := t.instrumentationConfig() + tx.maxSpans = instrumentationConfig.maxSpans + tx.spanFramesMinDuration = instrumentationConfig.spanFramesMinDuration + tx.stackTraceLimit = instrumentationConfig.stackTraceLimit + tx.Context.captureHeaders = instrumentationConfig.captureHeaders + tx.breakdownMetricsEnabled = t.breakdownMetrics.enabled + tx.propagateLegacyHeader = instrumentationConfig.propagateLegacyHeader + + if root { + sampler := instrumentationConfig.sampler + if sampler == nil || sampler.Sample(tx.traceContext) { + o := tx.traceContext.Options.WithRecorded(true) + tx.traceContext.Options = o + } + } else { + // TODO(axw) make this behaviour configurable. In some cases + // it may not be a good idea to honour the recorded flag, as + // it may open up the application to DoS by forced sampling. + // Even ignoring bad actors, a service that has many feeder + // applications may end up being sampled at a very high rate. + tx.traceContext.Options = opts.TraceContext.Options + } + tx.timestamp = opts.Start + if tx.timestamp.IsZero() { + tx.timestamp = time.Now() + } + return tx +} + +// TransactionOptions holds options for Tracer.StartTransactionOptions. +type TransactionOptions struct { + // TraceContext holds the TraceContext for a new transaction. If this is + // zero, a new trace will be started. + TraceContext TraceContext + + // TransactionID holds the ID to assign to the transaction. If this is + // zero, a new ID will be generated and used instead. + TransactionID SpanID + + // Start is the start time of the transaction. If this has the + // zero value, time.Now() will be used instead. + Start time.Time +} + +// Transaction describes an event occurring in the monitored service. +type Transaction struct { + tracer *Tracer + traceContext TraceContext + + mu sync.RWMutex + + // TransactionData holds the transaction data. This field is set to + // nil when either of the transaction's End or Discard methods are called. + *TransactionData +} + +// Sampled reports whether or not the transaction is sampled. +func (tx *Transaction) Sampled() bool { + if tx == nil { + return false + } + return tx.traceContext.Options.Recorded() +} + +// TraceContext returns the transaction's TraceContext. +// +// The resulting TraceContext's Span field holds the transaction's ID. +// If tx is nil, a zero (invalid) TraceContext is returned. +func (tx *Transaction) TraceContext() TraceContext { + if tx == nil { + return TraceContext{} + } + return tx.traceContext +} + +// ShouldPropagateLegacyHeader reports whether instrumentation should +// propagate the legacy "Elastic-Apm-Traceparent" header in addition to +// the standard W3C "traceparent" header. +// +// This method will be removed in a future major version when we remove +// support for propagating the legacy header. +func (tx *Transaction) ShouldPropagateLegacyHeader() bool { + tx.mu.Lock() + defer tx.mu.Unlock() + if tx.ended() { + return false + } + return tx.propagateLegacyHeader +} + +// EnsureParent returns the span ID for for tx's parent, generating a +// parent span ID if one has not already been set and tx has not been +// ended. If tx is nil or has been ended, a zero (invalid) SpanID is +// returned. +// +// This method can be used for generating a span ID for the RUM +// (Real User Monitoring) agent, where the RUM agent is initialized +// after the backend service returns. +func (tx *Transaction) EnsureParent() SpanID { + if tx == nil { + return SpanID{} + } + + tx.mu.Lock() + defer tx.mu.Unlock() + if tx.ended() { + return SpanID{} + } + + tx.TransactionData.mu.Lock() + defer tx.TransactionData.mu.Unlock() + if tx.parentSpan.isZero() { + // parentSpan can only be zero if tx is a root transaction + // for which GenerateParentTraceContext() has not previously + // been called. Reuse the latter half of the trace ID for + // the parent span ID; the first half is used for the + // transaction ID. + copy(tx.parentSpan[:], tx.traceContext.Trace[8:]) + } + return tx.parentSpan +} + +// Discard discards a previously started transaction. +// +// Calling Discard will set tx's TransactionData field to nil, so callers must +// ensure tx is not updated after Discard returns. +func (tx *Transaction) Discard() { + tx.mu.Lock() + defer tx.mu.Unlock() + if tx.ended() { + return + } + tx.reset(tx.tracer) +} + +// End enqueues tx for sending to the Elastic APM server. +// +// Calling End will set tx's TransactionData field to nil, so callers +// must ensure tx is not updated after End returns. +// +// If tx.Duration has not been set, End will set it to the elapsed time +// since the transaction's start time. +func (tx *Transaction) End() { + tx.mu.Lock() + defer tx.mu.Unlock() + if tx.ended() { + return + } + if tx.Duration < 0 { + tx.Duration = time.Since(tx.timestamp) + } + tx.enqueue() + tx.TransactionData = nil +} + +func (tx *Transaction) enqueue() { + event := tracerEvent{eventType: transactionEvent} + event.tx.Transaction = tx + event.tx.TransactionData = tx.TransactionData + select { + case tx.tracer.events <- event: + default: + // Enqueuing a transaction should never block. + tx.tracer.breakdownMetrics.recordTransaction(tx.TransactionData) + + // TODO(axw) use an atomic operation to increment. + tx.tracer.statsMu.Lock() + tx.tracer.stats.TransactionsDropped++ + tx.tracer.statsMu.Unlock() + tx.reset(tx.tracer) + } +} + +// ended reports whether or not End or Discard has been called. +// +// This must be called with tx.mu held. +func (tx *Transaction) ended() bool { + return tx.TransactionData == nil +} + +// TransactionData holds the details for a transaction, and is embedded +// inside Transaction. When a transaction is ended, its TransactionData +// field will be set to nil. +type TransactionData struct { + // Name holds the transaction name, initialized with the value + // passed to StartTransaction. + Name string + + // Type holds the transaction type, initialized with the value + // passed to StartTransaction. + Type string + + // Duration holds the transaction duration, initialized to -1. + // + // If you do not update Duration, calling Transaction.End will + // calculate the duration based on the elapsed time since the + // transaction's start time. + Duration time.Duration + + // Context describes the context in which the transaction occurs. + Context Context + + // Result holds the transaction result. + Result string + + maxSpans int + spanFramesMinDuration time.Duration + stackTraceLimit int + breakdownMetricsEnabled bool + propagateLegacyHeader bool + timestamp time.Time + + mu sync.Mutex + spansCreated int + spansDropped int + childrenTimer childrenTimer + spanTimings spanTimingsMap + rand *rand.Rand // for ID generation + // parentSpan holds the transaction's parent ID. It is protected by + // mu, since it can be updated by calling EnsureParent. + parentSpan SpanID +} + +// reset resets the TransactionData back to its zero state and places it back +// into the transaction pool. +func (td *TransactionData) reset(tracer *Tracer) { + *td = TransactionData{ + Context: td.Context, + Duration: -1, + rand: td.rand, + spanTimings: td.spanTimings, + } + td.Context.reset() + td.spanTimings.reset() + tracer.transactionDataPool.Put(td) +} diff --git a/vendor/go.elastic.co/apm/transport/api.go b/vendor/go.elastic.co/apm/transport/api.go new file mode 100644 index 00000000000..a9c2fe263d3 --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/api.go @@ -0,0 +1,33 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package transport + +import ( + "context" + "io" +) + +// Transport provides an interface for sending streams of encoded model +// entities to the Elastic APM server, and for querying config. Methods +// are not required to be safe for concurrent use. +type Transport interface { + // SendStream sends a data stream to the server, returning when the + // stream has been closed (Read returns io.EOF) or the HTTP request + // terminates. + SendStream(context.Context, io.Reader) error +} diff --git a/vendor/go.elastic.co/apm/transport/default.go b/vendor/go.elastic.co/apm/transport/default.go new file mode 100644 index 00000000000..2a730eda67f --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/default.go @@ -0,0 +1,54 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package transport + +var ( + // Default is the default Transport, using the + // ELASTIC_APM_* environment variables. + // + // If ELASTIC_APM_SERVER_URL is set to an invalid + // location, Default will be set to a Transport + // returning an error for every operation. + Default Transport + + // Discard is a Transport on which all operations + // succeed without doing anything. + Discard = discardTransport{} +) + +func init() { + _, _ = InitDefault() +} + +// InitDefault (re-)initializes Default, the default Transport, returning +// its new value along with the error that will be returned by the Transport +// if the environment variable configuration is invalid. The result is always +// non-nil. +func InitDefault() (Transport, error) { + t, err := getDefault() + Default = t + return t, err +} + +func getDefault() (Transport, error) { + s, err := NewHTTPTransport() + if err != nil { + return discardTransport{err}, err + } + return s, nil +} diff --git a/vendor/go.elastic.co/apm/transport/discard.go b/vendor/go.elastic.co/apm/transport/discard.go new file mode 100644 index 00000000000..3d61a34da7f --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/discard.go @@ -0,0 +1,31 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package transport + +import ( + "context" + "io" +) + +type discardTransport struct { + err error +} + +func (s discardTransport) SendStream(context.Context, io.Reader) error { + return s.err +} diff --git a/vendor/go.elastic.co/apm/transport/doc.go b/vendor/go.elastic.co/apm/transport/doc.go new file mode 100644 index 00000000000..c5bf29e1909 --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/doc.go @@ -0,0 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package transport provides an interface and implementation +// for transporting data to the Elastic APM server. +package transport diff --git a/vendor/go.elastic.co/apm/transport/http.go b/vendor/go.elastic.co/apm/transport/http.go new file mode 100644 index 00000000000..d084a51d417 --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/http.go @@ -0,0 +1,638 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package transport + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "encoding/pem" + "fmt" + "io" + "io/ioutil" + "math/rand" + "mime/multipart" + "net/http" + "net/textproto" + "net/url" + "os" + "path" + "runtime" + "strconv" + "strings" + "sync/atomic" + "time" + + "github.com/pkg/errors" + + "go.elastic.co/apm/apmconfig" + "go.elastic.co/apm/internal/apmversion" + "go.elastic.co/apm/internal/configutil" +) + +const ( + intakePath = "/intake/v2/events" + profilePath = "/intake/v2/profile" + configPath = "/config/v1/agents" + + envAPIKey = "ELASTIC_APM_API_KEY" + envSecretToken = "ELASTIC_APM_SECRET_TOKEN" + envServerURLs = "ELASTIC_APM_SERVER_URLS" + envServerURL = "ELASTIC_APM_SERVER_URL" + envServerTimeout = "ELASTIC_APM_SERVER_TIMEOUT" + envServerCert = "ELASTIC_APM_SERVER_CERT" + envVerifyServerCert = "ELASTIC_APM_VERIFY_SERVER_CERT" +) + +var ( + // Take a copy of the http.DefaultTransport pointer, + // in case another package replaces the value later. + defaultHTTPTransport = http.DefaultTransport.(*http.Transport) + + defaultServerURL, _ = url.Parse("http://localhost:8200") + defaultServerTimeout = 30 * time.Second +) + +// HTTPTransport is an implementation of Transport, sending payloads via +// a net/http client. +type HTTPTransport struct { + // Client exposes the http.Client used by the HTTPTransport for + // sending requests to the APM Server. + Client *http.Client + intakeHeaders http.Header + configHeaders http.Header + profileHeaders http.Header + shuffleRand *rand.Rand + + urlIndex int32 + intakeURLs []*url.URL + configURLs []*url.URL + profileURLs []*url.URL +} + +// NewHTTPTransport returns a new HTTPTransport which can be used for +// streaming data to the APM Server. The returned HTTPTransport will be +// initialized using the following environment variables: +// +// - ELASTIC_APM_SERVER_URLS: a comma-separated list of APM Server URLs. +// The transport will use this list of URLs for sending requests, +// switching to the next URL in the list upon error. The list will be +// shuffled first. If no URLs are specified, then the transport will +// use the default URL "http://localhost:8200". +// +// - ELASTIC_APM_SERVER_TIMEOUT: timeout for requests to the APM Server. +// If not specified, defaults to 30 seconds. +// +// - ELASTIC_APM_SECRET_TOKEN: used to authenticate the agent. +// +// - ELASTIC_APM_SERVER_CERT: path to a PEM-encoded certificate that +// must match the APM Server-supplied certificate. This can be used +// to pin a self signed certificate. If this is set, then +// ELASTIC_APM_VERIFY_SERVER_CERT is ignored. +// +// - ELASTIC_APM_VERIFY_SERVER_CERT: if set to "false", the transport +// will not verify the APM Server's TLS certificate. Only relevant +// when using HTTPS. By default, the transport will verify server +// certificates. +// +func NewHTTPTransport() (*HTTPTransport, error) { + verifyServerCert, err := configutil.ParseBoolEnv(envVerifyServerCert, true) + if err != nil { + return nil, err + } + + serverTimeout, err := configutil.ParseDurationEnv(envServerTimeout, defaultServerTimeout) + if err != nil { + return nil, err + } + if serverTimeout < 0 { + serverTimeout = 0 + } + + serverURLs, err := initServerURLs() + if err != nil { + return nil, err + } + + tlsConfig := &tls.Config{InsecureSkipVerify: !verifyServerCert} + serverCertPath := os.Getenv(envServerCert) + if serverCertPath != "" { + serverCert, err := loadCertificate(serverCertPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to load certificate from %s", serverCertPath) + } + // Disable standard verification, we'll check that the + // server supplies the exact certificate provided. + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + return verifyPeerCertificate(rawCerts, serverCert) + } + } + + client := &http.Client{ + Timeout: serverTimeout, + Transport: &http.Transport{ + Proxy: defaultHTTPTransport.Proxy, + DialContext: defaultHTTPTransport.DialContext, + MaxIdleConns: defaultHTTPTransport.MaxIdleConns, + IdleConnTimeout: defaultHTTPTransport.IdleConnTimeout, + TLSHandshakeTimeout: defaultHTTPTransport.TLSHandshakeTimeout, + ExpectContinueTimeout: defaultHTTPTransport.ExpectContinueTimeout, + TLSClientConfig: tlsConfig, + }, + } + + commonHeaders := make(http.Header) + commonHeaders.Set("User-Agent", defaultUserAgent()) + + intakeHeaders := copyHeaders(commonHeaders) + intakeHeaders.Set("Content-Type", "application/x-ndjson") + intakeHeaders.Set("Content-Encoding", "deflate") + intakeHeaders.Set("Transfer-Encoding", "chunked") + + profileHeaders := copyHeaders(commonHeaders) + + t := &HTTPTransport{ + Client: client, + configHeaders: commonHeaders, + intakeHeaders: intakeHeaders, + profileHeaders: profileHeaders, + } + if apiKey := os.Getenv(envAPIKey); apiKey != "" { + t.SetAPIKey(apiKey) + } else if secretToken := os.Getenv(envSecretToken); secretToken != "" { + t.SetSecretToken(secretToken) + } + t.SetServerURL(serverURLs...) + return t, nil +} + +// SetServerURL sets the APM Server URL (or URLs) for sending requests. +// At least one URL must be specified, or the method will panic. The +// list will be randomly shuffled. +func (t *HTTPTransport) SetServerURL(u ...*url.URL) { + if len(u) == 0 { + panic("SetServerURL expects at least one URL") + } + intakeURLs := make([]*url.URL, len(u)) + configURLs := make([]*url.URL, len(u)) + profileURLs := make([]*url.URL, len(u)) + for i, u := range u { + intakeURLs[i] = urlWithPath(u, intakePath) + configURLs[i] = urlWithPath(u, configPath) + profileURLs[i] = urlWithPath(u, profilePath) + } + if n := len(intakeURLs); n > 0 { + if t.shuffleRand == nil { + t.shuffleRand = rand.New(rand.NewSource(time.Now().UnixNano())) + } + for i := n - 1; i > 0; i-- { + j := t.shuffleRand.Intn(i + 1) + intakeURLs[i], intakeURLs[j] = intakeURLs[j], intakeURLs[i] + configURLs[i], configURLs[j] = configURLs[j], configURLs[i] + profileURLs[i], profileURLs[j] = profileURLs[j], profileURLs[i] + } + } + t.intakeURLs = intakeURLs + t.configURLs = configURLs + t.profileURLs = profileURLs + t.urlIndex = 0 +} + +// SetUserAgent sets the User-Agent header that will be sent with each request. +func (t *HTTPTransport) SetUserAgent(ua string) { + t.setCommonHeader("User-Agent", ua) +} + +// SetSecretToken sets the Authorization header with the given secret token. +// +// This overrides the value specified via the ELASTIC_APM_SECRET_TOKEN or +// ELASTIC_APM_API_KEY environment variables, if either are set. +func (t *HTTPTransport) SetSecretToken(secretToken string) { + if secretToken != "" { + t.setCommonHeader("Authorization", "Bearer "+secretToken) + } else { + t.deleteCommonHeader("Authorization") + } +} + +// SetAPIKey sets the Authorization header with the given API Key. +// +// This overrides the value specified via the ELASTIC_APM_SECRET_TOKEN or +// ELASTIC_APM_API_KEY environment variables, if either are set. +func (t *HTTPTransport) SetAPIKey(apiKey string) { + if apiKey != "" { + t.setCommonHeader("Authorization", "ApiKey "+apiKey) + } else { + t.deleteCommonHeader("Authorization") + } +} + +func (t *HTTPTransport) setCommonHeader(key, value string) { + t.configHeaders.Set(key, value) + t.intakeHeaders.Set(key, value) + t.profileHeaders.Set(key, value) +} + +func (t *HTTPTransport) deleteCommonHeader(key string) { + t.configHeaders.Del(key) + t.intakeHeaders.Del(key) + t.profileHeaders.Del(key) +} + +// SendStream sends the stream over HTTP. If SendStream returns an error and +// the transport is configured with more than one APM Server URL, then the +// following request will be sent to the next URL in the list. +func (t *HTTPTransport) SendStream(ctx context.Context, r io.Reader) error { + urlIndex := atomic.LoadInt32(&t.urlIndex) + intakeURL := t.intakeURLs[urlIndex] + req := t.newRequest("POST", intakeURL) + req = requestWithContext(ctx, req) + req.Header = t.intakeHeaders + req.Body = ioutil.NopCloser(r) + if err := t.sendStreamRequest(req); err != nil { + atomic.StoreInt32(&t.urlIndex, (urlIndex+1)%int32(len(t.intakeURLs))) + return err + } + return nil +} + +func (t *HTTPTransport) sendStreamRequest(req *http.Request) error { + resp, err := t.Client.Do(req) + if err != nil { + return errors.Wrap(err, "sending event request failed") + } + switch resp.StatusCode { + case http.StatusOK, http.StatusAccepted: + resp.Body.Close() + return nil + } + defer resp.Body.Close() + + result := newHTTPError(resp) + if resp.StatusCode == http.StatusNotFound && result.Message == "404 page not found" { + // This may be an old (pre-6.5) APM server + // that does not support the v2 intake API. + result.Message = fmt.Sprintf("%s not found (requires APM Server 6.5.0 or newer)", req.URL) + } + return result +} + +// SendProfile sends a symbolised pprof profile, encoded as protobuf, and gzip-compressed. +// +// NOTE this is an experimental API, and may be removed in a future minor version, without +// being considered a breaking change. +func (t *HTTPTransport) SendProfile( + ctx context.Context, + metadataReader io.Reader, + profileReaders ...io.Reader, +) error { + urlIndex := atomic.LoadInt32(&t.urlIndex) + profileURL := t.profileURLs[urlIndex] + req := t.newRequest("POST", profileURL) + req = requestWithContext(ctx, req) + req.Header = t.profileHeaders + + writeBody := func(w *multipart.Writer) error { + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="metadata"`)) + h.Set("Content-Type", "application/json") + part, err := w.CreatePart(h) + if err != nil { + return err + } + if _, err := io.Copy(part, metadataReader); err != nil { + return err + } + + for _, profileReader := range profileReaders { + h = make(textproto.MIMEHeader) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="profile"`)) + h.Set("Content-Type", `application/x-protobuf; messageType="perftools.profiles.Profile"`) + part, err = w.CreatePart(h) + if err != nil { + return err + } + if _, err := io.Copy(part, profileReader); err != nil { + return err + } + } + return w.Close() + } + pipeR, pipeW := io.Pipe() + mpw := multipart.NewWriter(pipeW) + req.Header.Set("Content-Type", mpw.FormDataContentType()) + req.Body = pipeR + go func() { + err := writeBody(mpw) + pipeW.CloseWithError(err) + }() + return t.sendProfileRequest(req) +} + +func (t *HTTPTransport) sendProfileRequest(req *http.Request) error { + resp, err := t.Client.Do(req) + if err != nil { + return errors.Wrap(err, "sending profile request failed") + } + switch resp.StatusCode { + case http.StatusOK, http.StatusAccepted: + resp.Body.Close() + return nil + } + defer resp.Body.Close() + + result := newHTTPError(resp) + if resp.StatusCode == http.StatusNotFound && result.Message == "404 page not found" { + // TODO(axw) correct minimum server version. + result.Message = fmt.Sprintf("%s not found (requires APM Server 7.5.0 or newer)", req.URL) + } + return result +} + +// WatchConfig polls the APM Server for agent config changes, sending +// them over the returned channel. +func (t *HTTPTransport) WatchConfig(ctx context.Context, args apmconfig.WatchParams) <-chan apmconfig.Change { + // We have an initial delay to allow application initialisation code + // to close apm.DefaultTracer, which would cancel watching config. + const initialDelay = 1 * time.Second + + changes := make(chan apmconfig.Change) + go func() { + defer close(changes) + + var etag string + var out chan apmconfig.Change + var change apmconfig.Change + timer := time.NewTimer(initialDelay) + for { + select { + case <-ctx.Done(): + return + case out <- change: + out = nil + change = apmconfig.Change{} + continue + case <-timer.C: + } + + urlIndex := atomic.LoadInt32(&t.urlIndex) + query := make(url.Values) + query.Set("service.name", args.Service.Name) + if args.Service.Environment != "" { + query.Set("service.environment", args.Service.Environment) + } + url := *t.configURLs[urlIndex] + url.RawQuery = query.Encode() + + req := t.newRequest("GET", &url) + req.Header = t.configHeaders + if etag != "" { + req.Header = copyHeaders(req.Header) + req.Header.Set("If-None-Match", strconv.QuoteToASCII(etag)) + } + + req = requestWithContext(ctx, req) + resp := t.configRequest(req) + var send bool + if resp.err != nil { + // The request will have failed if the context has been + // cancelled. No need to send a a change in this case. + send = ctx.Err() == nil + } + if !send && resp.attrs != nil { + etag = resp.etag + send = true + } + if send { + change = apmconfig.Change{Err: resp.err, Attrs: resp.attrs} + out = changes + } + timer.Reset(resp.maxAge) + } + }() + return changes +} + +func (t *HTTPTransport) configRequest(req *http.Request) configResponse { + // defaultMaxAge is the default amount of time to wait between + // requests. This should only be used when the server does not + // respond with a Cache-Control header, or where the header is + // malformed. + const defaultMaxAge = 5 * time.Minute + + resp, err := t.Client.Do(req) + if err != nil { + // TODO(axw) this might indicate that the APM Server is unavailable. + // In this case, we should allow a change in URL due to SendStream + // to cut the defaultMaxAge delay short. + return configResponse{ + err: errors.Wrap(err, "sending config request failed"), + maxAge: defaultMaxAge, + } + } + defer resp.Body.Close() + + var response configResponse + if etag, err := strconv.Unquote(resp.Header.Get("Etag")); err == nil { + response.etag = etag + } + cacheControl := parseCacheControl(resp.Header.Get("Cache-Control")) + response.maxAge = cacheControl.maxAge + if response.maxAge < 0 { + response.maxAge = defaultMaxAge + } + + switch resp.StatusCode { + case http.StatusNotModified, http.StatusForbidden, http.StatusNotFound: + // 304 (Not Modified) is returned when the config has not changed since the previous query. + // 403 (Forbidden) is returned if the server does not have the connection to Kibana enabled. + // 404 (Not Found) is returned by old servers that do not implement the config endpoint. + return response + case http.StatusOK: + attrs := make(map[string]string) + // TODO(axw) handling EOF shouldn't be necessary, server currently responds with an empty + // body when there is no config. + if err := json.NewDecoder(resp.Body).Decode(&attrs); err != nil && err != io.EOF { + response.err = err + } else { + response.attrs = attrs + } + return response + } + response.err = newHTTPError(resp) + return response +} + +func (t *HTTPTransport) newRequest(method string, url *url.URL) *http.Request { + req := &http.Request{ + Method: method, + URL: url, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Host: url.Host, + } + return req +} + +func urlWithPath(url *url.URL, p string) *url.URL { + urlCopy := *url + urlCopy.Path = path.Clean(urlCopy.Path + p) + if urlCopy.RawPath != "" { + urlCopy.RawPath = path.Clean(urlCopy.RawPath + p) + } + return &urlCopy +} + +// HTTPError is an error returned by HTTPTransport methods when requests fail. +type HTTPError struct { + Response *http.Response + Message string +} + +func newHTTPError(resp *http.Response) *HTTPError { + bodyContents, err := ioutil.ReadAll(resp.Body) + if err == nil { + resp.Body = ioutil.NopCloser(bytes.NewReader(bodyContents)) + } + return &HTTPError{ + Response: resp, + Message: strings.TrimSpace(string(bodyContents)), + } +} + +func (e *HTTPError) Error() string { + msg := fmt.Sprintf("request failed with %s", e.Response.Status) + if e.Message != "" { + msg += ": " + e.Message + } + return msg +} + +// initServerURLs parses ELASTIC_APM_SERVER_URLS if specified, +// otherwise parses ELASTIC_APM_SERVER_URL if specified. If +// neither are specified, then the default localhost URL is +// returned. +func initServerURLs() ([]*url.URL, error) { + key := envServerURLs + value := os.Getenv(key) + if value == "" { + key = envServerURL + value = os.Getenv(key) + } + var urls []*url.URL + for _, field := range strings.Split(value, ",") { + field = strings.TrimSpace(field) + if field == "" { + continue + } + u, err := url.Parse(field) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", key) + } + urls = append(urls, u) + } + if len(urls) == 0 { + urls = []*url.URL{defaultServerURL} + } + return urls, nil +} + +func requestWithContext(ctx context.Context, req *http.Request) *http.Request { + url := req.URL + req.URL = nil + reqCopy := req.WithContext(ctx) + reqCopy.URL = url + req.URL = url + return reqCopy +} + +func loadCertificate(path string) (*x509.Certificate, error) { + pemBytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + for { + var certBlock *pem.Block + certBlock, pemBytes = pem.Decode(pemBytes) + if certBlock == nil { + return nil, errors.New("missing or invalid certificate") + } + if certBlock.Type == "CERTIFICATE" { + return x509.ParseCertificate(certBlock.Bytes) + } + } +} + +func verifyPeerCertificate(rawCerts [][]byte, trusted *x509.Certificate) error { + if len(rawCerts) == 0 { + return errors.New("missing leaf certificate") + } + cert, err := x509.ParseCertificate(rawCerts[0]) + if err != nil { + return errors.Wrap(err, "failed to parse certificate from server") + } + if !cert.Equal(trusted) { + return errors.New("failed to verify server certificate") + } + return nil +} + +func defaultUserAgent() string { + return fmt.Sprintf("elasticapm-go/%s go/%s", apmversion.AgentVersion, runtime.Version()) +} + +func copyHeaders(in http.Header) http.Header { + out := make(http.Header, len(in)) + for k, vs := range in { + vsCopy := make([]string, len(vs)) + copy(vsCopy, vs) + out[k] = vsCopy + } + return out +} + +type configResponse struct { + err error + attrs map[string]string + etag string + maxAge time.Duration +} + +type cacheControl struct { + maxAge time.Duration +} + +func parseCacheControl(s string) cacheControl { + fields := strings.SplitN(s, "max-age=", 2) + if len(fields) < 2 { + return cacheControl{maxAge: -1} + } + s = fields[1] + if i := strings.IndexRune(s, ','); i != -1 { + s = s[:i] + } + maxAge, err := strconv.ParseUint(s, 10, 32) + if err != nil { + return cacheControl{maxAge: -1} + } + return cacheControl{maxAge: time.Duration(maxAge) * time.Second} +} diff --git a/vendor/go.elastic.co/apm/transport/transporttest/doc.go b/vendor/go.elastic.co/apm/transport/transporttest/doc.go new file mode 100644 index 00000000000..13f9e3adf1f --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/transporttest/doc.go @@ -0,0 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Package transporttest provides implementations of +// transport.Transport for testing purposes. +package transporttest diff --git a/vendor/go.elastic.co/apm/transport/transporttest/err.go b/vendor/go.elastic.co/apm/transport/transporttest/err.go new file mode 100644 index 00000000000..668fff8a310 --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/transporttest/err.go @@ -0,0 +1,54 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package transporttest + +import ( + "context" + "io" + "io/ioutil" + + "go.elastic.co/apm/transport" +) + +// Discard is a transport.Transport which discards +// all streams, and returns no errors. +var Discard transport.Transport = ErrorTransport{} + +// ErrorTransport is a transport that returns the stored error +// for each method call. +type ErrorTransport struct { + Error error +} + +// SendStream discards the stream and returns t.Error. +func (t ErrorTransport) SendStream(ctx context.Context, r io.Reader) error { + errc := make(chan error, 1) + go func() { + _, err := io.Copy(ioutil.Discard, r) + errc <- err + }() + select { + case err := <-errc: + if err != nil { + return err + } + return t.Error + case <-ctx.Done(): + return ctx.Err() + } +} diff --git a/vendor/go.elastic.co/apm/transport/transporttest/recorder.go b/vendor/go.elastic.co/apm/transport/transporttest/recorder.go new file mode 100644 index 00000000000..4db125ab8b4 --- /dev/null +++ b/vendor/go.elastic.co/apm/transport/transporttest/recorder.go @@ -0,0 +1,203 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package transporttest + +import ( + "compress/zlib" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "sync" + + "github.com/google/go-cmp/cmp" + + "go.elastic.co/apm" + "go.elastic.co/apm/model" +) + +// NewRecorderTracer returns a new apm.Tracer and +// RecorderTransport, which is set as the tracer's transport. +// +// DEPRECATED. Use apmtest.NewRecordingTracer instead. +func NewRecorderTracer() (*apm.Tracer, *RecorderTransport) { + var transport RecorderTransport + tracer, err := apm.NewTracerOptions(apm.TracerOptions{ + ServiceName: "transporttest", + Transport: &transport, + }) + if err != nil { + panic(err) + } + return tracer, &transport +} + +// RecorderTransport implements transport.Transport, recording the +// streams sent. The streams can be retrieved using the Payloads +// method. +type RecorderTransport struct { + mu sync.Mutex + metadata *metadata + payloads Payloads +} + +// ResetPayloads clears out any recorded payloads. +func (r *RecorderTransport) ResetPayloads() { + r.mu.Lock() + defer r.mu.Unlock() + r.payloads = Payloads{} +} + +// SendStream records the stream such that it can later be obtained via Payloads. +func (r *RecorderTransport) SendStream(ctx context.Context, stream io.Reader) error { + return r.record(ctx, stream) +} + +// SendProfile records the stream such that it can later be obtained via Payloads. +func (r *RecorderTransport) SendProfile(ctx context.Context, metadata io.Reader, profiles ...io.Reader) error { + return r.recordProto(ctx, metadata, profiles) +} + +// Metadata returns the metadata recorded by the transport. If metadata is yet to +// be received, this method will panic. +func (r *RecorderTransport) Metadata() (_ model.System, _ model.Process, _ model.Service, labels model.StringMap) { + r.mu.Lock() + defer r.mu.Unlock() + return r.metadata.System, r.metadata.Process, r.metadata.Service, r.metadata.Labels +} + +// Payloads returns the payloads recorded by SendStream. +func (r *RecorderTransport) Payloads() Payloads { + r.mu.Lock() + defer r.mu.Unlock() + return r.payloads +} + +func (r *RecorderTransport) record(ctx context.Context, stream io.Reader) error { + reader, err := zlib.NewReader(stream) + if err != nil { + if err == io.ErrUnexpectedEOF { + if contextDone(ctx) { + return ctx.Err() + } + // truly unexpected + } + panic(err) + } + decoder := json.NewDecoder(reader) + + // The first object of any request must be a metadata struct. + var metadataPayload struct { + Metadata metadata `json:"metadata"` + } + if err := decoder.Decode(&metadataPayload); err != nil { + panic(err) + } + r.recordMetadata(&metadataPayload.Metadata) + + for { + var payload struct { + Error *model.Error `json:"error"` + Metrics *model.Metrics `json:"metricset"` + Span *model.Span `json:"span"` + Transaction *model.Transaction `json:"transaction"` + } + err := decoder.Decode(&payload) + if err == io.EOF || (err == io.ErrUnexpectedEOF && contextDone(ctx)) { + break + } else if err != nil { + panic(err) + } + r.mu.Lock() + switch { + case payload.Error != nil: + r.payloads.Errors = append(r.payloads.Errors, *payload.Error) + case payload.Metrics != nil: + r.payloads.Metrics = append(r.payloads.Metrics, *payload.Metrics) + case payload.Span != nil: + r.payloads.Spans = append(r.payloads.Spans, *payload.Span) + case payload.Transaction != nil: + r.payloads.Transactions = append(r.payloads.Transactions, *payload.Transaction) + } + r.mu.Unlock() + } + return nil +} + +func (r *RecorderTransport) recordProto(ctx context.Context, metadataReader io.Reader, profileReaders []io.Reader) error { + var metadata metadata + if err := json.NewDecoder(metadataReader).Decode(&metadata); err != nil { + panic(err) + } + r.recordMetadata(&metadata) + + r.mu.Lock() + defer r.mu.Unlock() + for _, profileReader := range profileReaders { + data, err := ioutil.ReadAll(profileReader) + if err != nil { + panic(err) + } + r.payloads.Profiles = append(r.payloads.Profiles, data) + } + return nil +} + +func (r *RecorderTransport) recordMetadata(m *metadata) { + r.mu.Lock() + defer r.mu.Unlock() + if r.metadata == nil { + r.metadata = m + } else { + // Make sure the metadata doesn't change between requests. + if diff := cmp.Diff(r.metadata, m); diff != "" { + panic(fmt.Errorf("metadata changed\n%s", diff)) + } + } +} + +func contextDone(ctx context.Context) bool { + select { + case <-ctx.Done(): + return true + default: + return false + } +} + +// Payloads holds the recorded payloads. +type Payloads struct { + Errors []model.Error + Metrics []model.Metrics + Spans []model.Span + Transactions []model.Transaction + Profiles [][]byte +} + +// Len returns the number of recorded payloads. +func (p *Payloads) Len() int { + return len(p.Transactions) + len(p.Errors) + len(p.Metrics) +} + +type metadata struct { + System model.System `json:"system"` + Process model.Process `json:"process"` + Service model.Service `json:"service"` + Labels model.StringMap `json:"labels,omitempty"` +} diff --git a/vendor/go.elastic.co/apm/utils.go b/vendor/go.elastic.co/apm/utils.go new file mode 100644 index 00000000000..ae24404e315 --- /dev/null +++ b/vendor/go.elastic.co/apm/utils.go @@ -0,0 +1,242 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "fmt" + "math/rand" + "os" + "path/filepath" + "reflect" + "regexp" + "runtime" + "strings" + "time" + + "github.com/pkg/errors" + + "go.elastic.co/apm/internal/apmhostutil" + "go.elastic.co/apm/internal/apmstrings" + "go.elastic.co/apm/model" +) + +var ( + currentProcess model.Process + goAgent = model.Agent{Name: "go", Version: AgentVersion} + goLanguage = model.Language{Name: "go", Version: runtime.Version()} + goRuntime = model.Runtime{Name: runtime.Compiler, Version: runtime.Version()} + localSystem model.System + + serviceNameInvalidRegexp = regexp.MustCompile("[^" + serviceNameValidClass + "]") + labelKeyReplacer = strings.NewReplacer(`.`, `_`, `*`, `_`, `"`, `_`) + + rtypeBool = reflect.TypeOf(false) + rtypeFloat64 = reflect.TypeOf(float64(0)) +) + +const ( + envHostname = "ELASTIC_APM_HOSTNAME" + envServiceNodeName = "ELASTIC_APM_SERVICE_NODE_NAME" + + serviceNameValidClass = "a-zA-Z0-9 _-" + + // At the time of writing, all keyword length limits + // are 1024 runes, enforced by JSON Schema. + stringLengthLimit = 1024 + + // Non-keyword string fields are not limited in length + // by JSON Schema, but we still truncate all strings. + // Some strings, such as database statement, we explicitly + // allow to be longer than others. + longStringLengthLimit = 10000 +) + +func init() { + currentProcess = getCurrentProcess() + localSystem = getLocalSystem() +} + +func getCurrentProcess() model.Process { + ppid := os.Getppid() + title, err := currentProcessTitle() + if err != nil || title == "" { + title = filepath.Base(os.Args[0]) + } + return model.Process{ + Pid: os.Getpid(), + Ppid: &ppid, + Title: truncateString(title), + Argv: os.Args, + } +} + +func makeService(name, version, environment string) model.Service { + service := model.Service{ + Name: truncateString(name), + Version: truncateString(version), + Environment: truncateString(environment), + Agent: &goAgent, + Language: &goLanguage, + Runtime: &goRuntime, + } + + serviceNodeName := os.Getenv(envServiceNodeName) + if serviceNodeName != "" { + service.Node = &model.ServiceNode{ConfiguredName: truncateString(serviceNodeName)} + } + + return service +} + +func getLocalSystem() model.System { + system := model.System{ + Architecture: runtime.GOARCH, + Platform: runtime.GOOS, + } + system.Hostname = os.Getenv(envHostname) + if system.Hostname == "" { + if hostname, err := os.Hostname(); err == nil { + system.Hostname = hostname + } + } + system.Hostname = truncateString(system.Hostname) + if container, err := apmhostutil.Container(); err == nil { + system.Container = container + } + system.Kubernetes = getKubernetesMetadata() + return system +} + +func getKubernetesMetadata() *model.Kubernetes { + kubernetes, err := apmhostutil.Kubernetes() + if err != nil { + kubernetes = nil + } + namespace := os.Getenv("KUBERNETES_NAMESPACE") + podName := os.Getenv("KUBERNETES_POD_NAME") + podUID := os.Getenv("KUBERNETES_POD_UID") + nodeName := os.Getenv("KUBERNETES_NODE_NAME") + if namespace == "" && podName == "" && podUID == "" && nodeName == "" { + return kubernetes + } + if kubernetes == nil { + kubernetes = &model.Kubernetes{} + } + if namespace != "" { + kubernetes.Namespace = namespace + } + if nodeName != "" { + if kubernetes.Node == nil { + kubernetes.Node = &model.KubernetesNode{} + } + kubernetes.Node.Name = nodeName + } + if podName != "" || podUID != "" { + if kubernetes.Pod == nil { + kubernetes.Pod = &model.KubernetesPod{} + } + if podName != "" { + kubernetes.Pod.Name = podName + } + if podUID != "" { + kubernetes.Pod.UID = podUID + } + } + return kubernetes +} + +func cleanLabelKey(k string) string { + return labelKeyReplacer.Replace(k) +} + +// makeLabelValue returns v as a value suitable for including +// in a label value. If v is numerical or boolean, then it will +// be returned as-is; otherwise the value will be returned as a +// string, using fmt.Sprint if necessary, and possibly truncated +// using truncateString. +func makeLabelValue(v interface{}) interface{} { + switch v.(type) { + case nil, bool, float32, float64, + uint, uint8, uint16, uint32, uint64, + int, int8, int16, int32, int64: + return v + case string: + return truncateString(v.(string)) + } + // Slow path. If v has a non-basic type whose underlying + // type is convertible to bool or float64, return v as-is. + // Otherwise, stringify. + rtype := reflect.TypeOf(v) + if rtype.ConvertibleTo(rtypeBool) || rtype.ConvertibleTo(rtypeFloat64) { + // Custom type + return v + } + return truncateString(fmt.Sprint(v)) +} + +func validateServiceName(name string) error { + idx := serviceNameInvalidRegexp.FindStringIndex(name) + if idx == nil { + return nil + } + return errors.Errorf( + "invalid service name %q: character %q is not in the allowed set (%s)", + name, name[idx[0]], serviceNameValidClass, + ) +} + +func sanitizeServiceName(name string) string { + return serviceNameInvalidRegexp.ReplaceAllString(name, "_") +} + +func truncateString(s string) string { + s, _ = apmstrings.Truncate(s, stringLengthLimit) + return s +} + +func truncateLongString(s string) string { + s, _ = apmstrings.Truncate(s, longStringLengthLimit) + return s +} + +func nextGracePeriod(p time.Duration) time.Duration { + if p == -1 { + return 0 + } + for i := time.Duration(0); i < 6; i++ { + if p == (i * i * time.Second) { + return (i + 1) * (i + 1) * time.Second + } + } + return p +} + +// jitterDuration returns d +/- some multiple of d in the range [0,j]. +func jitterDuration(d time.Duration, rng *rand.Rand, j float64) time.Duration { + if d == 0 || j == 0 { + return d + } + r := (rng.Float64() * j * 2) - j + return d + time.Duration(float64(d)*r) +} + +func durationMicros(d time.Duration) float64 { + us := d / time.Microsecond + ns := d % time.Microsecond + return float64(us) + float64(ns)/1e9 +} diff --git a/vendor/go.elastic.co/apm/utils_linux.go b/vendor/go.elastic.co/apm/utils_linux.go new file mode 100644 index 00000000000..abf97366ab2 --- /dev/null +++ b/vendor/go.elastic.co/apm/utils_linux.go @@ -0,0 +1,40 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +import ( + "bytes" + "syscall" + "unsafe" +) + +func currentProcessTitle() (string, error) { + // PR_GET_NAME (since Linux 2.6.11) + // Return the name of the calling thread, in the buffer pointed to by + // (char *) arg2. The buffer should allow space for up to 16 bytes; + // the returned string will be null-terminated. + var buf [16]byte + if _, _, errno := syscall.RawSyscall6( + syscall.SYS_PRCTL, syscall.PR_GET_NAME, + uintptr(unsafe.Pointer(&buf[0])), + 0, 0, 0, 0, + ); errno != 0 { + return "", errno + } + return string(buf[:bytes.IndexByte(buf[:], 0)]), nil +} diff --git a/vendor/go.elastic.co/apm/utils_other.go b/vendor/go.elastic.co/apm/utils_other.go new file mode 100644 index 00000000000..06a38c5d9e4 --- /dev/null +++ b/vendor/go.elastic.co/apm/utils_other.go @@ -0,0 +1,38 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//+build !linux + +package apm + +import ( + "github.com/pkg/errors" + + sysinfo "github.com/elastic/go-sysinfo" +) + +func currentProcessTitle() (string, error) { + proc, err := sysinfo.Self() + if err != nil { + return "", errors.Wrap(err, "failed to get process info") + } + info, err := proc.Info() + if err != nil { + return "", errors.Wrap(err, "failed to get process info") + } + return info.Name, nil +} diff --git a/vendor/go.elastic.co/apm/version.go b/vendor/go.elastic.co/apm/version.go new file mode 100644 index 00000000000..4992c913715 --- /dev/null +++ b/vendor/go.elastic.co/apm/version.go @@ -0,0 +1,23 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apm + +const ( + // AgentVersion is the Elastic APM Go Agent version. + AgentVersion = "1.7.2" +) diff --git a/vendor/go.elastic.co/fastjson/.travis.yml b/vendor/go.elastic.co/fastjson/.travis.yml new file mode 100644 index 00000000000..9949d26e09c --- /dev/null +++ b/vendor/go.elastic.co/fastjson/.travis.yml @@ -0,0 +1,9 @@ +language: go +go_import_path: go.elastic.co/fastjson + +go: + - stable + - "1.8.x" + +script: + - go test -v ./... diff --git a/vendor/go.elastic.co/fastjson/LICENSE b/vendor/go.elastic.co/fastjson/LICENSE new file mode 100644 index 00000000000..f44e6a0fa12 --- /dev/null +++ b/vendor/go.elastic.co/fastjson/LICENSE @@ -0,0 +1,23 @@ +Copyright 2018 Elasticsearch BV + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +--- + +Copyright (c) 2016 Mail.Ru Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/go.elastic.co/fastjson/README.md b/vendor/go.elastic.co/fastjson/README.md new file mode 100644 index 00000000000..59ccccfaada --- /dev/null +++ b/vendor/go.elastic.co/fastjson/README.md @@ -0,0 +1,134 @@ +[![Travis-CI](https://travis-ci.org/elastic/go-fastjson.svg)](https://travis-ci.org/elastic/go-fastjson) + +# fastjson: fast JSON encoder for Go + +Package fastjson provides a library and code generator for fast JSON encoding. + +The supplied code generator (cmd/generate-fastjson) generates JSON marshalling +methods for all exported types within a specified package. + +## Requirements + +Go 1.8+ + +## License + +Apache 2.0. + +## Installation + +```bash +go get -u go.elastic.co/fastjson/... +``` + +## Code generation + +Package fastjson is intended to be used with the accompanying code generator, +cmd/generate-fastjson. This code generator will parse the Go code of a +specified package, and write out fastjson marshalling method (MarshalFastJSON) +definitions for the exported types in the package. + +You can provide your own custom marshalling logic for a type by defining a +MarshalFastJSON method for it. The generator will not generate methods for +those types with existing marshalling methods. + +### Usage + +``` +generate-fastjson + -f remove the output file if it exists + -o string + file to which output will be written (default "-") +``` + +### Custom omitempty extension + +The standard `json` struct tags defined by `encoding/json` are honoured, +enabling you to generate fastjson-marshalling code for your existing code. + +We extend the `omitempty` option by enabling you to define an unexported +method on your type `T`, `func (T) isZero() bool`, which will be called +to determine whether or not the value is considered empty. This enables +`omitempty` on non-pointer struct types. + +### Example + +Given the following package: + +```go +package example + +type Foo struct { + Bar Bar `json:",omitempty"` +} + +type Bar struct { + Baz Baz + Qux *Qux `json:"quux,omitempty"` +} + +func (b Bar) isZero() bool { + return b == (Bar{}) +} + +type Baz struct { +} + +func (Baz) MarshalFastJSON(w *fastjson.Writer) error { +} + +type Qux struct{} +``` + +Assuming we're in the package directory, we would generate the methods like so, +which will write a Go file to stdout: + +```bash +generate-fastjson . +``` + +Output: +```go +// Code generated by "generate-fastjson". DO NOT EDIT. + +package example + +import ( + "go.elastic.co/fastjson" +) + +func (v *Foo) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + if !v.Bar.isZero() { + w.RawString("\"Bar\":") + if err := v.Bar.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr == err + } + } + w.RawByte('}') + return nil +} + +func (v *Bar) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"Baz\":") + if err := v.Baz.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + if v.Qux != nil { + w.RawString(",\"quux\":") + if err := v.Qux.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr == err + } + } + w.RawByte('}') + return firstErr +} + +func (v *Qux) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawByte('}') + return nil +} +``` diff --git a/vendor/go.elastic.co/fastjson/doc.go b/vendor/go.elastic.co/fastjson/doc.go new file mode 100644 index 00000000000..4a3576be31e --- /dev/null +++ b/vendor/go.elastic.co/fastjson/doc.go @@ -0,0 +1,23 @@ +// Copyright 2018 Elasticsearch BV +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package fastjson provides a library for fast JSON encoding, +// optimised for static code generation. +// +// Fastjson functions and interfaces are structured such that +// all encoding appends to a buffer, enabling buffer reuse +// without forcing specific mechanisms such as sync.Pool. This +// enables zero-allocation encoding without incurring any +// concurrency overhead in certain applications. +package fastjson // import "go.elastic.co/fastjson" diff --git a/vendor/go.elastic.co/fastjson/go.mod b/vendor/go.elastic.co/fastjson/go.mod new file mode 100644 index 00000000000..0b130f35c1c --- /dev/null +++ b/vendor/go.elastic.co/fastjson/go.mod @@ -0,0 +1,3 @@ +module go.elastic.co/fastjson + +require github.com/pkg/errors v0.8.0 diff --git a/vendor/go.elastic.co/fastjson/go.sum b/vendor/go.elastic.co/fastjson/go.sum new file mode 100644 index 00000000000..3dfe462f062 --- /dev/null +++ b/vendor/go.elastic.co/fastjson/go.sum @@ -0,0 +1,2 @@ +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/vendor/go.elastic.co/fastjson/marshaler.go b/vendor/go.elastic.co/fastjson/marshaler.go new file mode 100644 index 00000000000..7359e6dfde1 --- /dev/null +++ b/vendor/go.elastic.co/fastjson/marshaler.go @@ -0,0 +1,151 @@ +// Copyright 2018 Elasticsearch BV +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fastjson + +import ( + "encoding/json" + "fmt" + + "github.com/pkg/errors" +) + +// Marshaler defines an interface that types can implement to provide +// fast JSON marshaling. +type Marshaler interface { + // MarshalFastJSON writes a JSON representation of the type to w. + // + // MarshalFastJSON is expected to suppress any panics. Depending + // on the application, it may be expected that MarshalFastJSON + // writes valid JSON to w, even in error cases. + // + // The returned error will be propagated up through to callers of + // fastjson.Marshal. + MarshalFastJSON(w *Writer) error +} + +// Appender defines an interface that types can implement to append +// their JSON representation to a buffer. +type Appender interface { + // AppendJSON appends the JSON representation of the value to the + // buffer, and returns the extended buffer. + // + // AppendJSON is required not to panic or fail. + AppendJSON([]byte) []byte +} + +// Marshal marshals v as JSON to w. +// +// For all basic types, Marshal uses w's methods to marshal the values +// directly. If v implements Marshaler, its MarshalFastJSON method will +// be used; if v implements Appender, its AppendJSON method will be used, +// and it is assumed to append valid JSON. As a final resort, we use +// json.Marshal. +// +// Where json.Marshal is used internally (see above), errors or panics +// produced by json.Marshal will be encoded as JSON objects, with special keys +// "__ERROR__" for errors, and "__PANIC__" for panics. e.g. if json.Marshal +// panics due to a broken json.Marshaler implementation or assumption, then +// Marshal will encode the panic as +// +// {"__PANIC__": "panic calling MarshalJSON for type Foo: reason"} +// +// Marshal returns the first error encountered. +func Marshal(w *Writer, v interface{}) error { + switch v := v.(type) { + case nil: + w.RawString("null") + case string: + w.String(v) + case uint: + w.Uint64(uint64(v)) + case uint8: + w.Uint64(uint64(v)) + case uint16: + w.Uint64(uint64(v)) + case uint32: + w.Uint64(uint64(v)) + case uint64: + w.Uint64(v) + case int: + w.Int64(int64(v)) + case int8: + w.Int64(int64(v)) + case int16: + w.Int64(int64(v)) + case int32: + w.Int64(int64(v)) + case int64: + w.Int64(v) + case float32: + w.Float32(v) + case float64: + w.Float64(v) + case bool: + w.Bool(v) + case map[string]interface{}: + if v == nil { + w.RawString("null") + return nil + } + w.RawByte('{') + var firstErr error + first := true + for k, v := range v { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + if err := Marshal(w, v); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr + case Marshaler: + return v.MarshalFastJSON(w) + case Appender: + w.buf = v.AppendJSON(w.buf) + default: + return marshalReflect(w, v) + } + return nil +} + +func marshalReflect(w *Writer, v interface{}) (result error) { + defer func() { + if r := recover(); r != nil { + err, ok := r.(error) + if !ok { + err = fmt.Errorf("%s", r) + } + result = errors.Wrapf(err, "panic calling MarshalJSON for type %T", v) + w.RawString(`{"__PANIC__":`) + w.String(fmt.Sprint(result)) + w.RawByte('}') + } + }() + raw, err := json.Marshal(v) + if err != nil { + w.RawString(`{"__ERROR__":`) + w.String(fmt.Sprint(err)) + w.RawByte('}') + return err + } + w.RawBytes(raw) + return nil +} diff --git a/vendor/go.elastic.co/fastjson/writer.go b/vendor/go.elastic.co/fastjson/writer.go new file mode 100644 index 00000000000..e66731d9f80 --- /dev/null +++ b/vendor/go.elastic.co/fastjson/writer.go @@ -0,0 +1,181 @@ +// Copyright 2018 Elasticsearch BV +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fastjson + +import ( + "strconv" + "time" + "unicode/utf8" +) + +// Writer is a JSON writer, appending to an internal buffer. +// +// Writer is not safe for concurrent use. A Writer can be +// reset and reused, which will reuse the underlying buffer. +type Writer struct { + buf []byte +} + +// Bytes returns the internal buffer. The result is invalidated when Reset is called. +func (w *Writer) Bytes() []byte { + return w.buf +} + +// Size returns the current size of the buffer. Size is typically used in conjunction +// with Rewind, to mark a position to which the writer may later be rewound. +func (w *Writer) Size() int { + return len(w.buf) +} + +// Rewind rewinds the buffer such that it has size bytes, dropping everything proceeding. +func (w *Writer) Rewind(size int) { + w.buf = w.buf[:size] +} + +// Reset resets the internal []byte buffer to empty. +func (w *Writer) Reset() { + w.buf = w.buf[:0] +} + +// RawByte appends c to the buffer. +func (w *Writer) RawByte(c byte) { + w.buf = append(w.buf, c) +} + +// RawBytes appends data, unmodified, to the buffer. +func (w *Writer) RawBytes(data []byte) { + w.buf = append(w.buf, data...) +} + +// RawString appends s to the buffer. +func (w *Writer) RawString(s string) { + w.buf = append(w.buf, s...) +} + +// Uint64 appends n to the buffer. +func (w *Writer) Uint64(n uint64) { + w.buf = strconv.AppendUint(w.buf, uint64(n), 10) +} + +// Int64 appends n to the buffer. +func (w *Writer) Int64(n int64) { + w.buf = strconv.AppendInt(w.buf, int64(n), 10) +} + +// Float32 appends n to the buffer. +func (w *Writer) Float32(n float32) { + w.buf = strconv.AppendFloat(w.buf, float64(n), 'g', -1, 32) +} + +// Float64 appends n to the buffer. +func (w *Writer) Float64(n float64) { + w.buf = strconv.AppendFloat(w.buf, float64(n), 'g', -1, 64) +} + +// Bool appends v to the buffer. +func (w *Writer) Bool(v bool) { + w.buf = strconv.AppendBool(w.buf, v) +} + +// Time appends t to the buffer, formatted according to layout. +// +// The encoded time is not surrounded by quotes; it is the +// responsibility of the caller to ensure the formatted time is +// quoted as necessary. +func (w *Writer) Time(t time.Time, layout string) { + w.buf = t.AppendFormat(w.buf, layout) +} + +// String appends s, quoted and escaped, to the buffer. +func (w *Writer) String(s string) { + w.RawByte('"') + w.StringContents(s) + w.RawByte('"') +} + +// Note: code below taken from mailru/easyjson, adapted to use Writer. + +const chars = "0123456789abcdef" + +func isNotEscapedSingleChar(c byte, escapeHTML bool) bool { + // Note: might make sense to use a table if there are more chars to escape. With 4 chars + // it benchmarks the same. + if escapeHTML { + return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf + } + return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf +} + +// StringContents is the same as String, but without the surrounding quotes. +func (w *Writer) StringContents(s string) { + // Portions of the string that contain no escapes are appended as byte slices. + + p := 0 // last non-escape symbol + + for i := 0; i < len(s); { + c := s[i] + + if isNotEscapedSingleChar(c, true) { + // single-width character, no escaping is required + i++ + continue + } else if c < utf8.RuneSelf { + // single-with character, need to escape + w.RawString(s[p:i]) + switch c { + case '\t': + w.RawString(`\t`) + case '\r': + w.RawString(`\r`) + case '\n': + w.RawString(`\n`) + case '\\': + w.RawString(`\\`) + case '"': + w.RawString(`\"`) + default: + w.RawString(`\u00`) + w.RawByte(chars[c>>4]) + w.RawByte(chars[c&0xf]) + } + + i++ + p = i + continue + } + + // broken utf + runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) + if runeValue == utf8.RuneError && runeWidth == 1 { + w.RawString(s[p:i]) + w.RawString(`\ufffd`) + i++ + p = i + continue + } + + // jsonp stuff - tab separator and line separator + if runeValue == '\u2028' || runeValue == '\u2029' { + w.RawString(s[p:i]) + w.RawString(`\u202`) + w.RawByte(chars[runeValue&0xf]) + i += runeWidth + p = i + continue + } + i += runeWidth + } + w.RawString(s[p:]) +} diff --git a/vendor/golang.org/x/crypto/blowfish/block.go b/vendor/golang.org/x/crypto/blowfish/block.go new file mode 100644 index 00000000000..9d80f19521b --- /dev/null +++ b/vendor/golang.org/x/crypto/blowfish/block.go @@ -0,0 +1,159 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package blowfish + +// getNextWord returns the next big-endian uint32 value from the byte slice +// at the given position in a circular manner, updating the position. +func getNextWord(b []byte, pos *int) uint32 { + var w uint32 + j := *pos + for i := 0; i < 4; i++ { + w = w<<8 | uint32(b[j]) + j++ + if j >= len(b) { + j = 0 + } + } + *pos = j + return w +} + +// ExpandKey performs a key expansion on the given *Cipher. Specifically, it +// performs the Blowfish algorithm's key schedule which sets up the *Cipher's +// pi and substitution tables for calls to Encrypt. This is used, primarily, +// by the bcrypt package to reuse the Blowfish key schedule during its +// set up. It's unlikely that you need to use this directly. +func ExpandKey(key []byte, c *Cipher) { + j := 0 + for i := 0; i < 18; i++ { + // Using inlined getNextWord for performance. + var d uint32 + for k := 0; k < 4; k++ { + d = d<<8 | uint32(key[j]) + j++ + if j >= len(key) { + j = 0 + } + } + c.p[i] ^= d + } + + var l, r uint32 + for i := 0; i < 18; i += 2 { + l, r = encryptBlock(l, r, c) + c.p[i], c.p[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s0[i], c.s0[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s1[i], c.s1[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s2[i], c.s2[i+1] = l, r + } + for i := 0; i < 256; i += 2 { + l, r = encryptBlock(l, r, c) + c.s3[i], c.s3[i+1] = l, r + } +} + +// This is similar to ExpandKey, but folds the salt during the key +// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero +// salt passed in, reusing ExpandKey turns out to be a place of inefficiency +// and specializing it here is useful. +func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) { + j := 0 + for i := 0; i < 18; i++ { + c.p[i] ^= getNextWord(key, &j) + } + + j = 0 + var l, r uint32 + for i := 0; i < 18; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.p[i], c.p[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s0[i], c.s0[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s1[i], c.s1[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s2[i], c.s2[i+1] = l, r + } + + for i := 0; i < 256; i += 2 { + l ^= getNextWord(salt, &j) + r ^= getNextWord(salt, &j) + l, r = encryptBlock(l, r, c) + c.s3[i], c.s3[i+1] = l, r + } +} + +func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { + xl, xr := l, r + xl ^= c.p[0] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16] + xr ^= c.p[17] + return xr, xl +} + +func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { + xl, xr := l, r + xl ^= c.p[17] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3] + xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2] + xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1] + xr ^= c.p[0] + return xr, xl +} diff --git a/vendor/golang.org/x/crypto/blowfish/cipher.go b/vendor/golang.org/x/crypto/blowfish/cipher.go new file mode 100644 index 00000000000..213bf204afe --- /dev/null +++ b/vendor/golang.org/x/crypto/blowfish/cipher.go @@ -0,0 +1,99 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm. +// +// Blowfish is a legacy cipher and its short block size makes it vulnerable to +// birthday bound attacks (see https://sweet32.info). It should only be used +// where compatibility with legacy systems, not security, is the goal. +// +// Deprecated: any new system should use AES (from crypto/aes, if necessary in +// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from +// golang.org/x/crypto/chacha20poly1305). +package blowfish // import "golang.org/x/crypto/blowfish" + +// The code is a port of Bruce Schneier's C implementation. +// See https://www.schneier.com/blowfish.html. + +import "strconv" + +// The Blowfish block size in bytes. +const BlockSize = 8 + +// A Cipher is an instance of Blowfish encryption using a particular key. +type Cipher struct { + p [18]uint32 + s0, s1, s2, s3 [256]uint32 +} + +type KeySizeError int + +func (k KeySizeError) Error() string { + return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k)) +} + +// NewCipher creates and returns a Cipher. +// The key argument should be the Blowfish key, from 1 to 56 bytes. +func NewCipher(key []byte) (*Cipher, error) { + var result Cipher + if k := len(key); k < 1 || k > 56 { + return nil, KeySizeError(k) + } + initCipher(&result) + ExpandKey(key, &result) + return &result, nil +} + +// NewSaltedCipher creates a returns a Cipher that folds a salt into its key +// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is +// sufficient and desirable. For bcrypt compatibility, the key can be over 56 +// bytes. +func NewSaltedCipher(key, salt []byte) (*Cipher, error) { + if len(salt) == 0 { + return NewCipher(key) + } + var result Cipher + if k := len(key); k < 1 { + return nil, KeySizeError(k) + } + initCipher(&result) + expandKeyWithSalt(key, salt, &result) + return &result, nil +} + +// BlockSize returns the Blowfish block size, 8 bytes. +// It is necessary to satisfy the Block interface in the +// package "crypto/cipher". +func (c *Cipher) BlockSize() int { return BlockSize } + +// Encrypt encrypts the 8-byte buffer src using the key k +// and stores the result in dst. +// Note that for amounts of data larger than a block, +// it is not safe to just call Encrypt on successive blocks; +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). +func (c *Cipher) Encrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + l, r = encryptBlock(l, r, c) + dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) + dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) +} + +// Decrypt decrypts the 8-byte buffer src using the key k +// and stores the result in dst. +func (c *Cipher) Decrypt(dst, src []byte) { + l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) + r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) + l, r = decryptBlock(l, r, c) + dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l) + dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r) +} + +func initCipher(c *Cipher) { + copy(c.p[0:], p[0:]) + copy(c.s0[0:], s0[0:]) + copy(c.s1[0:], s1[0:]) + copy(c.s2[0:], s2[0:]) + copy(c.s3[0:], s3[0:]) +} diff --git a/vendor/golang.org/x/crypto/blowfish/const.go b/vendor/golang.org/x/crypto/blowfish/const.go new file mode 100644 index 00000000000..d04077595ab --- /dev/null +++ b/vendor/golang.org/x/crypto/blowfish/const.go @@ -0,0 +1,199 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The startup permutation array and substitution boxes. +// They are the hexadecimal digits of PI; see: +// https://www.schneier.com/code/constants.txt. + +package blowfish + +var s0 = [256]uint32{ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, + 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, + 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, + 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, + 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, + 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, + 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, + 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, + 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, + 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, + 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, + 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, + 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, + 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, + 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, + 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, + 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, + 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, + 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, +} + +var s1 = [256]uint32{ + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, + 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, + 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, + 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, + 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, + 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, + 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, + 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, + 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, + 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, + 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, + 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, + 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, + 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, + 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, + 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, + 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, + 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, + 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, +} + +var s2 = [256]uint32{ + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, + 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, + 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, + 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, + 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, + 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, + 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, + 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, + 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, + 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, + 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, + 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, + 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, + 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, + 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, + 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, + 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, + 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, + 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, + 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, + 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, + 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, +} + +var s3 = [256]uint32{ + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, + 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, + 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, + 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, + 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, + 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, + 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, + 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, + 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, + 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, + 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, + 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, + 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, + 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, + 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, + 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, + 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, + 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, + 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, +} + +var p = [18]uint32{ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go new file mode 100644 index 00000000000..87f1e369cc2 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.11 +// +build !gccgo,!appengine + +package chacha20 + +const bufSize = 256 + +//go:noescape +func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) + +func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) { + xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter) +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s new file mode 100644 index 00000000000..b3a16ef751a --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s @@ -0,0 +1,308 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.11 +// +build !gccgo,!appengine + +#include "textflag.h" + +#define NUM_ROUNDS 10 + +// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) +TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0 + MOVD dst+0(FP), R1 + MOVD src+24(FP), R2 + MOVD src_len+32(FP), R3 + MOVD key+48(FP), R4 + MOVD nonce+56(FP), R6 + MOVD counter+64(FP), R7 + + MOVD $·constants(SB), R10 + MOVD $·incRotMatrix(SB), R11 + + MOVW (R7), R20 + + AND $~255, R3, R13 + ADD R2, R13, R12 // R12 for block end + AND $255, R3, R13 +loop: + MOVD $NUM_ROUNDS, R21 + VLD1 (R11), [V30.S4, V31.S4] + + // load contants + // VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4] + WORD $0x4D60E940 + + // load keys + // VLD4R 16(R4), [V4.S4, V5.S4, V6.S4, V7.S4] + WORD $0x4DFFE884 + // VLD4R 16(R4), [V8.S4, V9.S4, V10.S4, V11.S4] + WORD $0x4DFFE888 + SUB $32, R4 + + // load counter + nonce + // VLD1R (R7), [V12.S4] + WORD $0x4D40C8EC + + // VLD3R (R6), [V13.S4, V14.S4, V15.S4] + WORD $0x4D40E8CD + + // update counter + VADD V30.S4, V12.S4, V12.S4 + +chacha: + // V0..V3 += V4..V7 + // V12..V15 <<<= ((V12..V15 XOR V0..V3), 16) + VADD V0.S4, V4.S4, V0.S4 + VADD V1.S4, V5.S4, V1.S4 + VADD V2.S4, V6.S4, V2.S4 + VADD V3.S4, V7.S4, V3.S4 + VEOR V12.B16, V0.B16, V12.B16 + VEOR V13.B16, V1.B16, V13.B16 + VEOR V14.B16, V2.B16, V14.B16 + VEOR V15.B16, V3.B16, V15.B16 + VREV32 V12.H8, V12.H8 + VREV32 V13.H8, V13.H8 + VREV32 V14.H8, V14.H8 + VREV32 V15.H8, V15.H8 + // V8..V11 += V12..V15 + // V4..V7 <<<= ((V4..V7 XOR V8..V11), 12) + VADD V8.S4, V12.S4, V8.S4 + VADD V9.S4, V13.S4, V9.S4 + VADD V10.S4, V14.S4, V10.S4 + VADD V11.S4, V15.S4, V11.S4 + VEOR V8.B16, V4.B16, V16.B16 + VEOR V9.B16, V5.B16, V17.B16 + VEOR V10.B16, V6.B16, V18.B16 + VEOR V11.B16, V7.B16, V19.B16 + VSHL $12, V16.S4, V4.S4 + VSHL $12, V17.S4, V5.S4 + VSHL $12, V18.S4, V6.S4 + VSHL $12, V19.S4, V7.S4 + VSRI $20, V16.S4, V4.S4 + VSRI $20, V17.S4, V5.S4 + VSRI $20, V18.S4, V6.S4 + VSRI $20, V19.S4, V7.S4 + + // V0..V3 += V4..V7 + // V12..V15 <<<= ((V12..V15 XOR V0..V3), 8) + VADD V0.S4, V4.S4, V0.S4 + VADD V1.S4, V5.S4, V1.S4 + VADD V2.S4, V6.S4, V2.S4 + VADD V3.S4, V7.S4, V3.S4 + VEOR V12.B16, V0.B16, V12.B16 + VEOR V13.B16, V1.B16, V13.B16 + VEOR V14.B16, V2.B16, V14.B16 + VEOR V15.B16, V3.B16, V15.B16 + VTBL V31.B16, [V12.B16], V12.B16 + VTBL V31.B16, [V13.B16], V13.B16 + VTBL V31.B16, [V14.B16], V14.B16 + VTBL V31.B16, [V15.B16], V15.B16 + + // V8..V11 += V12..V15 + // V4..V7 <<<= ((V4..V7 XOR V8..V11), 7) + VADD V12.S4, V8.S4, V8.S4 + VADD V13.S4, V9.S4, V9.S4 + VADD V14.S4, V10.S4, V10.S4 + VADD V15.S4, V11.S4, V11.S4 + VEOR V8.B16, V4.B16, V16.B16 + VEOR V9.B16, V5.B16, V17.B16 + VEOR V10.B16, V6.B16, V18.B16 + VEOR V11.B16, V7.B16, V19.B16 + VSHL $7, V16.S4, V4.S4 + VSHL $7, V17.S4, V5.S4 + VSHL $7, V18.S4, V6.S4 + VSHL $7, V19.S4, V7.S4 + VSRI $25, V16.S4, V4.S4 + VSRI $25, V17.S4, V5.S4 + VSRI $25, V18.S4, V6.S4 + VSRI $25, V19.S4, V7.S4 + + // V0..V3 += V5..V7, V4 + // V15,V12-V14 <<<= ((V15,V12-V14 XOR V0..V3), 16) + VADD V0.S4, V5.S4, V0.S4 + VADD V1.S4, V6.S4, V1.S4 + VADD V2.S4, V7.S4, V2.S4 + VADD V3.S4, V4.S4, V3.S4 + VEOR V15.B16, V0.B16, V15.B16 + VEOR V12.B16, V1.B16, V12.B16 + VEOR V13.B16, V2.B16, V13.B16 + VEOR V14.B16, V3.B16, V14.B16 + VREV32 V12.H8, V12.H8 + VREV32 V13.H8, V13.H8 + VREV32 V14.H8, V14.H8 + VREV32 V15.H8, V15.H8 + + // V10 += V15; V5 <<<= ((V10 XOR V5), 12) + // ... + VADD V15.S4, V10.S4, V10.S4 + VADD V12.S4, V11.S4, V11.S4 + VADD V13.S4, V8.S4, V8.S4 + VADD V14.S4, V9.S4, V9.S4 + VEOR V10.B16, V5.B16, V16.B16 + VEOR V11.B16, V6.B16, V17.B16 + VEOR V8.B16, V7.B16, V18.B16 + VEOR V9.B16, V4.B16, V19.B16 + VSHL $12, V16.S4, V5.S4 + VSHL $12, V17.S4, V6.S4 + VSHL $12, V18.S4, V7.S4 + VSHL $12, V19.S4, V4.S4 + VSRI $20, V16.S4, V5.S4 + VSRI $20, V17.S4, V6.S4 + VSRI $20, V18.S4, V7.S4 + VSRI $20, V19.S4, V4.S4 + + // V0 += V5; V15 <<<= ((V0 XOR V15), 8) + // ... + VADD V5.S4, V0.S4, V0.S4 + VADD V6.S4, V1.S4, V1.S4 + VADD V7.S4, V2.S4, V2.S4 + VADD V4.S4, V3.S4, V3.S4 + VEOR V0.B16, V15.B16, V15.B16 + VEOR V1.B16, V12.B16, V12.B16 + VEOR V2.B16, V13.B16, V13.B16 + VEOR V3.B16, V14.B16, V14.B16 + VTBL V31.B16, [V12.B16], V12.B16 + VTBL V31.B16, [V13.B16], V13.B16 + VTBL V31.B16, [V14.B16], V14.B16 + VTBL V31.B16, [V15.B16], V15.B16 + + // V10 += V15; V5 <<<= ((V10 XOR V5), 7) + // ... + VADD V15.S4, V10.S4, V10.S4 + VADD V12.S4, V11.S4, V11.S4 + VADD V13.S4, V8.S4, V8.S4 + VADD V14.S4, V9.S4, V9.S4 + VEOR V10.B16, V5.B16, V16.B16 + VEOR V11.B16, V6.B16, V17.B16 + VEOR V8.B16, V7.B16, V18.B16 + VEOR V9.B16, V4.B16, V19.B16 + VSHL $7, V16.S4, V5.S4 + VSHL $7, V17.S4, V6.S4 + VSHL $7, V18.S4, V7.S4 + VSHL $7, V19.S4, V4.S4 + VSRI $25, V16.S4, V5.S4 + VSRI $25, V17.S4, V6.S4 + VSRI $25, V18.S4, V7.S4 + VSRI $25, V19.S4, V4.S4 + + SUB $1, R21 + CBNZ R21, chacha + + // VLD4R (R10), [V16.S4, V17.S4, V18.S4, V19.S4] + WORD $0x4D60E950 + + // VLD4R 16(R4), [V20.S4, V21.S4, V22.S4, V23.S4] + WORD $0x4DFFE894 + VADD V30.S4, V12.S4, V12.S4 + VADD V16.S4, V0.S4, V0.S4 + VADD V17.S4, V1.S4, V1.S4 + VADD V18.S4, V2.S4, V2.S4 + VADD V19.S4, V3.S4, V3.S4 + // VLD4R 16(R4), [V24.S4, V25.S4, V26.S4, V27.S4] + WORD $0x4DFFE898 + // restore R4 + SUB $32, R4 + + // load counter + nonce + // VLD1R (R7), [V28.S4] + WORD $0x4D40C8FC + // VLD3R (R6), [V29.S4, V30.S4, V31.S4] + WORD $0x4D40E8DD + + VADD V20.S4, V4.S4, V4.S4 + VADD V21.S4, V5.S4, V5.S4 + VADD V22.S4, V6.S4, V6.S4 + VADD V23.S4, V7.S4, V7.S4 + VADD V24.S4, V8.S4, V8.S4 + VADD V25.S4, V9.S4, V9.S4 + VADD V26.S4, V10.S4, V10.S4 + VADD V27.S4, V11.S4, V11.S4 + VADD V28.S4, V12.S4, V12.S4 + VADD V29.S4, V13.S4, V13.S4 + VADD V30.S4, V14.S4, V14.S4 + VADD V31.S4, V15.S4, V15.S4 + + VZIP1 V1.S4, V0.S4, V16.S4 + VZIP2 V1.S4, V0.S4, V17.S4 + VZIP1 V3.S4, V2.S4, V18.S4 + VZIP2 V3.S4, V2.S4, V19.S4 + VZIP1 V5.S4, V4.S4, V20.S4 + VZIP2 V5.S4, V4.S4, V21.S4 + VZIP1 V7.S4, V6.S4, V22.S4 + VZIP2 V7.S4, V6.S4, V23.S4 + VZIP1 V9.S4, V8.S4, V24.S4 + VZIP2 V9.S4, V8.S4, V25.S4 + VZIP1 V11.S4, V10.S4, V26.S4 + VZIP2 V11.S4, V10.S4, V27.S4 + VZIP1 V13.S4, V12.S4, V28.S4 + VZIP2 V13.S4, V12.S4, V29.S4 + VZIP1 V15.S4, V14.S4, V30.S4 + VZIP2 V15.S4, V14.S4, V31.S4 + VZIP1 V18.D2, V16.D2, V0.D2 + VZIP2 V18.D2, V16.D2, V4.D2 + VZIP1 V19.D2, V17.D2, V8.D2 + VZIP2 V19.D2, V17.D2, V12.D2 + VLD1.P 64(R2), [V16.B16, V17.B16, V18.B16, V19.B16] + + VZIP1 V22.D2, V20.D2, V1.D2 + VZIP2 V22.D2, V20.D2, V5.D2 + VZIP1 V23.D2, V21.D2, V9.D2 + VZIP2 V23.D2, V21.D2, V13.D2 + VLD1.P 64(R2), [V20.B16, V21.B16, V22.B16, V23.B16] + VZIP1 V26.D2, V24.D2, V2.D2 + VZIP2 V26.D2, V24.D2, V6.D2 + VZIP1 V27.D2, V25.D2, V10.D2 + VZIP2 V27.D2, V25.D2, V14.D2 + VLD1.P 64(R2), [V24.B16, V25.B16, V26.B16, V27.B16] + VZIP1 V30.D2, V28.D2, V3.D2 + VZIP2 V30.D2, V28.D2, V7.D2 + VZIP1 V31.D2, V29.D2, V11.D2 + VZIP2 V31.D2, V29.D2, V15.D2 + VLD1.P 64(R2), [V28.B16, V29.B16, V30.B16, V31.B16] + VEOR V0.B16, V16.B16, V16.B16 + VEOR V1.B16, V17.B16, V17.B16 + VEOR V2.B16, V18.B16, V18.B16 + VEOR V3.B16, V19.B16, V19.B16 + VST1.P [V16.B16, V17.B16, V18.B16, V19.B16], 64(R1) + VEOR V4.B16, V20.B16, V20.B16 + VEOR V5.B16, V21.B16, V21.B16 + VEOR V6.B16, V22.B16, V22.B16 + VEOR V7.B16, V23.B16, V23.B16 + VST1.P [V20.B16, V21.B16, V22.B16, V23.B16], 64(R1) + VEOR V8.B16, V24.B16, V24.B16 + VEOR V9.B16, V25.B16, V25.B16 + VEOR V10.B16, V26.B16, V26.B16 + VEOR V11.B16, V27.B16, V27.B16 + VST1.P [V24.B16, V25.B16, V26.B16, V27.B16], 64(R1) + VEOR V12.B16, V28.B16, V28.B16 + VEOR V13.B16, V29.B16, V29.B16 + VEOR V14.B16, V30.B16, V30.B16 + VEOR V15.B16, V31.B16, V31.B16 + VST1.P [V28.B16, V29.B16, V30.B16, V31.B16], 64(R1) + + ADD $4, R20 + MOVW R20, (R7) // update counter + + CMP R2, R12 + BGT loop + + RET + + +DATA ·constants+0x00(SB)/4, $0x61707865 +DATA ·constants+0x04(SB)/4, $0x3320646e +DATA ·constants+0x08(SB)/4, $0x79622d32 +DATA ·constants+0x0c(SB)/4, $0x6b206574 +GLOBL ·constants(SB), NOPTR|RODATA, $32 + +DATA ·incRotMatrix+0x00(SB)/4, $0x00000000 +DATA ·incRotMatrix+0x04(SB)/4, $0x00000001 +DATA ·incRotMatrix+0x08(SB)/4, $0x00000002 +DATA ·incRotMatrix+0x0c(SB)/4, $0x00000003 +DATA ·incRotMatrix+0x10(SB)/4, $0x02010003 +DATA ·incRotMatrix+0x14(SB)/4, $0x06050407 +DATA ·incRotMatrix+0x18(SB)/4, $0x0A09080B +DATA ·incRotMatrix+0x1c(SB)/4, $0x0E0D0C0F +GLOBL ·incRotMatrix(SB), NOPTR|RODATA, $32 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_generic.go b/vendor/golang.org/x/crypto/chacha20/chacha_generic.go new file mode 100644 index 00000000000..098ec9f6be0 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_generic.go @@ -0,0 +1,364 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package chacha20 implements the ChaCha20 and XChaCha20 encryption algorithms +// as specified in RFC 8439 and draft-irtf-cfrg-xchacha-01. +package chacha20 + +import ( + "crypto/cipher" + "encoding/binary" + "errors" + "math/bits" + + "golang.org/x/crypto/internal/subtle" +) + +const ( + // KeySize is the size of the key used by this cipher, in bytes. + KeySize = 32 + + // NonceSize is the size of the nonce used with the standard variant of this + // cipher, in bytes. + // + // Note that this is too short to be safely generated at random if the same + // key is reused more than 2³² times. + NonceSize = 12 + + // NonceSizeX is the size of the nonce used with the XChaCha20 variant of + // this cipher, in bytes. + NonceSizeX = 24 +) + +// Cipher is a stateful instance of ChaCha20 or XChaCha20 using a particular key +// and nonce. A *Cipher implements the cipher.Stream interface. +type Cipher struct { + // The ChaCha20 state is 16 words: 4 constant, 8 of key, 1 of counter + // (incremented after each block), and 3 of nonce. + key [8]uint32 + counter uint32 + nonce [3]uint32 + + // The last len bytes of buf are leftover key stream bytes from the previous + // XORKeyStream invocation. The size of buf depends on how many blocks are + // computed at a time. + buf [bufSize]byte + len int + + // The counter-independent results of the first round are cached after they + // are computed the first time. + precompDone bool + p1, p5, p9, p13 uint32 + p2, p6, p10, p14 uint32 + p3, p7, p11, p15 uint32 +} + +var _ cipher.Stream = (*Cipher)(nil) + +// NewUnauthenticatedCipher creates a new ChaCha20 stream cipher with the given +// 32 bytes key and a 12 or 24 bytes nonce. If a nonce of 24 bytes is provided, +// the XChaCha20 construction will be used. It returns an error if key or nonce +// have any other length. +// +// Note that ChaCha20, like all stream ciphers, is not authenticated and allows +// attackers to silently tamper with the plaintext. For this reason, it is more +// appropriate as a building block than as a standalone encryption mechanism. +// Instead, consider using package golang.org/x/crypto/chacha20poly1305. +func NewUnauthenticatedCipher(key, nonce []byte) (*Cipher, error) { + // This function is split into a wrapper so that the Cipher allocation will + // be inlined, and depending on how the caller uses the return value, won't + // escape to the heap. + c := &Cipher{} + return newUnauthenticatedCipher(c, key, nonce) +} + +func newUnauthenticatedCipher(c *Cipher, key, nonce []byte) (*Cipher, error) { + if len(key) != KeySize { + return nil, errors.New("chacha20: wrong key size") + } + if len(nonce) == NonceSizeX { + // XChaCha20 uses the ChaCha20 core to mix 16 bytes of the nonce into a + // derived key, allowing it to operate on a nonce of 24 bytes. See + // draft-irtf-cfrg-xchacha-01, Section 2.3. + key, _ = HChaCha20(key, nonce[0:16]) + cNonce := make([]byte, NonceSize) + copy(cNonce[4:12], nonce[16:24]) + nonce = cNonce + } else if len(nonce) != NonceSize { + return nil, errors.New("chacha20: wrong nonce size") + } + + c.key = [8]uint32{ + binary.LittleEndian.Uint32(key[0:4]), + binary.LittleEndian.Uint32(key[4:8]), + binary.LittleEndian.Uint32(key[8:12]), + binary.LittleEndian.Uint32(key[12:16]), + binary.LittleEndian.Uint32(key[16:20]), + binary.LittleEndian.Uint32(key[20:24]), + binary.LittleEndian.Uint32(key[24:28]), + binary.LittleEndian.Uint32(key[28:32]), + } + c.nonce = [3]uint32{ + binary.LittleEndian.Uint32(nonce[0:4]), + binary.LittleEndian.Uint32(nonce[4:8]), + binary.LittleEndian.Uint32(nonce[8:12]), + } + return c, nil +} + +// The constant first 4 words of the ChaCha20 state. +const ( + j0 uint32 = 0x61707865 // expa + j1 uint32 = 0x3320646e // nd 3 + j2 uint32 = 0x79622d32 // 2-by + j3 uint32 = 0x6b206574 // te k +) + +const blockSize = 64 + +// quarterRound is the core of ChaCha20. It shuffles the bits of 4 state words. +// It's executed 4 times for each of the 20 ChaCha20 rounds, operating on all 16 +// words each round, in columnar or diagonal groups of 4 at a time. +func quarterRound(a, b, c, d uint32) (uint32, uint32, uint32, uint32) { + a += b + d ^= a + d = bits.RotateLeft32(d, 16) + c += d + b ^= c + b = bits.RotateLeft32(b, 12) + a += b + d ^= a + d = bits.RotateLeft32(d, 8) + c += d + b ^= c + b = bits.RotateLeft32(b, 7) + return a, b, c, d +} + +// XORKeyStream XORs each byte in the given slice with a byte from the +// cipher's key stream. Dst and src must overlap entirely or not at all. +// +// If len(dst) < len(src), XORKeyStream will panic. It is acceptable +// to pass a dst bigger than src, and in that case, XORKeyStream will +// only update dst[:len(src)] and will not touch the rest of dst. +// +// Multiple calls to XORKeyStream behave as if the concatenation of +// the src buffers was passed in a single run. That is, Cipher +// maintains state and does not reset at each XORKeyStream call. +func (s *Cipher) XORKeyStream(dst, src []byte) { + if len(src) == 0 { + return + } + if len(dst) < len(src) { + panic("chacha20: output smaller than input") + } + dst = dst[:len(src)] + if subtle.InexactOverlap(dst, src) { + panic("chacha20: invalid buffer overlap") + } + + // First, drain any remaining key stream from a previous XORKeyStream. + if s.len != 0 { + keyStream := s.buf[bufSize-s.len:] + if len(src) < len(keyStream) { + keyStream = keyStream[:len(src)] + } + _ = src[len(keyStream)-1] // bounds check elimination hint + for i, b := range keyStream { + dst[i] = src[i] ^ b + } + s.len -= len(keyStream) + src = src[len(keyStream):] + dst = dst[len(keyStream):] + } + + const blocksPerBuf = bufSize / blockSize + numBufs := (uint64(len(src)) + bufSize - 1) / bufSize + if uint64(s.counter)+numBufs*blocksPerBuf >= 1<<32 { + panic("chacha20: counter overflow") + } + + // xorKeyStreamBlocks implementations expect input lengths that are a + // multiple of bufSize. Platform-specific ones process multiple blocks at a + // time, so have bufSizes that are a multiple of blockSize. + + rem := len(src) % bufSize + full := len(src) - rem + + if full > 0 { + s.xorKeyStreamBlocks(dst[:full], src[:full]) + } + + // If we have a partial (multi-)block, pad it for xorKeyStreamBlocks, and + // keep the leftover keystream for the next XORKeyStream invocation. + if rem > 0 { + s.buf = [bufSize]byte{} + copy(s.buf[:], src[full:]) + s.xorKeyStreamBlocks(s.buf[:], s.buf[:]) + s.len = bufSize - copy(dst[full:], s.buf[:]) + } +} + +func (s *Cipher) xorKeyStreamBlocksGeneric(dst, src []byte) { + if len(dst) != len(src) || len(dst)%blockSize != 0 { + panic("chacha20: internal error: wrong dst and/or src length") + } + + // To generate each block of key stream, the initial cipher state + // (represented below) is passed through 20 rounds of shuffling, + // alternatively applying quarterRounds by columns (like 1, 5, 9, 13) + // or by diagonals (like 1, 6, 11, 12). + // + // 0:cccccccc 1:cccccccc 2:cccccccc 3:cccccccc + // 4:kkkkkkkk 5:kkkkkkkk 6:kkkkkkkk 7:kkkkkkkk + // 8:kkkkkkkk 9:kkkkkkkk 10:kkkkkkkk 11:kkkkkkkk + // 12:bbbbbbbb 13:nnnnnnnn 14:nnnnnnnn 15:nnnnnnnn + // + // c=constant k=key b=blockcount n=nonce + var ( + c0, c1, c2, c3 = j0, j1, j2, j3 + c4, c5, c6, c7 = s.key[0], s.key[1], s.key[2], s.key[3] + c8, c9, c10, c11 = s.key[4], s.key[5], s.key[6], s.key[7] + _, c13, c14, c15 = s.counter, s.nonce[0], s.nonce[1], s.nonce[2] + ) + + // Three quarters of the first round don't depend on the counter, so we can + // calculate them here, and reuse them for multiple blocks in the loop, and + // for future XORKeyStream invocations. + if !s.precompDone { + s.p1, s.p5, s.p9, s.p13 = quarterRound(c1, c5, c9, c13) + s.p2, s.p6, s.p10, s.p14 = quarterRound(c2, c6, c10, c14) + s.p3, s.p7, s.p11, s.p15 = quarterRound(c3, c7, c11, c15) + s.precompDone = true + } + + for i := 0; i < len(src); i += blockSize { + // The remainder of the first column round. + fcr0, fcr4, fcr8, fcr12 := quarterRound(c0, c4, c8, s.counter) + + // The second diagonal round. + x0, x5, x10, x15 := quarterRound(fcr0, s.p5, s.p10, s.p15) + x1, x6, x11, x12 := quarterRound(s.p1, s.p6, s.p11, fcr12) + x2, x7, x8, x13 := quarterRound(s.p2, s.p7, fcr8, s.p13) + x3, x4, x9, x14 := quarterRound(s.p3, fcr4, s.p9, s.p14) + + // The remaining 18 rounds. + for i := 0; i < 9; i++ { + // Column round. + x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12) + x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13) + x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14) + x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15) + + // Diagonal round. + x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15) + x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12) + x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13) + x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14) + } + + // Finally, add back the initial state to generate the key stream. + x0 += c0 + x1 += c1 + x2 += c2 + x3 += c3 + x4 += c4 + x5 += c5 + x6 += c6 + x7 += c7 + x8 += c8 + x9 += c9 + x10 += c10 + x11 += c11 + x12 += s.counter + x13 += c13 + x14 += c14 + x15 += c15 + + s.counter += 1 + if s.counter == 0 { + panic("chacha20: internal error: counter overflow") + } + + in, out := src[i:], dst[i:] + in, out = in[:blockSize], out[:blockSize] // bounds check elimination hint + + // XOR the key stream with the source and write out the result. + xor(out[0:], in[0:], x0) + xor(out[4:], in[4:], x1) + xor(out[8:], in[8:], x2) + xor(out[12:], in[12:], x3) + xor(out[16:], in[16:], x4) + xor(out[20:], in[20:], x5) + xor(out[24:], in[24:], x6) + xor(out[28:], in[28:], x7) + xor(out[32:], in[32:], x8) + xor(out[36:], in[36:], x9) + xor(out[40:], in[40:], x10) + xor(out[44:], in[44:], x11) + xor(out[48:], in[48:], x12) + xor(out[52:], in[52:], x13) + xor(out[56:], in[56:], x14) + xor(out[60:], in[60:], x15) + } +} + +// HChaCha20 uses the ChaCha20 core to generate a derived key from a 32 bytes +// key and a 16 bytes nonce. It returns an error if key or nonce have any other +// length. It is used as part of the XChaCha20 construction. +func HChaCha20(key, nonce []byte) ([]byte, error) { + // This function is split into a wrapper so that the slice allocation will + // be inlined, and depending on how the caller uses the return value, won't + // escape to the heap. + out := make([]byte, 32) + return hChaCha20(out, key, nonce) +} + +func hChaCha20(out, key, nonce []byte) ([]byte, error) { + if len(key) != KeySize { + return nil, errors.New("chacha20: wrong HChaCha20 key size") + } + if len(nonce) != 16 { + return nil, errors.New("chacha20: wrong HChaCha20 nonce size") + } + + x0, x1, x2, x3 := j0, j1, j2, j3 + x4 := binary.LittleEndian.Uint32(key[0:4]) + x5 := binary.LittleEndian.Uint32(key[4:8]) + x6 := binary.LittleEndian.Uint32(key[8:12]) + x7 := binary.LittleEndian.Uint32(key[12:16]) + x8 := binary.LittleEndian.Uint32(key[16:20]) + x9 := binary.LittleEndian.Uint32(key[20:24]) + x10 := binary.LittleEndian.Uint32(key[24:28]) + x11 := binary.LittleEndian.Uint32(key[28:32]) + x12 := binary.LittleEndian.Uint32(nonce[0:4]) + x13 := binary.LittleEndian.Uint32(nonce[4:8]) + x14 := binary.LittleEndian.Uint32(nonce[8:12]) + x15 := binary.LittleEndian.Uint32(nonce[12:16]) + + for i := 0; i < 10; i++ { + // Diagonal round. + x0, x4, x8, x12 = quarterRound(x0, x4, x8, x12) + x1, x5, x9, x13 = quarterRound(x1, x5, x9, x13) + x2, x6, x10, x14 = quarterRound(x2, x6, x10, x14) + x3, x7, x11, x15 = quarterRound(x3, x7, x11, x15) + + // Column round. + x0, x5, x10, x15 = quarterRound(x0, x5, x10, x15) + x1, x6, x11, x12 = quarterRound(x1, x6, x11, x12) + x2, x7, x8, x13 = quarterRound(x2, x7, x8, x13) + x3, x4, x9, x14 = quarterRound(x3, x4, x9, x14) + } + + _ = out[31] // bounds check elimination hint + binary.LittleEndian.PutUint32(out[0:4], x0) + binary.LittleEndian.PutUint32(out[4:8], x1) + binary.LittleEndian.PutUint32(out[8:12], x2) + binary.LittleEndian.PutUint32(out[12:16], x3) + binary.LittleEndian.PutUint32(out[16:20], x12) + binary.LittleEndian.PutUint32(out[20:24], x13) + binary.LittleEndian.PutUint32(out[24:28], x14) + binary.LittleEndian.PutUint32(out[28:32], x15) + return out, nil +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go new file mode 100644 index 00000000000..ec609ed868b --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !arm64,!s390x,!ppc64le arm64,!go1.11 gccgo appengine + +package chacha20 + +const bufSize = blockSize + +func (s *Cipher) xorKeyStreamBlocks(dst, src []byte) { + s.xorKeyStreamBlocksGeneric(dst, src) +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go new file mode 100644 index 00000000000..d0ec61f08d9 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go @@ -0,0 +1,16 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !gccgo,!appengine + +package chacha20 + +const bufSize = 256 + +//go:noescape +func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32) + +func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) { + chaCha20_ctr32_vsx(&dst[0], &src[0], len(src), &c.key, &c.counter) +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s new file mode 100644 index 00000000000..533014ea3e8 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s @@ -0,0 +1,449 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on CRYPTOGAMS code with the following comment: +// # ==================================================================== +// # Written by Andy Polyakov for the OpenSSL +// # project. The module is, however, dual licensed under OpenSSL and +// # CRYPTOGAMS licenses depending on where you obtain it. For further +// # details see http://www.openssl.org/~appro/cryptogams/. +// # ==================================================================== + +// Code for the perl script that generates the ppc64 assembler +// can be found in the cryptogams repository at the link below. It is based on +// the original from openssl. + +// https://github.com/dot-asm/cryptogams/commit/a60f5b50ed908e91 + +// The differences in this and the original implementation are +// due to the calling conventions and initialization of constants. + +// +build !gccgo,!appengine + +#include "textflag.h" + +#define OUT R3 +#define INP R4 +#define LEN R5 +#define KEY R6 +#define CNT R7 +#define TMP R15 + +#define CONSTBASE R16 +#define BLOCKS R17 + +DATA consts<>+0x00(SB)/8, $0x3320646e61707865 +DATA consts<>+0x08(SB)/8, $0x6b20657479622d32 +DATA consts<>+0x10(SB)/8, $0x0000000000000001 +DATA consts<>+0x18(SB)/8, $0x0000000000000000 +DATA consts<>+0x20(SB)/8, $0x0000000000000004 +DATA consts<>+0x28(SB)/8, $0x0000000000000000 +DATA consts<>+0x30(SB)/8, $0x0a0b08090e0f0c0d +DATA consts<>+0x38(SB)/8, $0x0203000106070405 +DATA consts<>+0x40(SB)/8, $0x090a0b080d0e0f0c +DATA consts<>+0x48(SB)/8, $0x0102030005060704 +DATA consts<>+0x50(SB)/8, $0x6170786561707865 +DATA consts<>+0x58(SB)/8, $0x6170786561707865 +DATA consts<>+0x60(SB)/8, $0x3320646e3320646e +DATA consts<>+0x68(SB)/8, $0x3320646e3320646e +DATA consts<>+0x70(SB)/8, $0x79622d3279622d32 +DATA consts<>+0x78(SB)/8, $0x79622d3279622d32 +DATA consts<>+0x80(SB)/8, $0x6b2065746b206574 +DATA consts<>+0x88(SB)/8, $0x6b2065746b206574 +DATA consts<>+0x90(SB)/8, $0x0000000100000000 +DATA consts<>+0x98(SB)/8, $0x0000000300000002 +GLOBL consts<>(SB), RODATA, $0xa0 + +//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32) +TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40 + MOVD out+0(FP), OUT + MOVD inp+8(FP), INP + MOVD len+16(FP), LEN + MOVD key+24(FP), KEY + MOVD counter+32(FP), CNT + + // Addressing for constants + MOVD $consts<>+0x00(SB), CONSTBASE + MOVD $16, R8 + MOVD $32, R9 + MOVD $48, R10 + MOVD $64, R11 + SRD $6, LEN, BLOCKS + // V16 + LXVW4X (CONSTBASE)(R0), VS48 + ADD $80,CONSTBASE + + // Load key into V17,V18 + LXVW4X (KEY)(R0), VS49 + LXVW4X (KEY)(R8), VS50 + + // Load CNT, NONCE into V19 + LXVW4X (CNT)(R0), VS51 + + // Clear V27 + VXOR V27, V27, V27 + + // V28 + LXVW4X (CONSTBASE)(R11), VS60 + + // splat slot from V19 -> V26 + VSPLTW $0, V19, V26 + + VSLDOI $4, V19, V27, V19 + VSLDOI $12, V27, V19, V19 + + VADDUWM V26, V28, V26 + + MOVD $10, R14 + MOVD R14, CTR + +loop_outer_vsx: + // V0, V1, V2, V3 + LXVW4X (R0)(CONSTBASE), VS32 + LXVW4X (R8)(CONSTBASE), VS33 + LXVW4X (R9)(CONSTBASE), VS34 + LXVW4X (R10)(CONSTBASE), VS35 + + // splat values from V17, V18 into V4-V11 + VSPLTW $0, V17, V4 + VSPLTW $1, V17, V5 + VSPLTW $2, V17, V6 + VSPLTW $3, V17, V7 + VSPLTW $0, V18, V8 + VSPLTW $1, V18, V9 + VSPLTW $2, V18, V10 + VSPLTW $3, V18, V11 + + // VOR + VOR V26, V26, V12 + + // splat values from V19 -> V13, V14, V15 + VSPLTW $1, V19, V13 + VSPLTW $2, V19, V14 + VSPLTW $3, V19, V15 + + // splat const values + VSPLTISW $-16, V27 + VSPLTISW $12, V28 + VSPLTISW $8, V29 + VSPLTISW $7, V30 + +loop_vsx: + VADDUWM V0, V4, V0 + VADDUWM V1, V5, V1 + VADDUWM V2, V6, V2 + VADDUWM V3, V7, V3 + + VXOR V12, V0, V12 + VXOR V13, V1, V13 + VXOR V14, V2, V14 + VXOR V15, V3, V15 + + VRLW V12, V27, V12 + VRLW V13, V27, V13 + VRLW V14, V27, V14 + VRLW V15, V27, V15 + + VADDUWM V8, V12, V8 + VADDUWM V9, V13, V9 + VADDUWM V10, V14, V10 + VADDUWM V11, V15, V11 + + VXOR V4, V8, V4 + VXOR V5, V9, V5 + VXOR V6, V10, V6 + VXOR V7, V11, V7 + + VRLW V4, V28, V4 + VRLW V5, V28, V5 + VRLW V6, V28, V6 + VRLW V7, V28, V7 + + VADDUWM V0, V4, V0 + VADDUWM V1, V5, V1 + VADDUWM V2, V6, V2 + VADDUWM V3, V7, V3 + + VXOR V12, V0, V12 + VXOR V13, V1, V13 + VXOR V14, V2, V14 + VXOR V15, V3, V15 + + VRLW V12, V29, V12 + VRLW V13, V29, V13 + VRLW V14, V29, V14 + VRLW V15, V29, V15 + + VADDUWM V8, V12, V8 + VADDUWM V9, V13, V9 + VADDUWM V10, V14, V10 + VADDUWM V11, V15, V11 + + VXOR V4, V8, V4 + VXOR V5, V9, V5 + VXOR V6, V10, V6 + VXOR V7, V11, V7 + + VRLW V4, V30, V4 + VRLW V5, V30, V5 + VRLW V6, V30, V6 + VRLW V7, V30, V7 + + VADDUWM V0, V5, V0 + VADDUWM V1, V6, V1 + VADDUWM V2, V7, V2 + VADDUWM V3, V4, V3 + + VXOR V15, V0, V15 + VXOR V12, V1, V12 + VXOR V13, V2, V13 + VXOR V14, V3, V14 + + VRLW V15, V27, V15 + VRLW V12, V27, V12 + VRLW V13, V27, V13 + VRLW V14, V27, V14 + + VADDUWM V10, V15, V10 + VADDUWM V11, V12, V11 + VADDUWM V8, V13, V8 + VADDUWM V9, V14, V9 + + VXOR V5, V10, V5 + VXOR V6, V11, V6 + VXOR V7, V8, V7 + VXOR V4, V9, V4 + + VRLW V5, V28, V5 + VRLW V6, V28, V6 + VRLW V7, V28, V7 + VRLW V4, V28, V4 + + VADDUWM V0, V5, V0 + VADDUWM V1, V6, V1 + VADDUWM V2, V7, V2 + VADDUWM V3, V4, V3 + + VXOR V15, V0, V15 + VXOR V12, V1, V12 + VXOR V13, V2, V13 + VXOR V14, V3, V14 + + VRLW V15, V29, V15 + VRLW V12, V29, V12 + VRLW V13, V29, V13 + VRLW V14, V29, V14 + + VADDUWM V10, V15, V10 + VADDUWM V11, V12, V11 + VADDUWM V8, V13, V8 + VADDUWM V9, V14, V9 + + VXOR V5, V10, V5 + VXOR V6, V11, V6 + VXOR V7, V8, V7 + VXOR V4, V9, V4 + + VRLW V5, V30, V5 + VRLW V6, V30, V6 + VRLW V7, V30, V7 + VRLW V4, V30, V4 + BC 16, LT, loop_vsx + + VADDUWM V12, V26, V12 + + WORD $0x13600F8C // VMRGEW V0, V1, V27 + WORD $0x13821F8C // VMRGEW V2, V3, V28 + + WORD $0x10000E8C // VMRGOW V0, V1, V0 + WORD $0x10421E8C // VMRGOW V2, V3, V2 + + WORD $0x13A42F8C // VMRGEW V4, V5, V29 + WORD $0x13C63F8C // VMRGEW V6, V7, V30 + + XXPERMDI VS32, VS34, $0, VS33 + XXPERMDI VS32, VS34, $3, VS35 + XXPERMDI VS59, VS60, $0, VS32 + XXPERMDI VS59, VS60, $3, VS34 + + WORD $0x10842E8C // VMRGOW V4, V5, V4 + WORD $0x10C63E8C // VMRGOW V6, V7, V6 + + WORD $0x13684F8C // VMRGEW V8, V9, V27 + WORD $0x138A5F8C // VMRGEW V10, V11, V28 + + XXPERMDI VS36, VS38, $0, VS37 + XXPERMDI VS36, VS38, $3, VS39 + XXPERMDI VS61, VS62, $0, VS36 + XXPERMDI VS61, VS62, $3, VS38 + + WORD $0x11084E8C // VMRGOW V8, V9, V8 + WORD $0x114A5E8C // VMRGOW V10, V11, V10 + + WORD $0x13AC6F8C // VMRGEW V12, V13, V29 + WORD $0x13CE7F8C // VMRGEW V14, V15, V30 + + XXPERMDI VS40, VS42, $0, VS41 + XXPERMDI VS40, VS42, $3, VS43 + XXPERMDI VS59, VS60, $0, VS40 + XXPERMDI VS59, VS60, $3, VS42 + + WORD $0x118C6E8C // VMRGOW V12, V13, V12 + WORD $0x11CE7E8C // VMRGOW V14, V15, V14 + + VSPLTISW $4, V27 + VADDUWM V26, V27, V26 + + XXPERMDI VS44, VS46, $0, VS45 + XXPERMDI VS44, VS46, $3, VS47 + XXPERMDI VS61, VS62, $0, VS44 + XXPERMDI VS61, VS62, $3, VS46 + + VADDUWM V0, V16, V0 + VADDUWM V4, V17, V4 + VADDUWM V8, V18, V8 + VADDUWM V12, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + // Bottom of loop + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + + VXOR V27, V0, V27 + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(R10) + ADD $64, OUT + BEQ done_vsx + + VADDUWM V1, V16, V0 + VADDUWM V5, V17, V4 + VADDUWM V9, V18, V8 + VADDUWM V13, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + VXOR V27, V0, V27 + + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(V10) + ADD $64, OUT + BEQ done_vsx + + VADDUWM V2, V16, V0 + VADDUWM V6, V17, V4 + VADDUWM V10, V18, V8 + VADDUWM V14, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + + VXOR V27, V0, V27 + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(R10) + ADD $64, OUT + BEQ done_vsx + + VADDUWM V3, V16, V0 + VADDUWM V7, V17, V4 + VADDUWM V11, V18, V8 + VADDUWM V15, V19, V12 + + CMPU LEN, $64 + BLT tail_vsx + + LXVW4X (INP)(R0), VS59 + LXVW4X (INP)(R8), VS60 + LXVW4X (INP)(R9), VS61 + LXVW4X (INP)(R10), VS62 + + VXOR V27, V0, V27 + VXOR V28, V4, V28 + VXOR V29, V8, V29 + VXOR V30, V12, V30 + + STXVW4X VS59, (OUT)(R0) + STXVW4X VS60, (OUT)(R8) + ADD $64, INP + STXVW4X VS61, (OUT)(R9) + ADD $-64, LEN + STXVW4X VS62, (OUT)(R10) + ADD $64, OUT + + MOVD $10, R14 + MOVD R14, CTR + BNE loop_outer_vsx + +done_vsx: + // Increment counter by number of 64 byte blocks + MOVD (CNT), R14 + ADD BLOCKS, R14 + MOVD R14, (CNT) + RET + +tail_vsx: + ADD $32, R1, R11 + MOVD LEN, CTR + + // Save values on stack to copy from + STXVW4X VS32, (R11)(R0) + STXVW4X VS36, (R11)(R8) + STXVW4X VS40, (R11)(R9) + STXVW4X VS44, (R11)(R10) + ADD $-1, R11, R12 + ADD $-1, INP + ADD $-1, OUT + +looptail_vsx: + // Copying the result to OUT + // in bytes. + MOVBZU 1(R12), KEY + MOVBZU 1(INP), TMP + XOR KEY, TMP, KEY + MOVBU KEY, 1(OUT) + BC 16, LT, looptail_vsx + + // Clear the stack values + STXVW4X VS48, (R11)(R0) + STXVW4X VS48, (R11)(R8) + STXVW4X VS48, (R11)(R9) + STXVW4X VS48, (R11)(R10) + BR done_vsx diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go new file mode 100644 index 00000000000..cd55f45a333 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !gccgo,!appengine + +package chacha20 + +import "golang.org/x/sys/cpu" + +var haveAsm = cpu.S390X.HasVX + +const bufSize = 256 + +// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only +// be called when the vector facility is available. Implementation in asm_s390x.s. +//go:noescape +func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) + +func (c *Cipher) xorKeyStreamBlocks(dst, src []byte) { + if cpu.S390X.HasVX { + xorKeyStreamVX(dst, src, &c.key, &c.nonce, &c.counter) + } else { + c.xorKeyStreamBlocksGeneric(dst, src) + } +} diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s new file mode 100644 index 00000000000..de52a2ea8d1 --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s @@ -0,0 +1,224 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !gccgo,!appengine + +#include "go_asm.h" +#include "textflag.h" + +// This is an implementation of the ChaCha20 encryption algorithm as +// specified in RFC 7539. It uses vector instructions to compute +// 4 keystream blocks in parallel (256 bytes) which are then XORed +// with the bytes in the input slice. + +GLOBL ·constants<>(SB), RODATA|NOPTR, $32 +// BSWAP: swap bytes in each 4-byte element +DATA ·constants<>+0x00(SB)/4, $0x03020100 +DATA ·constants<>+0x04(SB)/4, $0x07060504 +DATA ·constants<>+0x08(SB)/4, $0x0b0a0908 +DATA ·constants<>+0x0c(SB)/4, $0x0f0e0d0c +// J0: [j0, j1, j2, j3] +DATA ·constants<>+0x10(SB)/4, $0x61707865 +DATA ·constants<>+0x14(SB)/4, $0x3320646e +DATA ·constants<>+0x18(SB)/4, $0x79622d32 +DATA ·constants<>+0x1c(SB)/4, $0x6b206574 + +#define BSWAP V5 +#define J0 V6 +#define KEY0 V7 +#define KEY1 V8 +#define NONCE V9 +#define CTR V10 +#define M0 V11 +#define M1 V12 +#define M2 V13 +#define M3 V14 +#define INC V15 +#define X0 V16 +#define X1 V17 +#define X2 V18 +#define X3 V19 +#define X4 V20 +#define X5 V21 +#define X6 V22 +#define X7 V23 +#define X8 V24 +#define X9 V25 +#define X10 V26 +#define X11 V27 +#define X12 V28 +#define X13 V29 +#define X14 V30 +#define X15 V31 + +#define NUM_ROUNDS 20 + +#define ROUND4(a0, a1, a2, a3, b0, b1, b2, b3, c0, c1, c2, c3, d0, d1, d2, d3) \ + VAF a1, a0, a0 \ + VAF b1, b0, b0 \ + VAF c1, c0, c0 \ + VAF d1, d0, d0 \ + VX a0, a2, a2 \ + VX b0, b2, b2 \ + VX c0, c2, c2 \ + VX d0, d2, d2 \ + VERLLF $16, a2, a2 \ + VERLLF $16, b2, b2 \ + VERLLF $16, c2, c2 \ + VERLLF $16, d2, d2 \ + VAF a2, a3, a3 \ + VAF b2, b3, b3 \ + VAF c2, c3, c3 \ + VAF d2, d3, d3 \ + VX a3, a1, a1 \ + VX b3, b1, b1 \ + VX c3, c1, c1 \ + VX d3, d1, d1 \ + VERLLF $12, a1, a1 \ + VERLLF $12, b1, b1 \ + VERLLF $12, c1, c1 \ + VERLLF $12, d1, d1 \ + VAF a1, a0, a0 \ + VAF b1, b0, b0 \ + VAF c1, c0, c0 \ + VAF d1, d0, d0 \ + VX a0, a2, a2 \ + VX b0, b2, b2 \ + VX c0, c2, c2 \ + VX d0, d2, d2 \ + VERLLF $8, a2, a2 \ + VERLLF $8, b2, b2 \ + VERLLF $8, c2, c2 \ + VERLLF $8, d2, d2 \ + VAF a2, a3, a3 \ + VAF b2, b3, b3 \ + VAF c2, c3, c3 \ + VAF d2, d3, d3 \ + VX a3, a1, a1 \ + VX b3, b1, b1 \ + VX c3, c1, c1 \ + VX d3, d1, d1 \ + VERLLF $7, a1, a1 \ + VERLLF $7, b1, b1 \ + VERLLF $7, c1, c1 \ + VERLLF $7, d1, d1 + +#define PERMUTE(mask, v0, v1, v2, v3) \ + VPERM v0, v0, mask, v0 \ + VPERM v1, v1, mask, v1 \ + VPERM v2, v2, mask, v2 \ + VPERM v3, v3, mask, v3 + +#define ADDV(x, v0, v1, v2, v3) \ + VAF x, v0, v0 \ + VAF x, v1, v1 \ + VAF x, v2, v2 \ + VAF x, v3, v3 + +#define XORV(off, dst, src, v0, v1, v2, v3) \ + VLM off(src), M0, M3 \ + PERMUTE(BSWAP, v0, v1, v2, v3) \ + VX v0, M0, M0 \ + VX v1, M1, M1 \ + VX v2, M2, M2 \ + VX v3, M3, M3 \ + VSTM M0, M3, off(dst) + +#define SHUFFLE(a, b, c, d, t, u, v, w) \ + VMRHF a, c, t \ // t = {a[0], c[0], a[1], c[1]} + VMRHF b, d, u \ // u = {b[0], d[0], b[1], d[1]} + VMRLF a, c, v \ // v = {a[2], c[2], a[3], c[3]} + VMRLF b, d, w \ // w = {b[2], d[2], b[3], d[3]} + VMRHF t, u, a \ // a = {a[0], b[0], c[0], d[0]} + VMRLF t, u, b \ // b = {a[1], b[1], c[1], d[1]} + VMRHF v, w, c \ // c = {a[2], b[2], c[2], d[2]} + VMRLF v, w, d // d = {a[3], b[3], c[3], d[3]} + +// func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32) +TEXT ·xorKeyStreamVX(SB), NOSPLIT, $0 + MOVD $·constants<>(SB), R1 + MOVD dst+0(FP), R2 // R2=&dst[0] + LMG src+24(FP), R3, R4 // R3=&src[0] R4=len(src) + MOVD key+48(FP), R5 // R5=key + MOVD nonce+56(FP), R6 // R6=nonce + MOVD counter+64(FP), R7 // R7=counter + + // load BSWAP and J0 + VLM (R1), BSWAP, J0 + + // setup + MOVD $95, R0 + VLM (R5), KEY0, KEY1 + VLL R0, (R6), NONCE + VZERO M0 + VLEIB $7, $32, M0 + VSRLB M0, NONCE, NONCE + + // initialize counter values + VLREPF (R7), CTR + VZERO INC + VLEIF $1, $1, INC + VLEIF $2, $2, INC + VLEIF $3, $3, INC + VAF INC, CTR, CTR + VREPIF $4, INC + +chacha: + VREPF $0, J0, X0 + VREPF $1, J0, X1 + VREPF $2, J0, X2 + VREPF $3, J0, X3 + VREPF $0, KEY0, X4 + VREPF $1, KEY0, X5 + VREPF $2, KEY0, X6 + VREPF $3, KEY0, X7 + VREPF $0, KEY1, X8 + VREPF $1, KEY1, X9 + VREPF $2, KEY1, X10 + VREPF $3, KEY1, X11 + VLR CTR, X12 + VREPF $1, NONCE, X13 + VREPF $2, NONCE, X14 + VREPF $3, NONCE, X15 + + MOVD $(NUM_ROUNDS/2), R1 + +loop: + ROUND4(X0, X4, X12, X8, X1, X5, X13, X9, X2, X6, X14, X10, X3, X7, X15, X11) + ROUND4(X0, X5, X15, X10, X1, X6, X12, X11, X2, X7, X13, X8, X3, X4, X14, X9) + + ADD $-1, R1 + BNE loop + + // decrement length + ADD $-256, R4 + + // rearrange vectors + SHUFFLE(X0, X1, X2, X3, M0, M1, M2, M3) + ADDV(J0, X0, X1, X2, X3) + SHUFFLE(X4, X5, X6, X7, M0, M1, M2, M3) + ADDV(KEY0, X4, X5, X6, X7) + SHUFFLE(X8, X9, X10, X11, M0, M1, M2, M3) + ADDV(KEY1, X8, X9, X10, X11) + VAF CTR, X12, X12 + SHUFFLE(X12, X13, X14, X15, M0, M1, M2, M3) + ADDV(NONCE, X12, X13, X14, X15) + + // increment counters + VAF INC, CTR, CTR + + // xor keystream with plaintext + XORV(0*64, R2, R3, X0, X4, X8, X12) + XORV(1*64, R2, R3, X1, X5, X9, X13) + XORV(2*64, R2, R3, X2, X6, X10, X14) + XORV(3*64, R2, R3, X3, X7, X11, X15) + + // increment pointers + MOVD $256(R2), R2 + MOVD $256(R3), R3 + + CMPBNE R4, $0, chacha + + VSTEF $0, CTR, (R7) + RET diff --git a/vendor/golang.org/x/crypto/chacha20/xor.go b/vendor/golang.org/x/crypto/chacha20/xor.go new file mode 100644 index 00000000000..0110c9865af --- /dev/null +++ b/vendor/golang.org/x/crypto/chacha20/xor.go @@ -0,0 +1,41 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found src the LICENSE file. + +package chacha20 + +import "runtime" + +// Platforms that have fast unaligned 32-bit little endian accesses. +const unaligned = runtime.GOARCH == "386" || + runtime.GOARCH == "amd64" || + runtime.GOARCH == "arm64" || + runtime.GOARCH == "ppc64le" || + runtime.GOARCH == "s390x" + +// xor reads a little endian uint32 from src, XORs it with u and +// places the result in little endian byte order in dst. +func xor(dst, src []byte, u uint32) { + _, _ = src[3], dst[3] // eliminate bounds checks + if unaligned { + // The compiler should optimize this code into + // 32-bit unaligned little endian loads and stores. + // TODO: delete once the compiler does a reliably + // good job with the generic code below. + // See issue #25111 for more details. + v := uint32(src[0]) + v |= uint32(src[1]) << 8 + v |= uint32(src[2]) << 16 + v |= uint32(src[3]) << 24 + v ^= u + dst[0] = byte(v) + dst[1] = byte(v >> 8) + dst[2] = byte(v >> 16) + dst[3] = byte(v >> 24) + } else { + dst[0] = src[0] ^ byte(u) + dst[1] = src[1] ^ byte(u>>8) + dst[2] = src[2] ^ byte(u>>16) + dst[3] = src[3] ^ byte(u>>24) + } +} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519.go b/vendor/golang.org/x/crypto/curve25519/curve25519.go new file mode 100644 index 00000000000..4b9a655d1b5 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/curve25519.go @@ -0,0 +1,95 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package curve25519 provides an implementation of the X25519 function, which +// performs scalar multiplication on the elliptic curve known as Curve25519. +// See RFC 7748. +package curve25519 // import "golang.org/x/crypto/curve25519" + +import ( + "crypto/subtle" + "fmt" +) + +// ScalarMult sets dst to the product scalar * point. +// +// Deprecated: when provided a low-order point, ScalarMult will set dst to all +// zeroes, irrespective of the scalar. Instead, use the X25519 function, which +// will return an error. +func ScalarMult(dst, scalar, point *[32]byte) { + scalarMult(dst, scalar, point) +} + +// ScalarBaseMult sets dst to the product scalar * base where base is the +// standard generator. +// +// It is recommended to use the X25519 function with Basepoint instead, as +// copying into fixed size arrays can lead to unexpected bugs. +func ScalarBaseMult(dst, scalar *[32]byte) { + ScalarMult(dst, scalar, &basePoint) +} + +const ( + // ScalarSize is the size of the scalar input to X25519. + ScalarSize = 32 + // PointSize is the size of the point input to X25519. + PointSize = 32 +) + +// Basepoint is the canonical Curve25519 generator. +var Basepoint []byte + +var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +func init() { Basepoint = basePoint[:] } + +func checkBasepoint() { + if subtle.ConstantTimeCompare(Basepoint, []byte{ + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }) != 1 { + panic("curve25519: global Basepoint value was modified") + } +} + +// X25519 returns the result of the scalar multiplication (scalar * point), +// according to RFC 7748, Section 5. scalar, point and the return value are +// slices of 32 bytes. +// +// scalar can be generated at random, for example with crypto/rand. point should +// be either Basepoint or the output of another X25519 call. +// +// If point is Basepoint (but not if it's a different slice with the same +// contents) a precomputed implementation might be used for performance. +func X25519(scalar, point []byte) ([]byte, error) { + // Outline the body of function, to let the allocation be inlined in the + // caller, and possibly avoid escaping to the heap. + var dst [32]byte + return x25519(&dst, scalar, point) +} + +func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) { + var in [32]byte + if l := len(scalar); l != 32 { + return nil, fmt.Errorf("bad scalar length: %d, expected %d", l, 32) + } + if l := len(point); l != 32 { + return nil, fmt.Errorf("bad point length: %d, expected %d", l, 32) + } + copy(in[:], scalar) + if &point[0] == &Basepoint[0] { + checkBasepoint() + ScalarBaseMult(dst, &in) + } else { + var base, zero [32]byte + copy(base[:], point) + ScalarMult(dst, &in, &base) + if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 { + return nil, fmt.Errorf("bad input point: low order point") + } + } + return dst[:], nil +} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go new file mode 100644 index 00000000000..5120b779b9b --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.go @@ -0,0 +1,240 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine,!purego + +package curve25519 + +// These functions are implemented in the .s files. The names of the functions +// in the rest of the file are also taken from the SUPERCOP sources to help +// people following along. + +//go:noescape + +func cswap(inout *[5]uint64, v uint64) + +//go:noescape + +func ladderstep(inout *[5][5]uint64) + +//go:noescape + +func freeze(inout *[5]uint64) + +//go:noescape + +func mul(dest, a, b *[5]uint64) + +//go:noescape + +func square(out, in *[5]uint64) + +// mladder uses a Montgomery ladder to calculate (xr/zr) *= s. +func mladder(xr, zr *[5]uint64, s *[32]byte) { + var work [5][5]uint64 + + work[0] = *xr + setint(&work[1], 1) + setint(&work[2], 0) + work[3] = *xr + setint(&work[4], 1) + + j := uint(6) + var prevbit byte + + for i := 31; i >= 0; i-- { + for j < 8 { + bit := ((*s)[i] >> j) & 1 + swap := bit ^ prevbit + prevbit = bit + cswap(&work[1], uint64(swap)) + ladderstep(&work) + j-- + } + j = 7 + } + + *xr = work[1] + *zr = work[2] +} + +func scalarMult(out, in, base *[32]byte) { + var e [32]byte + copy(e[:], (*in)[:]) + e[0] &= 248 + e[31] &= 127 + e[31] |= 64 + + var t, z [5]uint64 + unpack(&t, base) + mladder(&t, &z, &e) + invert(&z, &z) + mul(&t, &t, &z) + pack(out, &t) +} + +func setint(r *[5]uint64, v uint64) { + r[0] = v + r[1] = 0 + r[2] = 0 + r[3] = 0 + r[4] = 0 +} + +// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian +// order. +func unpack(r *[5]uint64, x *[32]byte) { + r[0] = uint64(x[0]) | + uint64(x[1])<<8 | + uint64(x[2])<<16 | + uint64(x[3])<<24 | + uint64(x[4])<<32 | + uint64(x[5])<<40 | + uint64(x[6]&7)<<48 + + r[1] = uint64(x[6])>>3 | + uint64(x[7])<<5 | + uint64(x[8])<<13 | + uint64(x[9])<<21 | + uint64(x[10])<<29 | + uint64(x[11])<<37 | + uint64(x[12]&63)<<45 + + r[2] = uint64(x[12])>>6 | + uint64(x[13])<<2 | + uint64(x[14])<<10 | + uint64(x[15])<<18 | + uint64(x[16])<<26 | + uint64(x[17])<<34 | + uint64(x[18])<<42 | + uint64(x[19]&1)<<50 + + r[3] = uint64(x[19])>>1 | + uint64(x[20])<<7 | + uint64(x[21])<<15 | + uint64(x[22])<<23 | + uint64(x[23])<<31 | + uint64(x[24])<<39 | + uint64(x[25]&15)<<47 + + r[4] = uint64(x[25])>>4 | + uint64(x[26])<<4 | + uint64(x[27])<<12 | + uint64(x[28])<<20 | + uint64(x[29])<<28 | + uint64(x[30])<<36 | + uint64(x[31]&127)<<44 +} + +// pack sets out = x where out is the usual, little-endian form of the 5, +// 51-bit limbs in x. +func pack(out *[32]byte, x *[5]uint64) { + t := *x + freeze(&t) + + out[0] = byte(t[0]) + out[1] = byte(t[0] >> 8) + out[2] = byte(t[0] >> 16) + out[3] = byte(t[0] >> 24) + out[4] = byte(t[0] >> 32) + out[5] = byte(t[0] >> 40) + out[6] = byte(t[0] >> 48) + + out[6] ^= byte(t[1]<<3) & 0xf8 + out[7] = byte(t[1] >> 5) + out[8] = byte(t[1] >> 13) + out[9] = byte(t[1] >> 21) + out[10] = byte(t[1] >> 29) + out[11] = byte(t[1] >> 37) + out[12] = byte(t[1] >> 45) + + out[12] ^= byte(t[2]<<6) & 0xc0 + out[13] = byte(t[2] >> 2) + out[14] = byte(t[2] >> 10) + out[15] = byte(t[2] >> 18) + out[16] = byte(t[2] >> 26) + out[17] = byte(t[2] >> 34) + out[18] = byte(t[2] >> 42) + out[19] = byte(t[2] >> 50) + + out[19] ^= byte(t[3]<<1) & 0xfe + out[20] = byte(t[3] >> 7) + out[21] = byte(t[3] >> 15) + out[22] = byte(t[3] >> 23) + out[23] = byte(t[3] >> 31) + out[24] = byte(t[3] >> 39) + out[25] = byte(t[3] >> 47) + + out[25] ^= byte(t[4]<<4) & 0xf0 + out[26] = byte(t[4] >> 4) + out[27] = byte(t[4] >> 12) + out[28] = byte(t[4] >> 20) + out[29] = byte(t[4] >> 28) + out[30] = byte(t[4] >> 36) + out[31] = byte(t[4] >> 44) +} + +// invert calculates r = x^-1 mod p using Fermat's little theorem. +func invert(r *[5]uint64, x *[5]uint64) { + var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 + + square(&z2, x) /* 2 */ + square(&t, &z2) /* 4 */ + square(&t, &t) /* 8 */ + mul(&z9, &t, x) /* 9 */ + mul(&z11, &z9, &z2) /* 11 */ + square(&t, &z11) /* 22 */ + mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ + + square(&t, &z2_5_0) /* 2^6 - 2^1 */ + for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ + square(&t, &t) + } + mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ + + square(&t, &z2_10_0) /* 2^11 - 2^1 */ + for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ + square(&t, &t) + } + mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ + + square(&t, &z2_20_0) /* 2^21 - 2^1 */ + for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ + square(&t, &t) + } + mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ + + square(&t, &t) /* 2^41 - 2^1 */ + for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ + square(&t, &t) + } + mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ + + square(&t, &z2_50_0) /* 2^51 - 2^1 */ + for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ + square(&t, &t) + } + mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ + + square(&t, &z2_100_0) /* 2^101 - 2^1 */ + for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ + square(&t, &t) + } + mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ + + square(&t, &t) /* 2^201 - 2^1 */ + for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ + square(&t, &t) + } + mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ + + square(&t, &t) /* 2^251 - 2^1 */ + square(&t, &t) /* 2^252 - 2^2 */ + square(&t, &t) /* 2^253 - 2^3 */ + + square(&t, &t) /* 2^254 - 2^4 */ + + square(&t, &t) /* 2^255 - 2^5 */ + mul(r, &t, &z11) /* 2^255 - 21 */ +} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s new file mode 100644 index 00000000000..0250c888592 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/curve25519_amd64.s @@ -0,0 +1,1793 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code was translated into a form compatible with 6a from the public +// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html + +// +build amd64,!gccgo,!appengine,!purego + +#define REDMASK51 0x0007FFFFFFFFFFFF + +// These constants cannot be encoded in non-MOVQ immediates. +// We access them directly from memory instead. + +DATA ·_121666_213(SB)/8, $996687872 +GLOBL ·_121666_213(SB), 8, $8 + +DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA +GLOBL ·_2P0(SB), 8, $8 + +DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE +GLOBL ·_2P1234(SB), 8, $8 + +// func freeze(inout *[5]uint64) +TEXT ·freeze(SB),7,$0-8 + MOVQ inout+0(FP), DI + + MOVQ 0(DI),SI + MOVQ 8(DI),DX + MOVQ 16(DI),CX + MOVQ 24(DI),R8 + MOVQ 32(DI),R9 + MOVQ $REDMASK51,AX + MOVQ AX,R10 + SUBQ $18,R10 + MOVQ $3,R11 +REDUCELOOP: + MOVQ SI,R12 + SHRQ $51,R12 + ANDQ AX,SI + ADDQ R12,DX + MOVQ DX,R12 + SHRQ $51,R12 + ANDQ AX,DX + ADDQ R12,CX + MOVQ CX,R12 + SHRQ $51,R12 + ANDQ AX,CX + ADDQ R12,R8 + MOVQ R8,R12 + SHRQ $51,R12 + ANDQ AX,R8 + ADDQ R12,R9 + MOVQ R9,R12 + SHRQ $51,R12 + ANDQ AX,R9 + IMUL3Q $19,R12,R12 + ADDQ R12,SI + SUBQ $1,R11 + JA REDUCELOOP + MOVQ $1,R12 + CMPQ R10,SI + CMOVQLT R11,R12 + CMPQ AX,DX + CMOVQNE R11,R12 + CMPQ AX,CX + CMOVQNE R11,R12 + CMPQ AX,R8 + CMOVQNE R11,R12 + CMPQ AX,R9 + CMOVQNE R11,R12 + NEGQ R12 + ANDQ R12,AX + ANDQ R12,R10 + SUBQ R10,SI + SUBQ AX,DX + SUBQ AX,CX + SUBQ AX,R8 + SUBQ AX,R9 + MOVQ SI,0(DI) + MOVQ DX,8(DI) + MOVQ CX,16(DI) + MOVQ R8,24(DI) + MOVQ R9,32(DI) + RET + +// func ladderstep(inout *[5][5]uint64) +TEXT ·ladderstep(SB),0,$296-8 + MOVQ inout+0(FP),DI + + MOVQ 40(DI),SI + MOVQ 48(DI),DX + MOVQ 56(DI),CX + MOVQ 64(DI),R8 + MOVQ 72(DI),R9 + MOVQ SI,AX + MOVQ DX,R10 + MOVQ CX,R11 + MOVQ R8,R12 + MOVQ R9,R13 + ADDQ ·_2P0(SB),AX + ADDQ ·_2P1234(SB),R10 + ADDQ ·_2P1234(SB),R11 + ADDQ ·_2P1234(SB),R12 + ADDQ ·_2P1234(SB),R13 + ADDQ 80(DI),SI + ADDQ 88(DI),DX + ADDQ 96(DI),CX + ADDQ 104(DI),R8 + ADDQ 112(DI),R9 + SUBQ 80(DI),AX + SUBQ 88(DI),R10 + SUBQ 96(DI),R11 + SUBQ 104(DI),R12 + SUBQ 112(DI),R13 + MOVQ SI,0(SP) + MOVQ DX,8(SP) + MOVQ CX,16(SP) + MOVQ R8,24(SP) + MOVQ R9,32(SP) + MOVQ AX,40(SP) + MOVQ R10,48(SP) + MOVQ R11,56(SP) + MOVQ R12,64(SP) + MOVQ R13,72(SP) + MOVQ 40(SP),AX + MULQ 40(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 48(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 56(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 64(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 40(SP),AX + SHLQ $1,AX + MULQ 72(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 48(SP),AX + MULQ 48(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 48(SP),AX + SHLQ $1,AX + MULQ 56(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 48(SP),AX + SHLQ $1,AX + MULQ 64(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 48(SP),DX + IMUL3Q $38,DX,AX + MULQ 72(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 56(SP),AX + MULQ 56(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 56(SP),DX + IMUL3Q $38,DX,AX + MULQ 64(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 56(SP),DX + IMUL3Q $38,DX,AX + MULQ 72(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 64(SP),DX + IMUL3Q $19,DX,AX + MULQ 64(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 64(SP),DX + IMUL3Q $38,DX,AX + MULQ 72(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 72(SP),DX + IMUL3Q $19,DX,AX + MULQ 72(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,80(SP) + MOVQ R8,88(SP) + MOVQ R9,96(SP) + MOVQ AX,104(SP) + MOVQ R10,112(SP) + MOVQ 0(SP),AX + MULQ 0(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 8(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 16(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 24(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 0(SP),AX + SHLQ $1,AX + MULQ 32(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 8(SP),AX + MULQ 8(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + SHLQ $1,AX + MULQ 16(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 8(SP),AX + SHLQ $1,AX + MULQ 24(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),DX + IMUL3Q $38,DX,AX + MULQ 32(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 16(SP),AX + MULQ 16(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 16(SP),DX + IMUL3Q $38,DX,AX + MULQ 24(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 16(SP),DX + IMUL3Q $38,DX,AX + MULQ 32(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 24(SP),DX + IMUL3Q $19,DX,AX + MULQ 24(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 24(SP),DX + IMUL3Q $38,DX,AX + MULQ 32(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 32(SP),DX + IMUL3Q $19,DX,AX + MULQ 32(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,120(SP) + MOVQ R8,128(SP) + MOVQ R9,136(SP) + MOVQ AX,144(SP) + MOVQ R10,152(SP) + MOVQ SI,SI + MOVQ R8,DX + MOVQ R9,CX + MOVQ AX,R8 + MOVQ R10,R9 + ADDQ ·_2P0(SB),SI + ADDQ ·_2P1234(SB),DX + ADDQ ·_2P1234(SB),CX + ADDQ ·_2P1234(SB),R8 + ADDQ ·_2P1234(SB),R9 + SUBQ 80(SP),SI + SUBQ 88(SP),DX + SUBQ 96(SP),CX + SUBQ 104(SP),R8 + SUBQ 112(SP),R9 + MOVQ SI,160(SP) + MOVQ DX,168(SP) + MOVQ CX,176(SP) + MOVQ R8,184(SP) + MOVQ R9,192(SP) + MOVQ 120(DI),SI + MOVQ 128(DI),DX + MOVQ 136(DI),CX + MOVQ 144(DI),R8 + MOVQ 152(DI),R9 + MOVQ SI,AX + MOVQ DX,R10 + MOVQ CX,R11 + MOVQ R8,R12 + MOVQ R9,R13 + ADDQ ·_2P0(SB),AX + ADDQ ·_2P1234(SB),R10 + ADDQ ·_2P1234(SB),R11 + ADDQ ·_2P1234(SB),R12 + ADDQ ·_2P1234(SB),R13 + ADDQ 160(DI),SI + ADDQ 168(DI),DX + ADDQ 176(DI),CX + ADDQ 184(DI),R8 + ADDQ 192(DI),R9 + SUBQ 160(DI),AX + SUBQ 168(DI),R10 + SUBQ 176(DI),R11 + SUBQ 184(DI),R12 + SUBQ 192(DI),R13 + MOVQ SI,200(SP) + MOVQ DX,208(SP) + MOVQ CX,216(SP) + MOVQ R8,224(SP) + MOVQ R9,232(SP) + MOVQ AX,240(SP) + MOVQ R10,248(SP) + MOVQ R11,256(SP) + MOVQ R12,264(SP) + MOVQ R13,272(SP) + MOVQ 224(SP),SI + IMUL3Q $19,SI,AX + MOVQ AX,280(SP) + MULQ 56(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 232(SP),DX + IMUL3Q $19,DX,AX + MOVQ AX,288(SP) + MULQ 48(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 200(SP),AX + MULQ 40(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 200(SP),AX + MULQ 48(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 200(SP),AX + MULQ 56(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 200(SP),AX + MULQ 64(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 200(SP),AX + MULQ 72(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 208(SP),AX + MULQ 40(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 208(SP),AX + MULQ 48(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 208(SP),AX + MULQ 56(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 208(SP),AX + MULQ 64(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 208(SP),DX + IMUL3Q $19,DX,AX + MULQ 72(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 216(SP),AX + MULQ 40(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 216(SP),AX + MULQ 48(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 216(SP),AX + MULQ 56(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 216(SP),DX + IMUL3Q $19,DX,AX + MULQ 64(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 216(SP),DX + IMUL3Q $19,DX,AX + MULQ 72(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 224(SP),AX + MULQ 40(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 224(SP),AX + MULQ 48(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 280(SP),AX + MULQ 64(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 280(SP),AX + MULQ 72(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 232(SP),AX + MULQ 40(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 288(SP),AX + MULQ 56(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 288(SP),AX + MULQ 64(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 288(SP),AX + MULQ 72(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,40(SP) + MOVQ R8,48(SP) + MOVQ R9,56(SP) + MOVQ AX,64(SP) + MOVQ R10,72(SP) + MOVQ 264(SP),SI + IMUL3Q $19,SI,AX + MOVQ AX,200(SP) + MULQ 16(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 272(SP),DX + IMUL3Q $19,DX,AX + MOVQ AX,208(SP) + MULQ 8(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 240(SP),AX + MULQ 0(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 240(SP),AX + MULQ 8(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 240(SP),AX + MULQ 16(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 240(SP),AX + MULQ 24(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 240(SP),AX + MULQ 32(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 248(SP),AX + MULQ 0(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 248(SP),AX + MULQ 8(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 248(SP),AX + MULQ 16(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 248(SP),AX + MULQ 24(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 248(SP),DX + IMUL3Q $19,DX,AX + MULQ 32(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 256(SP),AX + MULQ 0(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 256(SP),AX + MULQ 8(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 256(SP),AX + MULQ 16(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 256(SP),DX + IMUL3Q $19,DX,AX + MULQ 24(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 256(SP),DX + IMUL3Q $19,DX,AX + MULQ 32(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 264(SP),AX + MULQ 0(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 264(SP),AX + MULQ 8(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 200(SP),AX + MULQ 24(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 200(SP),AX + MULQ 32(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 272(SP),AX + MULQ 0(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 208(SP),AX + MULQ 16(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 208(SP),AX + MULQ 24(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 208(SP),AX + MULQ 32(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,DX + MOVQ R8,CX + MOVQ R9,R11 + MOVQ AX,R12 + MOVQ R10,R13 + ADDQ ·_2P0(SB),DX + ADDQ ·_2P1234(SB),CX + ADDQ ·_2P1234(SB),R11 + ADDQ ·_2P1234(SB),R12 + ADDQ ·_2P1234(SB),R13 + ADDQ 40(SP),SI + ADDQ 48(SP),R8 + ADDQ 56(SP),R9 + ADDQ 64(SP),AX + ADDQ 72(SP),R10 + SUBQ 40(SP),DX + SUBQ 48(SP),CX + SUBQ 56(SP),R11 + SUBQ 64(SP),R12 + SUBQ 72(SP),R13 + MOVQ SI,120(DI) + MOVQ R8,128(DI) + MOVQ R9,136(DI) + MOVQ AX,144(DI) + MOVQ R10,152(DI) + MOVQ DX,160(DI) + MOVQ CX,168(DI) + MOVQ R11,176(DI) + MOVQ R12,184(DI) + MOVQ R13,192(DI) + MOVQ 120(DI),AX + MULQ 120(DI) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 128(DI) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 136(DI) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 144(DI) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 120(DI),AX + SHLQ $1,AX + MULQ 152(DI) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 128(DI),AX + MULQ 128(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 128(DI),AX + SHLQ $1,AX + MULQ 136(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 128(DI),AX + SHLQ $1,AX + MULQ 144(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 128(DI),DX + IMUL3Q $38,DX,AX + MULQ 152(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(DI),AX + MULQ 136(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 136(DI),DX + IMUL3Q $38,DX,AX + MULQ 144(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(DI),DX + IMUL3Q $38,DX,AX + MULQ 152(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 144(DI),DX + IMUL3Q $19,DX,AX + MULQ 144(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 144(DI),DX + IMUL3Q $38,DX,AX + MULQ 152(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 152(DI),DX + IMUL3Q $19,DX,AX + MULQ 152(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,120(DI) + MOVQ R8,128(DI) + MOVQ R9,136(DI) + MOVQ AX,144(DI) + MOVQ R10,152(DI) + MOVQ 160(DI),AX + MULQ 160(DI) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 168(DI) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 176(DI) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 184(DI) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 160(DI),AX + SHLQ $1,AX + MULQ 192(DI) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 168(DI),AX + MULQ 168(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 168(DI),AX + SHLQ $1,AX + MULQ 176(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 168(DI),AX + SHLQ $1,AX + MULQ 184(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 168(DI),DX + IMUL3Q $38,DX,AX + MULQ 192(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),AX + MULQ 176(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 176(DI),DX + IMUL3Q $38,DX,AX + MULQ 184(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),DX + IMUL3Q $38,DX,AX + MULQ 192(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 184(DI),DX + IMUL3Q $19,DX,AX + MULQ 184(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 184(DI),DX + IMUL3Q $38,DX,AX + MULQ 192(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 192(DI),DX + IMUL3Q $19,DX,AX + MULQ 192(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + ANDQ DX,SI + MOVQ CX,R8 + SHRQ $51,CX + ADDQ R10,CX + ANDQ DX,R8 + MOVQ CX,R9 + SHRQ $51,CX + ADDQ R12,CX + ANDQ DX,R9 + MOVQ CX,AX + SHRQ $51,CX + ADDQ R14,CX + ANDQ DX,AX + MOVQ CX,R10 + SHRQ $51,CX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,160(DI) + MOVQ R8,168(DI) + MOVQ R9,176(DI) + MOVQ AX,184(DI) + MOVQ R10,192(DI) + MOVQ 184(DI),SI + IMUL3Q $19,SI,AX + MOVQ AX,0(SP) + MULQ 16(DI) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 192(DI),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 8(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 160(DI),AX + MULQ 0(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 160(DI),AX + MULQ 8(DI) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 160(DI),AX + MULQ 16(DI) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 160(DI),AX + MULQ 24(DI) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 160(DI),AX + MULQ 32(DI) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 168(DI),AX + MULQ 0(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 168(DI),AX + MULQ 8(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 168(DI),AX + MULQ 16(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 168(DI),AX + MULQ 24(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 168(DI),DX + IMUL3Q $19,DX,AX + MULQ 32(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),AX + MULQ 0(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 176(DI),AX + MULQ 8(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 176(DI),AX + MULQ 16(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 176(DI),DX + IMUL3Q $19,DX,AX + MULQ 24(DI) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 176(DI),DX + IMUL3Q $19,DX,AX + MULQ 32(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 184(DI),AX + MULQ 0(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 184(DI),AX + MULQ 8(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 0(SP),AX + MULQ 24(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SP),AX + MULQ 32(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 192(DI),AX + MULQ 0(DI) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),AX + MULQ 16(DI) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 8(SP),AX + MULQ 24(DI) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 32(DI) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,160(DI) + MOVQ R8,168(DI) + MOVQ R9,176(DI) + MOVQ AX,184(DI) + MOVQ R10,192(DI) + MOVQ 144(SP),SI + IMUL3Q $19,SI,AX + MOVQ AX,0(SP) + MULQ 96(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 152(SP),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 88(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 120(SP),AX + MULQ 80(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 120(SP),AX + MULQ 88(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 120(SP),AX + MULQ 96(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 120(SP),AX + MULQ 104(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 120(SP),AX + MULQ 112(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 128(SP),AX + MULQ 80(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 128(SP),AX + MULQ 88(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 128(SP),AX + MULQ 96(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 128(SP),AX + MULQ 104(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 128(SP),DX + IMUL3Q $19,DX,AX + MULQ 112(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(SP),AX + MULQ 80(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 136(SP),AX + MULQ 88(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 136(SP),AX + MULQ 96(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 136(SP),DX + IMUL3Q $19,DX,AX + MULQ 104(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 136(SP),DX + IMUL3Q $19,DX,AX + MULQ 112(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 144(SP),AX + MULQ 80(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 144(SP),AX + MULQ 88(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 0(SP),AX + MULQ 104(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SP),AX + MULQ 112(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 152(SP),AX + MULQ 80(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),AX + MULQ 96(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 8(SP),AX + MULQ 104(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 112(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,40(DI) + MOVQ R8,48(DI) + MOVQ R9,56(DI) + MOVQ AX,64(DI) + MOVQ R10,72(DI) + MOVQ 160(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + MOVQ AX,SI + MOVQ DX,CX + MOVQ 168(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,CX + MOVQ DX,R8 + MOVQ 176(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,R8 + MOVQ DX,R9 + MOVQ 184(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,R9 + MOVQ DX,R10 + MOVQ 192(SP),AX + MULQ ·_121666_213(SB) + SHRQ $13,AX + ADDQ AX,R10 + IMUL3Q $19,DX,DX + ADDQ DX,SI + ADDQ 80(SP),SI + ADDQ 88(SP),CX + ADDQ 96(SP),R8 + ADDQ 104(SP),R9 + ADDQ 112(SP),R10 + MOVQ SI,80(DI) + MOVQ CX,88(DI) + MOVQ R8,96(DI) + MOVQ R9,104(DI) + MOVQ R10,112(DI) + MOVQ 104(DI),SI + IMUL3Q $19,SI,AX + MOVQ AX,0(SP) + MULQ 176(SP) + MOVQ AX,SI + MOVQ DX,CX + MOVQ 112(DI),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 168(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 80(DI),AX + MULQ 160(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 80(DI),AX + MULQ 168(SP) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 80(DI),AX + MULQ 176(SP) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 80(DI),AX + MULQ 184(SP) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 80(DI),AX + MULQ 192(SP) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 88(DI),AX + MULQ 160(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 88(DI),AX + MULQ 168(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 88(DI),AX + MULQ 176(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 88(DI),AX + MULQ 184(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 88(DI),DX + IMUL3Q $19,DX,AX + MULQ 192(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 96(DI),AX + MULQ 160(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 96(DI),AX + MULQ 168(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 96(DI),AX + MULQ 176(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 96(DI),DX + IMUL3Q $19,DX,AX + MULQ 184(SP) + ADDQ AX,SI + ADCQ DX,CX + MOVQ 96(DI),DX + IMUL3Q $19,DX,AX + MULQ 192(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 104(DI),AX + MULQ 160(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 104(DI),AX + MULQ 168(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 0(SP),AX + MULQ 184(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SP),AX + MULQ 192(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 112(DI),AX + MULQ 160(SP) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SP),AX + MULQ 176(SP) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 8(SP),AX + MULQ 184(SP) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 192(SP) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ $REDMASK51,DX + SHLQ $13,SI,CX + ANDQ DX,SI + SHLQ $13,R8,R9 + ANDQ DX,R8 + ADDQ CX,R8 + SHLQ $13,R10,R11 + ANDQ DX,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ DX,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ DX,R14 + ADDQ R13,R14 + IMUL3Q $19,R15,CX + ADDQ CX,SI + MOVQ SI,CX + SHRQ $51,CX + ADDQ R8,CX + MOVQ CX,R8 + SHRQ $51,CX + ANDQ DX,SI + ADDQ R10,CX + MOVQ CX,R9 + SHRQ $51,CX + ANDQ DX,R8 + ADDQ R12,CX + MOVQ CX,AX + SHRQ $51,CX + ANDQ DX,R9 + ADDQ R14,CX + MOVQ CX,R10 + SHRQ $51,CX + ANDQ DX,AX + IMUL3Q $19,CX,CX + ADDQ CX,SI + ANDQ DX,R10 + MOVQ SI,80(DI) + MOVQ R8,88(DI) + MOVQ R9,96(DI) + MOVQ AX,104(DI) + MOVQ R10,112(DI) + RET + +// func cswap(inout *[4][5]uint64, v uint64) +TEXT ·cswap(SB),7,$0 + MOVQ inout+0(FP),DI + MOVQ v+8(FP),SI + + SUBQ $1, SI + NOTQ SI + MOVQ SI, X15 + PSHUFD $0x44, X15, X15 + + MOVOU 0(DI), X0 + MOVOU 16(DI), X2 + MOVOU 32(DI), X4 + MOVOU 48(DI), X6 + MOVOU 64(DI), X8 + MOVOU 80(DI), X1 + MOVOU 96(DI), X3 + MOVOU 112(DI), X5 + MOVOU 128(DI), X7 + MOVOU 144(DI), X9 + + MOVO X1, X10 + MOVO X3, X11 + MOVO X5, X12 + MOVO X7, X13 + MOVO X9, X14 + + PXOR X0, X10 + PXOR X2, X11 + PXOR X4, X12 + PXOR X6, X13 + PXOR X8, X14 + PAND X15, X10 + PAND X15, X11 + PAND X15, X12 + PAND X15, X13 + PAND X15, X14 + PXOR X10, X0 + PXOR X10, X1 + PXOR X11, X2 + PXOR X11, X3 + PXOR X12, X4 + PXOR X12, X5 + PXOR X13, X6 + PXOR X13, X7 + PXOR X14, X8 + PXOR X14, X9 + + MOVOU X0, 0(DI) + MOVOU X2, 16(DI) + MOVOU X4, 32(DI) + MOVOU X6, 48(DI) + MOVOU X8, 64(DI) + MOVOU X1, 80(DI) + MOVOU X3, 96(DI) + MOVOU X5, 112(DI) + MOVOU X7, 128(DI) + MOVOU X9, 144(DI) + RET + +// func mul(dest, a, b *[5]uint64) +TEXT ·mul(SB),0,$16-24 + MOVQ dest+0(FP), DI + MOVQ a+8(FP), SI + MOVQ b+16(FP), DX + + MOVQ DX,CX + MOVQ 24(SI),DX + IMUL3Q $19,DX,AX + MOVQ AX,0(SP) + MULQ 16(CX) + MOVQ AX,R8 + MOVQ DX,R9 + MOVQ 32(SI),DX + IMUL3Q $19,DX,AX + MOVQ AX,8(SP) + MULQ 8(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SI),AX + MULQ 0(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 0(SI),AX + MULQ 8(CX) + MOVQ AX,R10 + MOVQ DX,R11 + MOVQ 0(SI),AX + MULQ 16(CX) + MOVQ AX,R12 + MOVQ DX,R13 + MOVQ 0(SI),AX + MULQ 24(CX) + MOVQ AX,R14 + MOVQ DX,R15 + MOVQ 0(SI),AX + MULQ 32(CX) + MOVQ AX,BX + MOVQ DX,BP + MOVQ 8(SI),AX + MULQ 0(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SI),AX + MULQ 8(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 8(SI),AX + MULQ 16(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 8(SI),AX + MULQ 24(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 8(SI),DX + IMUL3Q $19,DX,AX + MULQ 32(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 16(SI),AX + MULQ 0(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 16(SI),AX + MULQ 8(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 16(SI),AX + MULQ 16(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 16(SI),DX + IMUL3Q $19,DX,AX + MULQ 24(CX) + ADDQ AX,R8 + ADCQ DX,R9 + MOVQ 16(SI),DX + IMUL3Q $19,DX,AX + MULQ 32(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 24(SI),AX + MULQ 0(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ 24(SI),AX + MULQ 8(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 0(SP),AX + MULQ 24(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 0(SP),AX + MULQ 32(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 32(SI),AX + MULQ 0(CX) + ADDQ AX,BX + ADCQ DX,BP + MOVQ 8(SP),AX + MULQ 16(CX) + ADDQ AX,R10 + ADCQ DX,R11 + MOVQ 8(SP),AX + MULQ 24(CX) + ADDQ AX,R12 + ADCQ DX,R13 + MOVQ 8(SP),AX + MULQ 32(CX) + ADDQ AX,R14 + ADCQ DX,R15 + MOVQ $REDMASK51,SI + SHLQ $13,R8,R9 + ANDQ SI,R8 + SHLQ $13,R10,R11 + ANDQ SI,R10 + ADDQ R9,R10 + SHLQ $13,R12,R13 + ANDQ SI,R12 + ADDQ R11,R12 + SHLQ $13,R14,R15 + ANDQ SI,R14 + ADDQ R13,R14 + SHLQ $13,BX,BP + ANDQ SI,BX + ADDQ R15,BX + IMUL3Q $19,BP,DX + ADDQ DX,R8 + MOVQ R8,DX + SHRQ $51,DX + ADDQ R10,DX + MOVQ DX,CX + SHRQ $51,DX + ANDQ SI,R8 + ADDQ R12,DX + MOVQ DX,R9 + SHRQ $51,DX + ANDQ SI,CX + ADDQ R14,DX + MOVQ DX,AX + SHRQ $51,DX + ANDQ SI,R9 + ADDQ BX,DX + MOVQ DX,R10 + SHRQ $51,DX + ANDQ SI,AX + IMUL3Q $19,DX,DX + ADDQ DX,R8 + ANDQ SI,R10 + MOVQ R8,0(DI) + MOVQ CX,8(DI) + MOVQ R9,16(DI) + MOVQ AX,24(DI) + MOVQ R10,32(DI) + RET + +// func square(out, in *[5]uint64) +TEXT ·square(SB),7,$0-16 + MOVQ out+0(FP), DI + MOVQ in+8(FP), SI + + MOVQ 0(SI),AX + MULQ 0(SI) + MOVQ AX,CX + MOVQ DX,R8 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 8(SI) + MOVQ AX,R9 + MOVQ DX,R10 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 16(SI) + MOVQ AX,R11 + MOVQ DX,R12 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 24(SI) + MOVQ AX,R13 + MOVQ DX,R14 + MOVQ 0(SI),AX + SHLQ $1,AX + MULQ 32(SI) + MOVQ AX,R15 + MOVQ DX,BX + MOVQ 8(SI),AX + MULQ 8(SI) + ADDQ AX,R11 + ADCQ DX,R12 + MOVQ 8(SI),AX + SHLQ $1,AX + MULQ 16(SI) + ADDQ AX,R13 + ADCQ DX,R14 + MOVQ 8(SI),AX + SHLQ $1,AX + MULQ 24(SI) + ADDQ AX,R15 + ADCQ DX,BX + MOVQ 8(SI),DX + IMUL3Q $38,DX,AX + MULQ 32(SI) + ADDQ AX,CX + ADCQ DX,R8 + MOVQ 16(SI),AX + MULQ 16(SI) + ADDQ AX,R15 + ADCQ DX,BX + MOVQ 16(SI),DX + IMUL3Q $38,DX,AX + MULQ 24(SI) + ADDQ AX,CX + ADCQ DX,R8 + MOVQ 16(SI),DX + IMUL3Q $38,DX,AX + MULQ 32(SI) + ADDQ AX,R9 + ADCQ DX,R10 + MOVQ 24(SI),DX + IMUL3Q $19,DX,AX + MULQ 24(SI) + ADDQ AX,R9 + ADCQ DX,R10 + MOVQ 24(SI),DX + IMUL3Q $38,DX,AX + MULQ 32(SI) + ADDQ AX,R11 + ADCQ DX,R12 + MOVQ 32(SI),DX + IMUL3Q $19,DX,AX + MULQ 32(SI) + ADDQ AX,R13 + ADCQ DX,R14 + MOVQ $REDMASK51,SI + SHLQ $13,CX,R8 + ANDQ SI,CX + SHLQ $13,R9,R10 + ANDQ SI,R9 + ADDQ R8,R9 + SHLQ $13,R11,R12 + ANDQ SI,R11 + ADDQ R10,R11 + SHLQ $13,R13,R14 + ANDQ SI,R13 + ADDQ R12,R13 + SHLQ $13,R15,BX + ANDQ SI,R15 + ADDQ R14,R15 + IMUL3Q $19,BX,DX + ADDQ DX,CX + MOVQ CX,DX + SHRQ $51,DX + ADDQ R9,DX + ANDQ SI,CX + MOVQ DX,R8 + SHRQ $51,DX + ADDQ R11,DX + ANDQ SI,R8 + MOVQ DX,R9 + SHRQ $51,DX + ADDQ R13,DX + ANDQ SI,R9 + MOVQ DX,AX + SHRQ $51,DX + ADDQ R15,DX + ANDQ SI,AX + MOVQ DX,R10 + SHRQ $51,DX + IMUL3Q $19,DX,DX + ADDQ DX,CX + ANDQ SI,R10 + MOVQ CX,0(DI) + MOVQ R8,8(DI) + MOVQ R9,16(DI) + MOVQ AX,24(DI) + MOVQ R10,32(DI) + RET diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go b/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go new file mode 100644 index 00000000000..c43b13fc83e --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/curve25519_generic.go @@ -0,0 +1,828 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package curve25519 + +import "encoding/binary" + +// This code is a port of the public domain, "ref10" implementation of +// curve25519 from SUPERCOP 20130419 by D. J. Bernstein. + +// fieldElement represents an element of the field GF(2^255 - 19). An element +// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 +// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on +// context. +type fieldElement [10]int32 + +func feZero(fe *fieldElement) { + for i := range fe { + fe[i] = 0 + } +} + +func feOne(fe *fieldElement) { + feZero(fe) + fe[0] = 1 +} + +func feAdd(dst, a, b *fieldElement) { + for i := range dst { + dst[i] = a[i] + b[i] + } +} + +func feSub(dst, a, b *fieldElement) { + for i := range dst { + dst[i] = a[i] - b[i] + } +} + +func feCopy(dst, src *fieldElement) { + for i := range dst { + dst[i] = src[i] + } +} + +// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0. +// +// Preconditions: b in {0,1}. +func feCSwap(f, g *fieldElement, b int32) { + b = -b + for i := range f { + t := b & (f[i] ^ g[i]) + f[i] ^= t + g[i] ^= t + } +} + +// load3 reads a 24-bit, little-endian value from in. +func load3(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + return r +} + +// load4 reads a 32-bit, little-endian value from in. +func load4(in []byte) int64 { + return int64(binary.LittleEndian.Uint32(in)) +} + +func feFromBytes(dst *fieldElement, src *[32]byte) { + h0 := load4(src[:]) + h1 := load3(src[4:]) << 6 + h2 := load3(src[7:]) << 5 + h3 := load3(src[10:]) << 3 + h4 := load3(src[13:]) << 2 + h5 := load4(src[16:]) + h6 := load3(src[20:]) << 7 + h7 := load3(src[23:]) << 5 + h8 := load3(src[26:]) << 4 + h9 := (load3(src[29:]) & 0x7fffff) << 2 + + var carry [10]int64 + carry[9] = (h9 + 1<<24) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + carry[1] = (h1 + 1<<24) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[3] = (h3 + 1<<24) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[5] = (h5 + 1<<24) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + carry[7] = (h7 + 1<<24) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[0] = (h0 + 1<<25) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[2] = (h2 + 1<<25) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[4] = (h4 + 1<<25) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[6] = (h6 + 1<<25) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + carry[8] = (h8 + 1<<25) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + dst[0] = int32(h0) + dst[1] = int32(h1) + dst[2] = int32(h2) + dst[3] = int32(h3) + dst[4] = int32(h4) + dst[5] = int32(h5) + dst[6] = int32(h6) + dst[7] = int32(h7) + dst[8] = int32(h8) + dst[9] = int32(h9) +} + +// feToBytes marshals h to s. +// Preconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Write p=2^255-19; q=floor(h/p). +// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). +// +// Proof: +// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. +// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. +// +// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). +// Then 0> 25 + q = (h[0] + q) >> 26 + q = (h[1] + q) >> 25 + q = (h[2] + q) >> 26 + q = (h[3] + q) >> 25 + q = (h[4] + q) >> 26 + q = (h[5] + q) >> 25 + q = (h[6] + q) >> 26 + q = (h[7] + q) >> 25 + q = (h[8] + q) >> 26 + q = (h[9] + q) >> 25 + + // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. + h[0] += 19 * q + // Goal: Output h-2^255 q, which is between 0 and 2^255-20. + + carry[0] = h[0] >> 26 + h[1] += carry[0] + h[0] -= carry[0] << 26 + carry[1] = h[1] >> 25 + h[2] += carry[1] + h[1] -= carry[1] << 25 + carry[2] = h[2] >> 26 + h[3] += carry[2] + h[2] -= carry[2] << 26 + carry[3] = h[3] >> 25 + h[4] += carry[3] + h[3] -= carry[3] << 25 + carry[4] = h[4] >> 26 + h[5] += carry[4] + h[4] -= carry[4] << 26 + carry[5] = h[5] >> 25 + h[6] += carry[5] + h[5] -= carry[5] << 25 + carry[6] = h[6] >> 26 + h[7] += carry[6] + h[6] -= carry[6] << 26 + carry[7] = h[7] >> 25 + h[8] += carry[7] + h[7] -= carry[7] << 25 + carry[8] = h[8] >> 26 + h[9] += carry[8] + h[8] -= carry[8] << 26 + carry[9] = h[9] >> 25 + h[9] -= carry[9] << 25 + // h10 = carry9 + + // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; + // evidently 2^255 h10-2^255 q = 0. + // Goal: Output h[0]+...+2^230 h[9]. + + s[0] = byte(h[0] >> 0) + s[1] = byte(h[0] >> 8) + s[2] = byte(h[0] >> 16) + s[3] = byte((h[0] >> 24) | (h[1] << 2)) + s[4] = byte(h[1] >> 6) + s[5] = byte(h[1] >> 14) + s[6] = byte((h[1] >> 22) | (h[2] << 3)) + s[7] = byte(h[2] >> 5) + s[8] = byte(h[2] >> 13) + s[9] = byte((h[2] >> 21) | (h[3] << 5)) + s[10] = byte(h[3] >> 3) + s[11] = byte(h[3] >> 11) + s[12] = byte((h[3] >> 19) | (h[4] << 6)) + s[13] = byte(h[4] >> 2) + s[14] = byte(h[4] >> 10) + s[15] = byte(h[4] >> 18) + s[16] = byte(h[5] >> 0) + s[17] = byte(h[5] >> 8) + s[18] = byte(h[5] >> 16) + s[19] = byte((h[5] >> 24) | (h[6] << 1)) + s[20] = byte(h[6] >> 7) + s[21] = byte(h[6] >> 15) + s[22] = byte((h[6] >> 23) | (h[7] << 3)) + s[23] = byte(h[7] >> 5) + s[24] = byte(h[7] >> 13) + s[25] = byte((h[7] >> 21) | (h[8] << 4)) + s[26] = byte(h[8] >> 4) + s[27] = byte(h[8] >> 12) + s[28] = byte((h[8] >> 20) | (h[9] << 6)) + s[29] = byte(h[9] >> 2) + s[30] = byte(h[9] >> 10) + s[31] = byte(h[9] >> 18) +} + +// feMul calculates h = f * g +// Can overlap h with f or g. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Notes on implementation strategy: +// +// Using schoolbook multiplication. +// Karatsuba would save a little in some cost models. +// +// Most multiplications by 2 and 19 are 32-bit precomputations; +// cheaper than 64-bit postcomputations. +// +// There is one remaining multiplication by 19 in the carry chain; +// one *19 precomputation can be merged into this, +// but the resulting data flow is considerably less clean. +// +// There are 12 carries below. +// 10 of them are 2-way parallelizable and vectorizable. +// Can get away with 11 carries, but then data flow is much deeper. +// +// With tighter constraints on inputs can squeeze carries into int32. +func feMul(h, f, g *fieldElement) { + f0 := f[0] + f1 := f[1] + f2 := f[2] + f3 := f[3] + f4 := f[4] + f5 := f[5] + f6 := f[6] + f7 := f[7] + f8 := f[8] + f9 := f[9] + g0 := g[0] + g1 := g[1] + g2 := g[2] + g3 := g[3] + g4 := g[4] + g5 := g[5] + g6 := g[6] + g7 := g[7] + g8 := g[8] + g9 := g[9] + g1_19 := 19 * g1 // 1.4*2^29 + g2_19 := 19 * g2 // 1.4*2^30; still ok + g3_19 := 19 * g3 + g4_19 := 19 * g4 + g5_19 := 19 * g5 + g6_19 := 19 * g6 + g7_19 := 19 * g7 + g8_19 := 19 * g8 + g9_19 := 19 * g9 + f1_2 := 2 * f1 + f3_2 := 2 * f3 + f5_2 := 2 * f5 + f7_2 := 2 * f7 + f9_2 := 2 * f9 + f0g0 := int64(f0) * int64(g0) + f0g1 := int64(f0) * int64(g1) + f0g2 := int64(f0) * int64(g2) + f0g3 := int64(f0) * int64(g3) + f0g4 := int64(f0) * int64(g4) + f0g5 := int64(f0) * int64(g5) + f0g6 := int64(f0) * int64(g6) + f0g7 := int64(f0) * int64(g7) + f0g8 := int64(f0) * int64(g8) + f0g9 := int64(f0) * int64(g9) + f1g0 := int64(f1) * int64(g0) + f1g1_2 := int64(f1_2) * int64(g1) + f1g2 := int64(f1) * int64(g2) + f1g3_2 := int64(f1_2) * int64(g3) + f1g4 := int64(f1) * int64(g4) + f1g5_2 := int64(f1_2) * int64(g5) + f1g6 := int64(f1) * int64(g6) + f1g7_2 := int64(f1_2) * int64(g7) + f1g8 := int64(f1) * int64(g8) + f1g9_38 := int64(f1_2) * int64(g9_19) + f2g0 := int64(f2) * int64(g0) + f2g1 := int64(f2) * int64(g1) + f2g2 := int64(f2) * int64(g2) + f2g3 := int64(f2) * int64(g3) + f2g4 := int64(f2) * int64(g4) + f2g5 := int64(f2) * int64(g5) + f2g6 := int64(f2) * int64(g6) + f2g7 := int64(f2) * int64(g7) + f2g8_19 := int64(f2) * int64(g8_19) + f2g9_19 := int64(f2) * int64(g9_19) + f3g0 := int64(f3) * int64(g0) + f3g1_2 := int64(f3_2) * int64(g1) + f3g2 := int64(f3) * int64(g2) + f3g3_2 := int64(f3_2) * int64(g3) + f3g4 := int64(f3) * int64(g4) + f3g5_2 := int64(f3_2) * int64(g5) + f3g6 := int64(f3) * int64(g6) + f3g7_38 := int64(f3_2) * int64(g7_19) + f3g8_19 := int64(f3) * int64(g8_19) + f3g9_38 := int64(f3_2) * int64(g9_19) + f4g0 := int64(f4) * int64(g0) + f4g1 := int64(f4) * int64(g1) + f4g2 := int64(f4) * int64(g2) + f4g3 := int64(f4) * int64(g3) + f4g4 := int64(f4) * int64(g4) + f4g5 := int64(f4) * int64(g5) + f4g6_19 := int64(f4) * int64(g6_19) + f4g7_19 := int64(f4) * int64(g7_19) + f4g8_19 := int64(f4) * int64(g8_19) + f4g9_19 := int64(f4) * int64(g9_19) + f5g0 := int64(f5) * int64(g0) + f5g1_2 := int64(f5_2) * int64(g1) + f5g2 := int64(f5) * int64(g2) + f5g3_2 := int64(f5_2) * int64(g3) + f5g4 := int64(f5) * int64(g4) + f5g5_38 := int64(f5_2) * int64(g5_19) + f5g6_19 := int64(f5) * int64(g6_19) + f5g7_38 := int64(f5_2) * int64(g7_19) + f5g8_19 := int64(f5) * int64(g8_19) + f5g9_38 := int64(f5_2) * int64(g9_19) + f6g0 := int64(f6) * int64(g0) + f6g1 := int64(f6) * int64(g1) + f6g2 := int64(f6) * int64(g2) + f6g3 := int64(f6) * int64(g3) + f6g4_19 := int64(f6) * int64(g4_19) + f6g5_19 := int64(f6) * int64(g5_19) + f6g6_19 := int64(f6) * int64(g6_19) + f6g7_19 := int64(f6) * int64(g7_19) + f6g8_19 := int64(f6) * int64(g8_19) + f6g9_19 := int64(f6) * int64(g9_19) + f7g0 := int64(f7) * int64(g0) + f7g1_2 := int64(f7_2) * int64(g1) + f7g2 := int64(f7) * int64(g2) + f7g3_38 := int64(f7_2) * int64(g3_19) + f7g4_19 := int64(f7) * int64(g4_19) + f7g5_38 := int64(f7_2) * int64(g5_19) + f7g6_19 := int64(f7) * int64(g6_19) + f7g7_38 := int64(f7_2) * int64(g7_19) + f7g8_19 := int64(f7) * int64(g8_19) + f7g9_38 := int64(f7_2) * int64(g9_19) + f8g0 := int64(f8) * int64(g0) + f8g1 := int64(f8) * int64(g1) + f8g2_19 := int64(f8) * int64(g2_19) + f8g3_19 := int64(f8) * int64(g3_19) + f8g4_19 := int64(f8) * int64(g4_19) + f8g5_19 := int64(f8) * int64(g5_19) + f8g6_19 := int64(f8) * int64(g6_19) + f8g7_19 := int64(f8) * int64(g7_19) + f8g8_19 := int64(f8) * int64(g8_19) + f8g9_19 := int64(f8) * int64(g9_19) + f9g0 := int64(f9) * int64(g0) + f9g1_38 := int64(f9_2) * int64(g1_19) + f9g2_19 := int64(f9) * int64(g2_19) + f9g3_38 := int64(f9_2) * int64(g3_19) + f9g4_19 := int64(f9) * int64(g4_19) + f9g5_38 := int64(f9_2) * int64(g5_19) + f9g6_19 := int64(f9) * int64(g6_19) + f9g7_38 := int64(f9_2) * int64(g7_19) + f9g8_19 := int64(f9) * int64(g8_19) + f9g9_38 := int64(f9_2) * int64(g9_19) + h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38 + h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19 + h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38 + h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19 + h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38 + h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19 + h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38 + h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19 + h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38 + h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 + var carry [10]int64 + + // |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) + // i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 + // |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) + // i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + // |h0| <= 2^25 + // |h4| <= 2^25 + // |h1| <= 1.51*2^58 + // |h5| <= 1.51*2^58 + + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + // |h1| <= 2^24; from now on fits into int32 + // |h5| <= 2^24; from now on fits into int32 + // |h2| <= 1.21*2^59 + // |h6| <= 1.21*2^59 + + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + // |h2| <= 2^25; from now on fits into int32 unchanged + // |h6| <= 2^25; from now on fits into int32 unchanged + // |h3| <= 1.51*2^58 + // |h7| <= 1.51*2^58 + + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + // |h3| <= 2^24; from now on fits into int32 unchanged + // |h7| <= 2^24; from now on fits into int32 unchanged + // |h4| <= 1.52*2^33 + // |h8| <= 1.52*2^33 + + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + // |h4| <= 2^25; from now on fits into int32 unchanged + // |h8| <= 2^25; from now on fits into int32 unchanged + // |h5| <= 1.01*2^24 + // |h9| <= 1.51*2^58 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + // |h9| <= 2^24; from now on fits into int32 unchanged + // |h0| <= 1.8*2^37 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + // |h0| <= 2^25; from now on fits into int32 unchanged + // |h1| <= 1.01*2^24 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feSquare calculates h = f*f. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func feSquare(h, f *fieldElement) { + f0 := f[0] + f1 := f[1] + f2 := f[2] + f3 := f[3] + f4 := f[4] + f5 := f[5] + f6 := f[6] + f7 := f[7] + f8 := f[8] + f9 := f[9] + f0_2 := 2 * f0 + f1_2 := 2 * f1 + f2_2 := 2 * f2 + f3_2 := 2 * f3 + f4_2 := 2 * f4 + f5_2 := 2 * f5 + f6_2 := 2 * f6 + f7_2 := 2 * f7 + f5_38 := 38 * f5 // 1.31*2^30 + f6_19 := 19 * f6 // 1.31*2^30 + f7_38 := 38 * f7 // 1.31*2^30 + f8_19 := 19 * f8 // 1.31*2^30 + f9_38 := 38 * f9 // 1.31*2^30 + f0f0 := int64(f0) * int64(f0) + f0f1_2 := int64(f0_2) * int64(f1) + f0f2_2 := int64(f0_2) * int64(f2) + f0f3_2 := int64(f0_2) * int64(f3) + f0f4_2 := int64(f0_2) * int64(f4) + f0f5_2 := int64(f0_2) * int64(f5) + f0f6_2 := int64(f0_2) * int64(f6) + f0f7_2 := int64(f0_2) * int64(f7) + f0f8_2 := int64(f0_2) * int64(f8) + f0f9_2 := int64(f0_2) * int64(f9) + f1f1_2 := int64(f1_2) * int64(f1) + f1f2_2 := int64(f1_2) * int64(f2) + f1f3_4 := int64(f1_2) * int64(f3_2) + f1f4_2 := int64(f1_2) * int64(f4) + f1f5_4 := int64(f1_2) * int64(f5_2) + f1f6_2 := int64(f1_2) * int64(f6) + f1f7_4 := int64(f1_2) * int64(f7_2) + f1f8_2 := int64(f1_2) * int64(f8) + f1f9_76 := int64(f1_2) * int64(f9_38) + f2f2 := int64(f2) * int64(f2) + f2f3_2 := int64(f2_2) * int64(f3) + f2f4_2 := int64(f2_2) * int64(f4) + f2f5_2 := int64(f2_2) * int64(f5) + f2f6_2 := int64(f2_2) * int64(f6) + f2f7_2 := int64(f2_2) * int64(f7) + f2f8_38 := int64(f2_2) * int64(f8_19) + f2f9_38 := int64(f2) * int64(f9_38) + f3f3_2 := int64(f3_2) * int64(f3) + f3f4_2 := int64(f3_2) * int64(f4) + f3f5_4 := int64(f3_2) * int64(f5_2) + f3f6_2 := int64(f3_2) * int64(f6) + f3f7_76 := int64(f3_2) * int64(f7_38) + f3f8_38 := int64(f3_2) * int64(f8_19) + f3f9_76 := int64(f3_2) * int64(f9_38) + f4f4 := int64(f4) * int64(f4) + f4f5_2 := int64(f4_2) * int64(f5) + f4f6_38 := int64(f4_2) * int64(f6_19) + f4f7_38 := int64(f4) * int64(f7_38) + f4f8_38 := int64(f4_2) * int64(f8_19) + f4f9_38 := int64(f4) * int64(f9_38) + f5f5_38 := int64(f5) * int64(f5_38) + f5f6_38 := int64(f5_2) * int64(f6_19) + f5f7_76 := int64(f5_2) * int64(f7_38) + f5f8_38 := int64(f5_2) * int64(f8_19) + f5f9_76 := int64(f5_2) * int64(f9_38) + f6f6_19 := int64(f6) * int64(f6_19) + f6f7_38 := int64(f6) * int64(f7_38) + f6f8_38 := int64(f6_2) * int64(f8_19) + f6f9_38 := int64(f6) * int64(f9_38) + f7f7_38 := int64(f7) * int64(f7_38) + f7f8_38 := int64(f7_2) * int64(f8_19) + f7f9_76 := int64(f7_2) * int64(f9_38) + f8f8_19 := int64(f8) * int64(f8_19) + f8f9_38 := int64(f8) * int64(f9_38) + f9f9_38 := int64(f9) * int64(f9_38) + h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38 + h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38 + h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19 + h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38 + h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38 + h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38 + h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19 + h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38 + h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38 + h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2 + var carry [10]int64 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feMul121666 calculates h = f * 121666. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func feMul121666(h, f *fieldElement) { + h0 := int64(f[0]) * 121666 + h1 := int64(f[1]) * 121666 + h2 := int64(f[2]) * 121666 + h3 := int64(f[3]) * 121666 + h4 := int64(f[4]) * 121666 + h5 := int64(f[5]) * 121666 + h6 := int64(f[6]) * 121666 + h7 := int64(f[7]) * 121666 + h8 := int64(f[8]) * 121666 + h9 := int64(f[9]) * 121666 + var carry [10]int64 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feInvert sets out = z^-1. +func feInvert(out, z *fieldElement) { + var t0, t1, t2, t3 fieldElement + var i int + + feSquare(&t0, z) + for i = 1; i < 1; i++ { + feSquare(&t0, &t0) + } + feSquare(&t1, &t0) + for i = 1; i < 2; i++ { + feSquare(&t1, &t1) + } + feMul(&t1, z, &t1) + feMul(&t0, &t0, &t1) + feSquare(&t2, &t0) + for i = 1; i < 1; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t1, &t2) + feSquare(&t2, &t1) + for i = 1; i < 5; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t2, &t1) + for i = 1; i < 10; i++ { + feSquare(&t2, &t2) + } + feMul(&t2, &t2, &t1) + feSquare(&t3, &t2) + for i = 1; i < 20; i++ { + feSquare(&t3, &t3) + } + feMul(&t2, &t3, &t2) + feSquare(&t2, &t2) + for i = 1; i < 10; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t2, &t1) + for i = 1; i < 50; i++ { + feSquare(&t2, &t2) + } + feMul(&t2, &t2, &t1) + feSquare(&t3, &t2) + for i = 1; i < 100; i++ { + feSquare(&t3, &t3) + } + feMul(&t2, &t3, &t2) + feSquare(&t2, &t2) + for i = 1; i < 50; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t1, &t1) + for i = 1; i < 5; i++ { + feSquare(&t1, &t1) + } + feMul(out, &t1, &t0) +} + +func scalarMultGeneric(out, in, base *[32]byte) { + var e [32]byte + + copy(e[:], in[:]) + e[0] &= 248 + e[31] &= 127 + e[31] |= 64 + + var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement + feFromBytes(&x1, base) + feOne(&x2) + feCopy(&x3, &x1) + feOne(&z3) + + swap := int32(0) + for pos := 254; pos >= 0; pos-- { + b := e[pos/8] >> uint(pos&7) + b &= 1 + swap ^= int32(b) + feCSwap(&x2, &x3, swap) + feCSwap(&z2, &z3, swap) + swap = int32(b) + + feSub(&tmp0, &x3, &z3) + feSub(&tmp1, &x2, &z2) + feAdd(&x2, &x2, &z2) + feAdd(&z2, &x3, &z3) + feMul(&z3, &tmp0, &x2) + feMul(&z2, &z2, &tmp1) + feSquare(&tmp0, &tmp1) + feSquare(&tmp1, &x2) + feAdd(&x3, &z3, &z2) + feSub(&z2, &z3, &z2) + feMul(&x2, &tmp1, &tmp0) + feSub(&tmp1, &tmp1, &tmp0) + feSquare(&z2, &z2) + feMul121666(&z3, &tmp1) + feSquare(&x3, &x3) + feAdd(&tmp0, &tmp0, &z3) + feMul(&z3, &x1, &z2) + feMul(&z2, &tmp1, &tmp0) + } + + feCSwap(&x2, &x3, swap) + feCSwap(&z2, &z3, swap) + + feInvert(&z2, &z2) + feMul(&x2, &x2, &z2) + feToBytes(out, &x2) +} diff --git a/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go b/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go new file mode 100644 index 00000000000..047d49afc27 --- /dev/null +++ b/vendor/golang.org/x/crypto/curve25519/curve25519_noasm.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 gccgo appengine purego + +package curve25519 + +func scalarMult(out, in, base *[32]byte) { + scalarMultGeneric(out, in, base) +} diff --git a/vendor/golang.org/x/crypto/internal/subtle/aliasing.go b/vendor/golang.org/x/crypto/internal/subtle/aliasing.go new file mode 100644 index 00000000000..f38797bfa1b --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/subtle/aliasing.go @@ -0,0 +1,32 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine + +// Package subtle implements functions that are often useful in cryptographic +// code but require careful thought to use correctly. +package subtle // import "golang.org/x/crypto/internal/subtle" + +import "unsafe" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && + uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) +} + +// InexactOverlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// InexactOverlap can be used to implement the requirements of the crypto/cipher +// AEAD, Block, BlockMode and Stream interfaces. +func InexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return AnyOverlap(x, y) +} diff --git a/vendor/golang.org/x/crypto/internal/subtle/aliasing_appengine.go b/vendor/golang.org/x/crypto/internal/subtle/aliasing_appengine.go new file mode 100644 index 00000000000..0cc4a8a642c --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/subtle/aliasing_appengine.go @@ -0,0 +1,35 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +// Package subtle implements functions that are often useful in cryptographic +// code but require careful thought to use correctly. +package subtle // import "golang.org/x/crypto/internal/subtle" + +// This is the Google App Engine standard variant based on reflect +// because the unsafe package and cgo are disallowed. + +import "reflect" + +// AnyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +func AnyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() && + reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer() +} + +// InexactOverlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// InexactOverlap can be used to implement the requirements of the crypto/cipher +// AEAD, Block, BlockMode and Stream interfaces. +func InexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return AnyOverlap(x, y) +} diff --git a/vendor/golang.org/x/crypto/poly1305/bits_compat.go b/vendor/golang.org/x/crypto/poly1305/bits_compat.go new file mode 100644 index 00000000000..157a69f61bd --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/bits_compat.go @@ -0,0 +1,39 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.13 + +package poly1305 + +// Generic fallbacks for the math/bits intrinsics, copied from +// src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had +// variable time fallbacks until Go 1.13. + +func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) { + sum = x + y + carry + carryOut = ((x & y) | ((x | y) &^ sum)) >> 63 + return +} + +func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) { + diff = x - y - borrow + borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63 + return +} + +func bitsMul64(x, y uint64) (hi, lo uint64) { + const mask32 = 1<<32 - 1 + x0 := x & mask32 + x1 := x >> 32 + y0 := y & mask32 + y1 := y >> 32 + w0 := x0 * y0 + t := x1*y0 + w0>>32 + w1 := t & mask32 + w2 := t >> 32 + w1 += x0 * y1 + hi = x1*y1 + w2 + w1>>32 + lo = x * y + return +} diff --git a/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go b/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go new file mode 100644 index 00000000000..a0a185f0fc7 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/bits_go1.13.go @@ -0,0 +1,21 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.13 + +package poly1305 + +import "math/bits" + +func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) { + return bits.Add64(x, y, carry) +} + +func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) { + return bits.Sub64(x, y, borrow) +} + +func bitsMul64(x, y uint64) (hi, lo uint64) { + return bits.Mul64(x, y) +} diff --git a/vendor/golang.org/x/crypto/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/poly1305/mac_noasm.go new file mode 100644 index 00000000000..a8dd589ae39 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/mac_noasm.go @@ -0,0 +1,11 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64,!ppc64le gccgo appengine + +package poly1305 + +type mac struct{ macGeneric } + +func newMAC(key *[32]byte) mac { return mac{newMACGeneric(key)} } diff --git a/vendor/golang.org/x/crypto/poly1305/poly1305.go b/vendor/golang.org/x/crypto/poly1305/poly1305.go new file mode 100644 index 00000000000..066159b797d --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/poly1305.go @@ -0,0 +1,89 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package poly1305 implements Poly1305 one-time message authentication code as +// specified in https://cr.yp.to/mac/poly1305-20050329.pdf. +// +// Poly1305 is a fast, one-time authentication function. It is infeasible for an +// attacker to generate an authenticator for a message without the key. However, a +// key must only be used for a single message. Authenticating two different +// messages with the same key allows an attacker to forge authenticators for other +// messages with the same key. +// +// Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was +// used with a fixed key in order to generate one-time keys from an nonce. +// However, in this package AES isn't used and the one-time key is specified +// directly. +package poly1305 // import "golang.org/x/crypto/poly1305" + +import "crypto/subtle" + +// TagSize is the size, in bytes, of a poly1305 authenticator. +const TagSize = 16 + +// Sum generates an authenticator for msg using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + sum(out, m, key) +} + +// Verify returns true if mac is a valid authenticator for m with the given key. +func Verify(mac *[16]byte, m []byte, key *[32]byte) bool { + var tmp [16]byte + Sum(&tmp, m, key) + return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1 +} + +// New returns a new MAC computing an authentication +// tag of all data written to it with the given key. +// This allows writing the message progressively instead +// of passing it as a single slice. Common users should use +// the Sum function instead. +// +// The key must be unique for each message, as authenticating +// two different messages with the same key allows an attacker +// to forge messages at will. +func New(key *[32]byte) *MAC { + return &MAC{ + mac: newMAC(key), + finalized: false, + } +} + +// MAC is an io.Writer computing an authentication tag +// of the data written to it. +// +// MAC cannot be used like common hash.Hash implementations, +// because using a poly1305 key twice breaks its security. +// Therefore writing data to a running MAC after calling +// Sum causes it to panic. +type MAC struct { + mac // platform-dependent implementation + + finalized bool +} + +// Size returns the number of bytes Sum will return. +func (h *MAC) Size() int { return TagSize } + +// Write adds more data to the running message authentication code. +// It never returns an error. +// +// It must not be called after the first call of Sum. +func (h *MAC) Write(p []byte) (n int, err error) { + if h.finalized { + panic("poly1305: write to MAC after Sum") + } + return h.mac.Write(p) +} + +// Sum computes the authenticator of all data written to the +// message authentication code. +func (h *MAC) Sum(b []byte) []byte { + var mac [TagSize]byte + h.mac.Sum(&mac) + h.finalized = true + return append(b, mac[:]...) +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_amd64.go b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go new file mode 100644 index 00000000000..df56a652ff0 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_amd64.go @@ -0,0 +1,58 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +package poly1305 + +//go:noescape +func update(state *macState, msg []byte) + +func sum(out *[16]byte, m []byte, key *[32]byte) { + h := newMAC(key) + h.Write(m) + h.Sum(out) +} + +func newMAC(key *[32]byte) (h mac) { + initialize(key, &h.r, &h.s) + return +} + +// mac is a wrapper for macGeneric that redirects calls that would have gone to +// updateGeneric to update. +// +// Its Write and Sum methods are otherwise identical to the macGeneric ones, but +// using function pointers would carry a major performance cost. +type mac struct{ macGeneric } + +func (h *mac) Write(p []byte) (int, error) { + nn := len(p) + if h.offset > 0 { + n := copy(h.buffer[h.offset:], p) + if h.offset+n < TagSize { + h.offset += n + return nn, nil + } + p = p[n:] + h.offset = 0 + update(&h.macState, h.buffer[:]) + } + if n := len(p) - (len(p) % TagSize); n > 0 { + update(&h.macState, p[:n]) + p = p[n:] + } + if len(p) > 0 { + h.offset += copy(h.buffer[h.offset:], p) + } + return nn, nil +} + +func (h *mac) Sum(out *[16]byte) { + state := h.macState + if h.offset > 0 { + update(&state, h.buffer[:h.offset]) + } + finalize(out, &state.h, &state.s) +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_amd64.s b/vendor/golang.org/x/crypto/poly1305/sum_amd64.s new file mode 100644 index 00000000000..8c0cefbb3cb --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_amd64.s @@ -0,0 +1,108 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +#include "textflag.h" + +#define POLY1305_ADD(msg, h0, h1, h2) \ + ADDQ 0(msg), h0; \ + ADCQ 8(msg), h1; \ + ADCQ $1, h2; \ + LEAQ 16(msg), msg + +#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \ + MOVQ r0, AX; \ + MULQ h0; \ + MOVQ AX, t0; \ + MOVQ DX, t1; \ + MOVQ r0, AX; \ + MULQ h1; \ + ADDQ AX, t1; \ + ADCQ $0, DX; \ + MOVQ r0, t2; \ + IMULQ h2, t2; \ + ADDQ DX, t2; \ + \ + MOVQ r1, AX; \ + MULQ h0; \ + ADDQ AX, t1; \ + ADCQ $0, DX; \ + MOVQ DX, h0; \ + MOVQ r1, t3; \ + IMULQ h2, t3; \ + MOVQ r1, AX; \ + MULQ h1; \ + ADDQ AX, t2; \ + ADCQ DX, t3; \ + ADDQ h0, t2; \ + ADCQ $0, t3; \ + \ + MOVQ t0, h0; \ + MOVQ t1, h1; \ + MOVQ t2, h2; \ + ANDQ $3, h2; \ + MOVQ t2, t0; \ + ANDQ $0xFFFFFFFFFFFFFFFC, t0; \ + ADDQ t0, h0; \ + ADCQ t3, h1; \ + ADCQ $0, h2; \ + SHRQ $2, t3, t2; \ + SHRQ $2, t3; \ + ADDQ t2, h0; \ + ADCQ t3, h1; \ + ADCQ $0, h2 + +// func update(state *[7]uint64, msg []byte) +TEXT ·update(SB), $0-32 + MOVQ state+0(FP), DI + MOVQ msg_base+8(FP), SI + MOVQ msg_len+16(FP), R15 + + MOVQ 0(DI), R8 // h0 + MOVQ 8(DI), R9 // h1 + MOVQ 16(DI), R10 // h2 + MOVQ 24(DI), R11 // r0 + MOVQ 32(DI), R12 // r1 + + CMPQ R15, $16 + JB bytes_between_0_and_15 + +loop: + POLY1305_ADD(SI, R8, R9, R10) + +multiply: + POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14) + SUBQ $16, R15 + CMPQ R15, $16 + JAE loop + +bytes_between_0_and_15: + TESTQ R15, R15 + JZ done + MOVQ $1, BX + XORQ CX, CX + XORQ R13, R13 + ADDQ R15, SI + +flush_buffer: + SHLQ $8, BX, CX + SHLQ $8, BX + MOVB -1(SI), R13 + XORQ R13, BX + DECQ SI + DECQ R15 + JNZ flush_buffer + + ADDQ BX, R8 + ADCQ CX, R9 + ADCQ $0, R10 + MOVQ $16, R15 + JMP multiply + +done: + MOVQ R8, 0(DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + RET diff --git a/vendor/golang.org/x/crypto/poly1305/sum_generic.go b/vendor/golang.org/x/crypto/poly1305/sum_generic.go new file mode 100644 index 00000000000..1187eab78fd --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_generic.go @@ -0,0 +1,307 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file provides the generic implementation of Sum and MAC. Other files +// might provide optimized assembly implementations of some of this code. + +package poly1305 + +import "encoding/binary" + +// Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag +// for a 64 bytes message is approximately +// +// s + m[0:16] * r⁴ + m[16:32] * r³ + m[32:48] * r² + m[48:64] * r mod 2¹³⁰ - 5 +// +// for some secret r and s. It can be computed sequentially like +// +// for len(msg) > 0: +// h += read(msg, 16) +// h *= r +// h %= 2¹³⁰ - 5 +// return h + s +// +// All the complexity is about doing performant constant-time math on numbers +// larger than any available numeric type. + +func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) { + h := newMACGeneric(key) + h.Write(msg) + h.Sum(out) +} + +func newMACGeneric(key *[32]byte) (h macGeneric) { + initialize(key, &h.r, &h.s) + return +} + +// macState holds numbers in saturated 64-bit little-endian limbs. That is, +// the value of [x0, x1, x2] is x[0] + x[1] * 2⁶⁴ + x[2] * 2¹²⁸. +type macState struct { + // h is the main accumulator. It is to be interpreted modulo 2¹³⁰ - 5, but + // can grow larger during and after rounds. + h [3]uint64 + // r and s are the private key components. + r [2]uint64 + s [2]uint64 +} + +type macGeneric struct { + macState + + buffer [TagSize]byte + offset int +} + +// Write splits the incoming message into TagSize chunks, and passes them to +// update. It buffers incomplete chunks. +func (h *macGeneric) Write(p []byte) (int, error) { + nn := len(p) + if h.offset > 0 { + n := copy(h.buffer[h.offset:], p) + if h.offset+n < TagSize { + h.offset += n + return nn, nil + } + p = p[n:] + h.offset = 0 + updateGeneric(&h.macState, h.buffer[:]) + } + if n := len(p) - (len(p) % TagSize); n > 0 { + updateGeneric(&h.macState, p[:n]) + p = p[n:] + } + if len(p) > 0 { + h.offset += copy(h.buffer[h.offset:], p) + } + return nn, nil +} + +// Sum flushes the last incomplete chunk from the buffer, if any, and generates +// the MAC output. It does not modify its state, in order to allow for multiple +// calls to Sum, even if no Write is allowed after Sum. +func (h *macGeneric) Sum(out *[TagSize]byte) { + state := h.macState + if h.offset > 0 { + updateGeneric(&state, h.buffer[:h.offset]) + } + finalize(out, &state.h, &state.s) +} + +// [rMask0, rMask1] is the specified Poly1305 clamping mask in little-endian. It +// clears some bits of the secret coefficient to make it possible to implement +// multiplication more efficiently. +const ( + rMask0 = 0x0FFFFFFC0FFFFFFF + rMask1 = 0x0FFFFFFC0FFFFFFC +) + +func initialize(key *[32]byte, r, s *[2]uint64) { + r[0] = binary.LittleEndian.Uint64(key[0:8]) & rMask0 + r[1] = binary.LittleEndian.Uint64(key[8:16]) & rMask1 + s[0] = binary.LittleEndian.Uint64(key[16:24]) + s[1] = binary.LittleEndian.Uint64(key[24:32]) +} + +// uint128 holds a 128-bit number as two 64-bit limbs, for use with the +// bits.Mul64 and bits.Add64 intrinsics. +type uint128 struct { + lo, hi uint64 +} + +func mul64(a, b uint64) uint128 { + hi, lo := bitsMul64(a, b) + return uint128{lo, hi} +} + +func add128(a, b uint128) uint128 { + lo, c := bitsAdd64(a.lo, b.lo, 0) + hi, c := bitsAdd64(a.hi, b.hi, c) + if c != 0 { + panic("poly1305: unexpected overflow") + } + return uint128{lo, hi} +} + +func shiftRightBy2(a uint128) uint128 { + a.lo = a.lo>>2 | (a.hi&3)<<62 + a.hi = a.hi >> 2 + return a +} + +// updateGeneric absorbs msg into the state.h accumulator. For each chunk m of +// 128 bits of message, it computes +// +// h₊ = (h + m) * r mod 2¹³⁰ - 5 +// +// If the msg length is not a multiple of TagSize, it assumes the last +// incomplete chunk is the final one. +func updateGeneric(state *macState, msg []byte) { + h0, h1, h2 := state.h[0], state.h[1], state.h[2] + r0, r1 := state.r[0], state.r[1] + + for len(msg) > 0 { + var c uint64 + + // For the first step, h + m, we use a chain of bits.Add64 intrinsics. + // The resulting value of h might exceed 2¹³⁰ - 5, but will be partially + // reduced at the end of the multiplication below. + // + // The spec requires us to set a bit just above the message size, not to + // hide leading zeroes. For full chunks, that's 1 << 128, so we can just + // add 1 to the most significant (2¹²⁸) limb, h2. + if len(msg) >= TagSize { + h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0) + h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(msg[8:16]), c) + h2 += c + 1 + + msg = msg[TagSize:] + } else { + var buf [TagSize]byte + copy(buf[:], msg) + buf[len(msg)] = 1 + + h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0) + h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(buf[8:16]), c) + h2 += c + + msg = nil + } + + // Multiplication of big number limbs is similar to elementary school + // columnar multiplication. Instead of digits, there are 64-bit limbs. + // + // We are multiplying a 3 limbs number, h, by a 2 limbs number, r. + // + // h2 h1 h0 x + // r1 r0 = + // ---------------- + // h2r0 h1r0 h0r0 <-- individual 128-bit products + // + h2r1 h1r1 h0r1 + // ------------------------ + // m3 m2 m1 m0 <-- result in 128-bit overlapping limbs + // ------------------------ + // m3.hi m2.hi m1.hi m0.hi <-- carry propagation + // + m3.lo m2.lo m1.lo m0.lo + // ------------------------------- + // t4 t3 t2 t1 t0 <-- final result in 64-bit limbs + // + // The main difference from pen-and-paper multiplication is that we do + // carry propagation in a separate step, as if we wrote two digit sums + // at first (the 128-bit limbs), and then carried the tens all at once. + + h0r0 := mul64(h0, r0) + h1r0 := mul64(h1, r0) + h2r0 := mul64(h2, r0) + h0r1 := mul64(h0, r1) + h1r1 := mul64(h1, r1) + h2r1 := mul64(h2, r1) + + // Since h2 is known to be at most 7 (5 + 1 + 1), and r0 and r1 have their + // top 4 bits cleared by rMask{0,1}, we know that their product is not going + // to overflow 64 bits, so we can ignore the high part of the products. + // + // This also means that the product doesn't have a fifth limb (t4). + if h2r0.hi != 0 { + panic("poly1305: unexpected overflow") + } + if h2r1.hi != 0 { + panic("poly1305: unexpected overflow") + } + + m0 := h0r0 + m1 := add128(h1r0, h0r1) // These two additions don't overflow thanks again + m2 := add128(h2r0, h1r1) // to the 4 masked bits at the top of r0 and r1. + m3 := h2r1 + + t0 := m0.lo + t1, c := bitsAdd64(m1.lo, m0.hi, 0) + t2, c := bitsAdd64(m2.lo, m1.hi, c) + t3, _ := bitsAdd64(m3.lo, m2.hi, c) + + // Now we have the result as 4 64-bit limbs, and we need to reduce it + // modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do + // a cheap partial reduction according to the reduction identity + // + // c * 2¹³⁰ + n = c * 5 + n mod 2¹³⁰ - 5 + // + // because 2¹³⁰ = 5 mod 2¹³⁰ - 5. Partial reduction since the result is + // likely to be larger than 2¹³⁰ - 5, but still small enough to fit the + // assumptions we make about h in the rest of the code. + // + // See also https://speakerdeck.com/gtank/engineering-prime-numbers?slide=23 + + // We split the final result at the 2¹³⁰ mark into h and cc, the carry. + // Note that the carry bits are effectively shifted left by 2, in other + // words, cc = c * 4 for the c in the reduction identity. + h0, h1, h2 = t0, t1, t2&maskLow2Bits + cc := uint128{t2 & maskNotLow2Bits, t3} + + // To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c. + + h0, c = bitsAdd64(h0, cc.lo, 0) + h1, c = bitsAdd64(h1, cc.hi, c) + h2 += c + + cc = shiftRightBy2(cc) + + h0, c = bitsAdd64(h0, cc.lo, 0) + h1, c = bitsAdd64(h1, cc.hi, c) + h2 += c + + // h2 is at most 3 + 1 + 1 = 5, making the whole of h at most + // + // 5 * 2¹²⁸ + (2¹²⁸ - 1) = 6 * 2¹²⁸ - 1 + } + + state.h[0], state.h[1], state.h[2] = h0, h1, h2 +} + +const ( + maskLow2Bits uint64 = 0x0000000000000003 + maskNotLow2Bits uint64 = ^maskLow2Bits +) + +// select64 returns x if v == 1 and y if v == 0, in constant time. +func select64(v, x, y uint64) uint64 { return ^(v-1)&x | (v-1)&y } + +// [p0, p1, p2] is 2¹³⁰ - 5 in little endian order. +const ( + p0 = 0xFFFFFFFFFFFFFFFB + p1 = 0xFFFFFFFFFFFFFFFF + p2 = 0x0000000000000003 +) + +// finalize completes the modular reduction of h and computes +// +// out = h + s mod 2¹²⁸ +// +func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) { + h0, h1, h2 := h[0], h[1], h[2] + + // After the partial reduction in updateGeneric, h might be more than + // 2¹³⁰ - 5, but will be less than 2 * (2¹³⁰ - 5). To complete the reduction + // in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the + // result if the subtraction underflows, and t otherwise. + + hMinusP0, b := bitsSub64(h0, p0, 0) + hMinusP1, b := bitsSub64(h1, p1, b) + _, b = bitsSub64(h2, p2, b) + + // h = h if h < p else h - p + h0 = select64(b, h0, hMinusP0) + h1 = select64(b, h1, hMinusP1) + + // Finally, we compute the last Poly1305 step + // + // tag = h + s mod 2¹²⁸ + // + // by just doing a wide addition with the 128 low bits of h and discarding + // the overflow. + h0, c := bitsAdd64(h0, s[0], 0) + h1, _ = bitsAdd64(h1, s[1], c) + + binary.LittleEndian.PutUint64(out[0:8], h0) + binary.LittleEndian.PutUint64(out[8:16], h1) +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_noasm.go b/vendor/golang.org/x/crypto/poly1305/sum_noasm.go new file mode 100644 index 00000000000..32a9cef6bbf --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_noasm.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build s390x,!go1.11 !amd64,!s390x,!ppc64le gccgo appengine nacl + +package poly1305 + +func sum(out *[TagSize]byte, msg []byte, key *[32]byte) { + h := newMAC(key) + h.Write(msg) + h.Sum(out) +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go new file mode 100644 index 00000000000..3233616935b --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.go @@ -0,0 +1,58 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ppc64le,!gccgo,!appengine + +package poly1305 + +//go:noescape +func update(state *macState, msg []byte) + +func sum(out *[16]byte, m []byte, key *[32]byte) { + h := newMAC(key) + h.Write(m) + h.Sum(out) +} + +func newMAC(key *[32]byte) (h mac) { + initialize(key, &h.r, &h.s) + return +} + +// mac is a wrapper for macGeneric that redirects calls that would have gone to +// updateGeneric to update. +// +// Its Write and Sum methods are otherwise identical to the macGeneric ones, but +// using function pointers would carry a major performance cost. +type mac struct{ macGeneric } + +func (h *mac) Write(p []byte) (int, error) { + nn := len(p) + if h.offset > 0 { + n := copy(h.buffer[h.offset:], p) + if h.offset+n < TagSize { + h.offset += n + return nn, nil + } + p = p[n:] + h.offset = 0 + update(&h.macState, h.buffer[:]) + } + if n := len(p) - (len(p) % TagSize); n > 0 { + update(&h.macState, p[:n]) + p = p[n:] + } + if len(p) > 0 { + h.offset += copy(h.buffer[h.offset:], p) + } + return nn, nil +} + +func (h *mac) Sum(out *[16]byte) { + state := h.macState + if h.offset > 0 { + update(&state, h.buffer[:h.offset]) + } + finalize(out, &state.h, &state.s) +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s new file mode 100644 index 00000000000..4e20bf299a5 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_ppc64le.s @@ -0,0 +1,181 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ppc64le,!gccgo,!appengine + +#include "textflag.h" + +// This was ported from the amd64 implementation. + +#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \ + MOVD (msg), t0; \ + MOVD 8(msg), t1; \ + MOVD $1, t2; \ + ADDC t0, h0, h0; \ + ADDE t1, h1, h1; \ + ADDE t2, h2; \ + ADD $16, msg + +#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \ + MULLD r0, h0, t0; \ + MULLD r0, h1, t4; \ + MULHDU r0, h0, t1; \ + MULHDU r0, h1, t5; \ + ADDC t4, t1, t1; \ + MULLD r0, h2, t2; \ + ADDZE t5; \ + MULHDU r1, h0, t4; \ + MULLD r1, h0, h0; \ + ADD t5, t2, t2; \ + ADDC h0, t1, t1; \ + MULLD h2, r1, t3; \ + ADDZE t4, h0; \ + MULHDU r1, h1, t5; \ + MULLD r1, h1, t4; \ + ADDC t4, t2, t2; \ + ADDE t5, t3, t3; \ + ADDC h0, t2, t2; \ + MOVD $-4, t4; \ + MOVD t0, h0; \ + MOVD t1, h1; \ + ADDZE t3; \ + ANDCC $3, t2, h2; \ + AND t2, t4, t0; \ + ADDC t0, h0, h0; \ + ADDE t3, h1, h1; \ + SLD $62, t3, t4; \ + SRD $2, t2; \ + ADDZE h2; \ + OR t4, t2, t2; \ + SRD $2, t3; \ + ADDC t2, h0, h0; \ + ADDE t3, h1, h1; \ + ADDZE h2 + +DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF +DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC +GLOBL ·poly1305Mask<>(SB), RODATA, $16 + +// func update(state *[7]uint64, msg []byte) +TEXT ·update(SB), $0-32 + MOVD state+0(FP), R3 + MOVD msg_base+8(FP), R4 + MOVD msg_len+16(FP), R5 + + MOVD 0(R3), R8 // h0 + MOVD 8(R3), R9 // h1 + MOVD 16(R3), R10 // h2 + MOVD 24(R3), R11 // r0 + MOVD 32(R3), R12 // r1 + + CMP R5, $16 + BLT bytes_between_0_and_15 + +loop: + POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22) + +multiply: + POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21) + ADD $-16, R5 + CMP R5, $16 + BGE loop + +bytes_between_0_and_15: + CMP $0, R5 + BEQ done + MOVD $0, R16 // h0 + MOVD $0, R17 // h1 + +flush_buffer: + CMP R5, $8 + BLE just1 + + MOVD $8, R21 + SUB R21, R5, R21 + + // Greater than 8 -- load the rightmost remaining bytes in msg + // and put into R17 (h1) + MOVD (R4)(R21), R17 + MOVD $16, R22 + + // Find the offset to those bytes + SUB R5, R22, R22 + SLD $3, R22 + + // Shift to get only the bytes in msg + SRD R22, R17, R17 + + // Put 1 at high end + MOVD $1, R23 + SLD $3, R21 + SLD R21, R23, R23 + OR R23, R17, R17 + + // Remainder is 8 + MOVD $8, R5 + +just1: + CMP R5, $8 + BLT less8 + + // Exactly 8 + MOVD (R4), R16 + + CMP $0, R17 + + // Check if we've already set R17; if not + // set 1 to indicate end of msg. + BNE carry + MOVD $1, R17 + BR carry + +less8: + MOVD $0, R16 // h0 + MOVD $0, R22 // shift count + CMP R5, $4 + BLT less4 + MOVWZ (R4), R16 + ADD $4, R4 + ADD $-4, R5 + MOVD $32, R22 + +less4: + CMP R5, $2 + BLT less2 + MOVHZ (R4), R21 + SLD R22, R21, R21 + OR R16, R21, R16 + ADD $16, R22 + ADD $-2, R5 + ADD $2, R4 + +less2: + CMP $0, R5 + BEQ insert1 + MOVBZ (R4), R21 + SLD R22, R21, R21 + OR R16, R21, R16 + ADD $8, R22 + +insert1: + // Insert 1 at end of msg + MOVD $1, R21 + SLD R22, R21, R21 + OR R16, R21, R16 + +carry: + // Add new values to h0, h1, h2 + ADDC R16, R8 + ADDE R17, R9 + ADDE $0, R10 + MOVD $16, R5 + ADD R5, R4 + BR multiply + +done: + // Save h0, h1, h2 in state + MOVD R8, 0(R3) + MOVD R9, 8(R3) + MOVD R10, 16(R3) + RET diff --git a/vendor/golang.org/x/crypto/poly1305/sum_s390x.go b/vendor/golang.org/x/crypto/poly1305/sum_s390x.go new file mode 100644 index 00000000000..a8920ee9d21 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_s390x.go @@ -0,0 +1,39 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build s390x,go1.11,!gccgo,!appengine + +package poly1305 + +import ( + "golang.org/x/sys/cpu" +) + +// poly1305vx is an assembly implementation of Poly1305 that uses vector +// instructions. It must only be called if the vector facility (vx) is +// available. +//go:noescape +func poly1305vx(out *[16]byte, m *byte, mlen uint64, key *[32]byte) + +// poly1305vmsl is an assembly implementation of Poly1305 that uses vector +// instructions, including VMSL. It must only be called if the vector facility (vx) is +// available and if VMSL is supported. +//go:noescape +func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]byte) + +func sum(out *[16]byte, m []byte, key *[32]byte) { + if cpu.S390X.HasVX { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + if cpu.S390X.HasVXE && len(m) > 256 { + poly1305vmsl(out, mPtr, uint64(len(m)), key) + } else { + poly1305vx(out, mPtr, uint64(len(m)), key) + } + } else { + sumGeneric(out, m, key) + } +} diff --git a/vendor/golang.org/x/crypto/poly1305/sum_s390x.s b/vendor/golang.org/x/crypto/poly1305/sum_s390x.s new file mode 100644 index 00000000000..ca5a309d867 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_s390x.s @@ -0,0 +1,378 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build s390x,go1.11,!gccgo,!appengine + +#include "textflag.h" + +// Implementation of Poly1305 using the vector facility (vx). + +// constants +#define MOD26 V0 +#define EX0 V1 +#define EX1 V2 +#define EX2 V3 + +// temporaries +#define T_0 V4 +#define T_1 V5 +#define T_2 V6 +#define T_3 V7 +#define T_4 V8 + +// key (r) +#define R_0 V9 +#define R_1 V10 +#define R_2 V11 +#define R_3 V12 +#define R_4 V13 +#define R5_1 V14 +#define R5_2 V15 +#define R5_3 V16 +#define R5_4 V17 +#define RSAVE_0 R5 +#define RSAVE_1 R6 +#define RSAVE_2 R7 +#define RSAVE_3 R8 +#define RSAVE_4 R9 +#define R5SAVE_1 V28 +#define R5SAVE_2 V29 +#define R5SAVE_3 V30 +#define R5SAVE_4 V31 + +// message block +#define F_0 V18 +#define F_1 V19 +#define F_2 V20 +#define F_3 V21 +#define F_4 V22 + +// accumulator +#define H_0 V23 +#define H_1 V24 +#define H_2 V25 +#define H_3 V26 +#define H_4 V27 + +GLOBL ·keyMask<>(SB), RODATA, $16 +DATA ·keyMask<>+0(SB)/8, $0xffffff0ffcffff0f +DATA ·keyMask<>+8(SB)/8, $0xfcffff0ffcffff0f + +GLOBL ·bswapMask<>(SB), RODATA, $16 +DATA ·bswapMask<>+0(SB)/8, $0x0f0e0d0c0b0a0908 +DATA ·bswapMask<>+8(SB)/8, $0x0706050403020100 + +GLOBL ·constants<>(SB), RODATA, $64 +// MOD26 +DATA ·constants<>+0(SB)/8, $0x3ffffff +DATA ·constants<>+8(SB)/8, $0x3ffffff +// EX0 +DATA ·constants<>+16(SB)/8, $0x0006050403020100 +DATA ·constants<>+24(SB)/8, $0x1016151413121110 +// EX1 +DATA ·constants<>+32(SB)/8, $0x060c0b0a09080706 +DATA ·constants<>+40(SB)/8, $0x161c1b1a19181716 +// EX2 +DATA ·constants<>+48(SB)/8, $0x0d0d0d0d0d0f0e0d +DATA ·constants<>+56(SB)/8, $0x1d1d1d1d1d1f1e1d + +// h = (f*g) % (2**130-5) [partial reduction] +#define MULTIPLY(f0, f1, f2, f3, f4, g0, g1, g2, g3, g4, g51, g52, g53, g54, h0, h1, h2, h3, h4) \ + VMLOF f0, g0, h0 \ + VMLOF f0, g1, h1 \ + VMLOF f0, g2, h2 \ + VMLOF f0, g3, h3 \ + VMLOF f0, g4, h4 \ + VMLOF f1, g54, T_0 \ + VMLOF f1, g0, T_1 \ + VMLOF f1, g1, T_2 \ + VMLOF f1, g2, T_3 \ + VMLOF f1, g3, T_4 \ + VMALOF f2, g53, h0, h0 \ + VMALOF f2, g54, h1, h1 \ + VMALOF f2, g0, h2, h2 \ + VMALOF f2, g1, h3, h3 \ + VMALOF f2, g2, h4, h4 \ + VMALOF f3, g52, T_0, T_0 \ + VMALOF f3, g53, T_1, T_1 \ + VMALOF f3, g54, T_2, T_2 \ + VMALOF f3, g0, T_3, T_3 \ + VMALOF f3, g1, T_4, T_4 \ + VMALOF f4, g51, h0, h0 \ + VMALOF f4, g52, h1, h1 \ + VMALOF f4, g53, h2, h2 \ + VMALOF f4, g54, h3, h3 \ + VMALOF f4, g0, h4, h4 \ + VAG T_0, h0, h0 \ + VAG T_1, h1, h1 \ + VAG T_2, h2, h2 \ + VAG T_3, h3, h3 \ + VAG T_4, h4, h4 + +// carry h0->h1 h3->h4, h1->h2 h4->h0, h0->h1 h2->h3, h3->h4 +#define REDUCE(h0, h1, h2, h3, h4) \ + VESRLG $26, h0, T_0 \ + VESRLG $26, h3, T_1 \ + VN MOD26, h0, h0 \ + VN MOD26, h3, h3 \ + VAG T_0, h1, h1 \ + VAG T_1, h4, h4 \ + VESRLG $26, h1, T_2 \ + VESRLG $26, h4, T_3 \ + VN MOD26, h1, h1 \ + VN MOD26, h4, h4 \ + VESLG $2, T_3, T_4 \ + VAG T_3, T_4, T_4 \ + VAG T_2, h2, h2 \ + VAG T_4, h0, h0 \ + VESRLG $26, h2, T_0 \ + VESRLG $26, h0, T_1 \ + VN MOD26, h2, h2 \ + VN MOD26, h0, h0 \ + VAG T_0, h3, h3 \ + VAG T_1, h1, h1 \ + VESRLG $26, h3, T_2 \ + VN MOD26, h3, h3 \ + VAG T_2, h4, h4 + +// expand in0 into d[0] and in1 into d[1] +#define EXPAND(in0, in1, d0, d1, d2, d3, d4) \ + VGBM $0x0707, d1 \ // d1=tmp + VPERM in0, in1, EX2, d4 \ + VPERM in0, in1, EX0, d0 \ + VPERM in0, in1, EX1, d2 \ + VN d1, d4, d4 \ + VESRLG $26, d0, d1 \ + VESRLG $30, d2, d3 \ + VESRLG $4, d2, d2 \ + VN MOD26, d0, d0 \ + VN MOD26, d1, d1 \ + VN MOD26, d2, d2 \ + VN MOD26, d3, d3 + +// pack h4:h0 into h1:h0 (no carry) +#define PACK(h0, h1, h2, h3, h4) \ + VESLG $26, h1, h1 \ + VESLG $26, h3, h3 \ + VO h0, h1, h0 \ + VO h2, h3, h2 \ + VESLG $4, h2, h2 \ + VLEIB $7, $48, h1 \ + VSLB h1, h2, h2 \ + VO h0, h2, h0 \ + VLEIB $7, $104, h1 \ + VSLB h1, h4, h3 \ + VO h3, h0, h0 \ + VLEIB $7, $24, h1 \ + VSRLB h1, h4, h1 + +// if h > 2**130-5 then h -= 2**130-5 +#define MOD(h0, h1, t0, t1, t2) \ + VZERO t0 \ + VLEIG $1, $5, t0 \ + VACCQ h0, t0, t1 \ + VAQ h0, t0, t0 \ + VONE t2 \ + VLEIG $1, $-4, t2 \ + VAQ t2, t1, t1 \ + VACCQ h1, t1, t1 \ + VONE t2 \ + VAQ t2, t1, t1 \ + VN h0, t1, t2 \ + VNC t0, t1, t1 \ + VO t1, t2, h0 + +// func poly1305vx(out *[16]byte, m *byte, mlen uint64, key *[32]key) +TEXT ·poly1305vx(SB), $0-32 + // This code processes up to 2 blocks (32 bytes) per iteration + // using the algorithm described in: + // NEON crypto, Daniel J. Bernstein & Peter Schwabe + // https://cryptojedi.org/papers/neoncrypto-20120320.pdf + LMG out+0(FP), R1, R4 // R1=out, R2=m, R3=mlen, R4=key + + // load MOD26, EX0, EX1 and EX2 + MOVD $·constants<>(SB), R5 + VLM (R5), MOD26, EX2 + + // setup r + VL (R4), T_0 + MOVD $·keyMask<>(SB), R6 + VL (R6), T_1 + VN T_0, T_1, T_0 + EXPAND(T_0, T_0, R_0, R_1, R_2, R_3, R_4) + + // setup r*5 + VLEIG $0, $5, T_0 + VLEIG $1, $5, T_0 + + // store r (for final block) + VMLOF T_0, R_1, R5SAVE_1 + VMLOF T_0, R_2, R5SAVE_2 + VMLOF T_0, R_3, R5SAVE_3 + VMLOF T_0, R_4, R5SAVE_4 + VLGVG $0, R_0, RSAVE_0 + VLGVG $0, R_1, RSAVE_1 + VLGVG $0, R_2, RSAVE_2 + VLGVG $0, R_3, RSAVE_3 + VLGVG $0, R_4, RSAVE_4 + + // skip r**2 calculation + CMPBLE R3, $16, skip + + // calculate r**2 + MULTIPLY(R_0, R_1, R_2, R_3, R_4, R_0, R_1, R_2, R_3, R_4, R5SAVE_1, R5SAVE_2, R5SAVE_3, R5SAVE_4, H_0, H_1, H_2, H_3, H_4) + REDUCE(H_0, H_1, H_2, H_3, H_4) + VLEIG $0, $5, T_0 + VLEIG $1, $5, T_0 + VMLOF T_0, H_1, R5_1 + VMLOF T_0, H_2, R5_2 + VMLOF T_0, H_3, R5_3 + VMLOF T_0, H_4, R5_4 + VLR H_0, R_0 + VLR H_1, R_1 + VLR H_2, R_2 + VLR H_3, R_3 + VLR H_4, R_4 + + // initialize h + VZERO H_0 + VZERO H_1 + VZERO H_2 + VZERO H_3 + VZERO H_4 + +loop: + CMPBLE R3, $32, b2 + VLM (R2), T_0, T_1 + SUB $32, R3 + MOVD $32(R2), R2 + EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4) + VLEIB $4, $1, F_4 + VLEIB $12, $1, F_4 + +multiply: + VAG H_0, F_0, F_0 + VAG H_1, F_1, F_1 + VAG H_2, F_2, F_2 + VAG H_3, F_3, F_3 + VAG H_4, F_4, F_4 + MULTIPLY(F_0, F_1, F_2, F_3, F_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, H_0, H_1, H_2, H_3, H_4) + REDUCE(H_0, H_1, H_2, H_3, H_4) + CMPBNE R3, $0, loop + +finish: + // sum vectors + VZERO T_0 + VSUMQG H_0, T_0, H_0 + VSUMQG H_1, T_0, H_1 + VSUMQG H_2, T_0, H_2 + VSUMQG H_3, T_0, H_3 + VSUMQG H_4, T_0, H_4 + + // h may be >= 2*(2**130-5) so we need to reduce it again + REDUCE(H_0, H_1, H_2, H_3, H_4) + + // carry h1->h4 + VESRLG $26, H_1, T_1 + VN MOD26, H_1, H_1 + VAQ T_1, H_2, H_2 + VESRLG $26, H_2, T_2 + VN MOD26, H_2, H_2 + VAQ T_2, H_3, H_3 + VESRLG $26, H_3, T_3 + VN MOD26, H_3, H_3 + VAQ T_3, H_4, H_4 + + // h is now < 2*(2**130-5) + // pack h into h1 (hi) and h0 (lo) + PACK(H_0, H_1, H_2, H_3, H_4) + + // if h > 2**130-5 then h -= 2**130-5 + MOD(H_0, H_1, T_0, T_1, T_2) + + // h += s + MOVD $·bswapMask<>(SB), R5 + VL (R5), T_1 + VL 16(R4), T_0 + VPERM T_0, T_0, T_1, T_0 // reverse bytes (to big) + VAQ T_0, H_0, H_0 + VPERM H_0, H_0, T_1, H_0 // reverse bytes (to little) + VST H_0, (R1) + + RET + +b2: + CMPBLE R3, $16, b1 + + // 2 blocks remaining + SUB $17, R3 + VL (R2), T_0 + VLL R3, 16(R2), T_1 + ADD $1, R3 + MOVBZ $1, R0 + CMPBEQ R3, $16, 2(PC) + VLVGB R3, R0, T_1 + EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4) + CMPBNE R3, $16, 2(PC) + VLEIB $12, $1, F_4 + VLEIB $4, $1, F_4 + + // setup [r²,r] + VLVGG $1, RSAVE_0, R_0 + VLVGG $1, RSAVE_1, R_1 + VLVGG $1, RSAVE_2, R_2 + VLVGG $1, RSAVE_3, R_3 + VLVGG $1, RSAVE_4, R_4 + VPDI $0, R5_1, R5SAVE_1, R5_1 + VPDI $0, R5_2, R5SAVE_2, R5_2 + VPDI $0, R5_3, R5SAVE_3, R5_3 + VPDI $0, R5_4, R5SAVE_4, R5_4 + + MOVD $0, R3 + BR multiply + +skip: + VZERO H_0 + VZERO H_1 + VZERO H_2 + VZERO H_3 + VZERO H_4 + + CMPBEQ R3, $0, finish + +b1: + // 1 block remaining + SUB $1, R3 + VLL R3, (R2), T_0 + ADD $1, R3 + MOVBZ $1, R0 + CMPBEQ R3, $16, 2(PC) + VLVGB R3, R0, T_0 + VZERO T_1 + EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4) + CMPBNE R3, $16, 2(PC) + VLEIB $4, $1, F_4 + VLEIG $1, $1, R_0 + VZERO R_1 + VZERO R_2 + VZERO R_3 + VZERO R_4 + VZERO R5_1 + VZERO R5_2 + VZERO R5_3 + VZERO R5_4 + + // setup [r, 1] + VLVGG $0, RSAVE_0, R_0 + VLVGG $0, RSAVE_1, R_1 + VLVGG $0, RSAVE_2, R_2 + VLVGG $0, RSAVE_3, R_3 + VLVGG $0, RSAVE_4, R_4 + VPDI $0, R5SAVE_1, R5_1, R5_1 + VPDI $0, R5SAVE_2, R5_2, R5_2 + VPDI $0, R5SAVE_3, R5_3, R5_3 + VPDI $0, R5SAVE_4, R5_4, R5_4 + + MOVD $0, R3 + BR multiply diff --git a/vendor/golang.org/x/crypto/poly1305/sum_vmsl_s390x.s b/vendor/golang.org/x/crypto/poly1305/sum_vmsl_s390x.s new file mode 100644 index 00000000000..e60bbc1d7f8 --- /dev/null +++ b/vendor/golang.org/x/crypto/poly1305/sum_vmsl_s390x.s @@ -0,0 +1,909 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build s390x,go1.11,!gccgo,!appengine + +#include "textflag.h" + +// Implementation of Poly1305 using the vector facility (vx) and the VMSL instruction. + +// constants +#define EX0 V1 +#define EX1 V2 +#define EX2 V3 + +// temporaries +#define T_0 V4 +#define T_1 V5 +#define T_2 V6 +#define T_3 V7 +#define T_4 V8 +#define T_5 V9 +#define T_6 V10 +#define T_7 V11 +#define T_8 V12 +#define T_9 V13 +#define T_10 V14 + +// r**2 & r**4 +#define R_0 V15 +#define R_1 V16 +#define R_2 V17 +#define R5_1 V18 +#define R5_2 V19 +// key (r) +#define RSAVE_0 R7 +#define RSAVE_1 R8 +#define RSAVE_2 R9 +#define R5SAVE_1 R10 +#define R5SAVE_2 R11 + +// message block +#define M0 V20 +#define M1 V21 +#define M2 V22 +#define M3 V23 +#define M4 V24 +#define M5 V25 + +// accumulator +#define H0_0 V26 +#define H1_0 V27 +#define H2_0 V28 +#define H0_1 V29 +#define H1_1 V30 +#define H2_1 V31 + +GLOBL ·keyMask<>(SB), RODATA, $16 +DATA ·keyMask<>+0(SB)/8, $0xffffff0ffcffff0f +DATA ·keyMask<>+8(SB)/8, $0xfcffff0ffcffff0f + +GLOBL ·bswapMask<>(SB), RODATA, $16 +DATA ·bswapMask<>+0(SB)/8, $0x0f0e0d0c0b0a0908 +DATA ·bswapMask<>+8(SB)/8, $0x0706050403020100 + +GLOBL ·constants<>(SB), RODATA, $48 +// EX0 +DATA ·constants<>+0(SB)/8, $0x18191a1b1c1d1e1f +DATA ·constants<>+8(SB)/8, $0x0000050403020100 +// EX1 +DATA ·constants<>+16(SB)/8, $0x18191a1b1c1d1e1f +DATA ·constants<>+24(SB)/8, $0x00000a0908070605 +// EX2 +DATA ·constants<>+32(SB)/8, $0x18191a1b1c1d1e1f +DATA ·constants<>+40(SB)/8, $0x0000000f0e0d0c0b + +GLOBL ·c<>(SB), RODATA, $48 +// EX0 +DATA ·c<>+0(SB)/8, $0x0000050403020100 +DATA ·c<>+8(SB)/8, $0x0000151413121110 +// EX1 +DATA ·c<>+16(SB)/8, $0x00000a0908070605 +DATA ·c<>+24(SB)/8, $0x00001a1918171615 +// EX2 +DATA ·c<>+32(SB)/8, $0x0000000f0e0d0c0b +DATA ·c<>+40(SB)/8, $0x0000001f1e1d1c1b + +GLOBL ·reduce<>(SB), RODATA, $32 +// 44 bit +DATA ·reduce<>+0(SB)/8, $0x0 +DATA ·reduce<>+8(SB)/8, $0xfffffffffff +// 42 bit +DATA ·reduce<>+16(SB)/8, $0x0 +DATA ·reduce<>+24(SB)/8, $0x3ffffffffff + +// h = (f*g) % (2**130-5) [partial reduction] +// uses T_0...T_9 temporary registers +// input: m02_0, m02_1, m02_2, m13_0, m13_1, m13_2, r_0, r_1, r_2, r5_1, r5_2, m4_0, m4_1, m4_2, m5_0, m5_1, m5_2 +// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 +// output: m02_0, m02_1, m02_2, m13_0, m13_1, m13_2 +#define MULTIPLY(m02_0, m02_1, m02_2, m13_0, m13_1, m13_2, r_0, r_1, r_2, r5_1, r5_2, m4_0, m4_1, m4_2, m5_0, m5_1, m5_2, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) \ + \ // Eliminate the dependency for the last 2 VMSLs + VMSLG m02_0, r_2, m4_2, m4_2 \ + VMSLG m13_0, r_2, m5_2, m5_2 \ // 8 VMSLs pipelined + VMSLG m02_0, r_0, m4_0, m4_0 \ + VMSLG m02_1, r5_2, V0, T_0 \ + VMSLG m02_0, r_1, m4_1, m4_1 \ + VMSLG m02_1, r_0, V0, T_1 \ + VMSLG m02_1, r_1, V0, T_2 \ + VMSLG m02_2, r5_1, V0, T_3 \ + VMSLG m02_2, r5_2, V0, T_4 \ + VMSLG m13_0, r_0, m5_0, m5_0 \ + VMSLG m13_1, r5_2, V0, T_5 \ + VMSLG m13_0, r_1, m5_1, m5_1 \ + VMSLG m13_1, r_0, V0, T_6 \ + VMSLG m13_1, r_1, V0, T_7 \ + VMSLG m13_2, r5_1, V0, T_8 \ + VMSLG m13_2, r5_2, V0, T_9 \ + VMSLG m02_2, r_0, m4_2, m4_2 \ + VMSLG m13_2, r_0, m5_2, m5_2 \ + VAQ m4_0, T_0, m02_0 \ + VAQ m4_1, T_1, m02_1 \ + VAQ m5_0, T_5, m13_0 \ + VAQ m5_1, T_6, m13_1 \ + VAQ m02_0, T_3, m02_0 \ + VAQ m02_1, T_4, m02_1 \ + VAQ m13_0, T_8, m13_0 \ + VAQ m13_1, T_9, m13_1 \ + VAQ m4_2, T_2, m02_2 \ + VAQ m5_2, T_7, m13_2 \ + +// SQUARE uses three limbs of r and r_2*5 to output square of r +// uses T_1, T_5 and T_7 temporary registers +// input: r_0, r_1, r_2, r5_2 +// temp: TEMP0, TEMP1, TEMP2 +// output: p0, p1, p2 +#define SQUARE(r_0, r_1, r_2, r5_2, p0, p1, p2, TEMP0, TEMP1, TEMP2) \ + VMSLG r_0, r_0, p0, p0 \ + VMSLG r_1, r5_2, V0, TEMP0 \ + VMSLG r_2, r5_2, p1, p1 \ + VMSLG r_0, r_1, V0, TEMP1 \ + VMSLG r_1, r_1, p2, p2 \ + VMSLG r_0, r_2, V0, TEMP2 \ + VAQ TEMP0, p0, p0 \ + VAQ TEMP1, p1, p1 \ + VAQ TEMP2, p2, p2 \ + VAQ TEMP0, p0, p0 \ + VAQ TEMP1, p1, p1 \ + VAQ TEMP2, p2, p2 \ + +// carry h0->h1->h2->h0 || h3->h4->h5->h3 +// uses T_2, T_4, T_5, T_7, T_8, T_9 +// t6, t7, t8, t9, t10, t11 +// input: h0, h1, h2, h3, h4, h5 +// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 +// output: h0, h1, h2, h3, h4, h5 +#define REDUCE(h0, h1, h2, h3, h4, h5, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) \ + VLM (R12), t6, t7 \ // 44 and 42 bit clear mask + VLEIB $7, $0x28, t10 \ // 5 byte shift mask + VREPIB $4, t8 \ // 4 bit shift mask + VREPIB $2, t11 \ // 2 bit shift mask + VSRLB t10, h0, t0 \ // h0 byte shift + VSRLB t10, h1, t1 \ // h1 byte shift + VSRLB t10, h2, t2 \ // h2 byte shift + VSRLB t10, h3, t3 \ // h3 byte shift + VSRLB t10, h4, t4 \ // h4 byte shift + VSRLB t10, h5, t5 \ // h5 byte shift + VSRL t8, t0, t0 \ // h0 bit shift + VSRL t8, t1, t1 \ // h2 bit shift + VSRL t11, t2, t2 \ // h2 bit shift + VSRL t8, t3, t3 \ // h3 bit shift + VSRL t8, t4, t4 \ // h4 bit shift + VESLG $2, t2, t9 \ // h2 carry x5 + VSRL t11, t5, t5 \ // h5 bit shift + VN t6, h0, h0 \ // h0 clear carry + VAQ t2, t9, t2 \ // h2 carry x5 + VESLG $2, t5, t9 \ // h5 carry x5 + VN t6, h1, h1 \ // h1 clear carry + VN t7, h2, h2 \ // h2 clear carry + VAQ t5, t9, t5 \ // h5 carry x5 + VN t6, h3, h3 \ // h3 clear carry + VN t6, h4, h4 \ // h4 clear carry + VN t7, h5, h5 \ // h5 clear carry + VAQ t0, h1, h1 \ // h0->h1 + VAQ t3, h4, h4 \ // h3->h4 + VAQ t1, h2, h2 \ // h1->h2 + VAQ t4, h5, h5 \ // h4->h5 + VAQ t2, h0, h0 \ // h2->h0 + VAQ t5, h3, h3 \ // h5->h3 + VREPG $1, t6, t6 \ // 44 and 42 bit masks across both halves + VREPG $1, t7, t7 \ + VSLDB $8, h0, h0, h0 \ // set up [h0/1/2, h3/4/5] + VSLDB $8, h1, h1, h1 \ + VSLDB $8, h2, h2, h2 \ + VO h0, h3, h3 \ + VO h1, h4, h4 \ + VO h2, h5, h5 \ + VESRLG $44, h3, t0 \ // 44 bit shift right + VESRLG $44, h4, t1 \ + VESRLG $42, h5, t2 \ + VN t6, h3, h3 \ // clear carry bits + VN t6, h4, h4 \ + VN t7, h5, h5 \ + VESLG $2, t2, t9 \ // multiply carry by 5 + VAQ t9, t2, t2 \ + VAQ t0, h4, h4 \ + VAQ t1, h5, h5 \ + VAQ t2, h3, h3 \ + +// carry h0->h1->h2->h0 +// input: h0, h1, h2 +// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8 +// output: h0, h1, h2 +#define REDUCE2(h0, h1, h2, t0, t1, t2, t3, t4, t5, t6, t7, t8) \ + VLEIB $7, $0x28, t3 \ // 5 byte shift mask + VREPIB $4, t4 \ // 4 bit shift mask + VREPIB $2, t7 \ // 2 bit shift mask + VGBM $0x003F, t5 \ // mask to clear carry bits + VSRLB t3, h0, t0 \ + VSRLB t3, h1, t1 \ + VSRLB t3, h2, t2 \ + VESRLG $4, t5, t5 \ // 44 bit clear mask + VSRL t4, t0, t0 \ + VSRL t4, t1, t1 \ + VSRL t7, t2, t2 \ + VESRLG $2, t5, t6 \ // 42 bit clear mask + VESLG $2, t2, t8 \ + VAQ t8, t2, t2 \ + VN t5, h0, h0 \ + VN t5, h1, h1 \ + VN t6, h2, h2 \ + VAQ t0, h1, h1 \ + VAQ t1, h2, h2 \ + VAQ t2, h0, h0 \ + VSRLB t3, h0, t0 \ + VSRLB t3, h1, t1 \ + VSRLB t3, h2, t2 \ + VSRL t4, t0, t0 \ + VSRL t4, t1, t1 \ + VSRL t7, t2, t2 \ + VN t5, h0, h0 \ + VN t5, h1, h1 \ + VESLG $2, t2, t8 \ + VN t6, h2, h2 \ + VAQ t0, h1, h1 \ + VAQ t8, t2, t2 \ + VAQ t1, h2, h2 \ + VAQ t2, h0, h0 \ + +// expands two message blocks into the lower halfs of the d registers +// moves the contents of the d registers into upper halfs +// input: in1, in2, d0, d1, d2, d3, d4, d5 +// temp: TEMP0, TEMP1, TEMP2, TEMP3 +// output: d0, d1, d2, d3, d4, d5 +#define EXPACC(in1, in2, d0, d1, d2, d3, d4, d5, TEMP0, TEMP1, TEMP2, TEMP3) \ + VGBM $0xff3f, TEMP0 \ + VGBM $0xff1f, TEMP1 \ + VESLG $4, d1, TEMP2 \ + VESLG $4, d4, TEMP3 \ + VESRLG $4, TEMP0, TEMP0 \ + VPERM in1, d0, EX0, d0 \ + VPERM in2, d3, EX0, d3 \ + VPERM in1, d2, EX2, d2 \ + VPERM in2, d5, EX2, d5 \ + VPERM in1, TEMP2, EX1, d1 \ + VPERM in2, TEMP3, EX1, d4 \ + VN TEMP0, d0, d0 \ + VN TEMP0, d3, d3 \ + VESRLG $4, d1, d1 \ + VESRLG $4, d4, d4 \ + VN TEMP1, d2, d2 \ + VN TEMP1, d5, d5 \ + VN TEMP0, d1, d1 \ + VN TEMP0, d4, d4 \ + +// expands one message block into the lower halfs of the d registers +// moves the contents of the d registers into upper halfs +// input: in, d0, d1, d2 +// temp: TEMP0, TEMP1, TEMP2 +// output: d0, d1, d2 +#define EXPACC2(in, d0, d1, d2, TEMP0, TEMP1, TEMP2) \ + VGBM $0xff3f, TEMP0 \ + VESLG $4, d1, TEMP2 \ + VGBM $0xff1f, TEMP1 \ + VPERM in, d0, EX0, d0 \ + VESRLG $4, TEMP0, TEMP0 \ + VPERM in, d2, EX2, d2 \ + VPERM in, TEMP2, EX1, d1 \ + VN TEMP0, d0, d0 \ + VN TEMP1, d2, d2 \ + VESRLG $4, d1, d1 \ + VN TEMP0, d1, d1 \ + +// pack h2:h0 into h1:h0 (no carry) +// input: h0, h1, h2 +// output: h0, h1, h2 +#define PACK(h0, h1, h2) \ + VMRLG h1, h2, h2 \ // copy h1 to upper half h2 + VESLG $44, h1, h1 \ // shift limb 1 44 bits, leaving 20 + VO h0, h1, h0 \ // combine h0 with 20 bits from limb 1 + VESRLG $20, h2, h1 \ // put top 24 bits of limb 1 into h1 + VLEIG $1, $0, h1 \ // clear h2 stuff from lower half of h1 + VO h0, h1, h0 \ // h0 now has 88 bits (limb 0 and 1) + VLEIG $0, $0, h2 \ // clear upper half of h2 + VESRLG $40, h2, h1 \ // h1 now has upper two bits of result + VLEIB $7, $88, h1 \ // for byte shift (11 bytes) + VSLB h1, h2, h2 \ // shift h2 11 bytes to the left + VO h0, h2, h0 \ // combine h0 with 20 bits from limb 1 + VLEIG $0, $0, h1 \ // clear upper half of h1 + +// if h > 2**130-5 then h -= 2**130-5 +// input: h0, h1 +// temp: t0, t1, t2 +// output: h0 +#define MOD(h0, h1, t0, t1, t2) \ + VZERO t0 \ + VLEIG $1, $5, t0 \ + VACCQ h0, t0, t1 \ + VAQ h0, t0, t0 \ + VONE t2 \ + VLEIG $1, $-4, t2 \ + VAQ t2, t1, t1 \ + VACCQ h1, t1, t1 \ + VONE t2 \ + VAQ t2, t1, t1 \ + VN h0, t1, t2 \ + VNC t0, t1, t1 \ + VO t1, t2, h0 \ + +// func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]key) +TEXT ·poly1305vmsl(SB), $0-32 + // This code processes 6 + up to 4 blocks (32 bytes) per iteration + // using the algorithm described in: + // NEON crypto, Daniel J. Bernstein & Peter Schwabe + // https://cryptojedi.org/papers/neoncrypto-20120320.pdf + // And as moddified for VMSL as described in + // Accelerating Poly1305 Cryptographic Message Authentication on the z14 + // O'Farrell et al, CASCON 2017, p48-55 + // https://ibm.ent.box.com/s/jf9gedj0e9d2vjctfyh186shaztavnht + + LMG out+0(FP), R1, R4 // R1=out, R2=m, R3=mlen, R4=key + VZERO V0 // c + + // load EX0, EX1 and EX2 + MOVD $·constants<>(SB), R5 + VLM (R5), EX0, EX2 // c + + // setup r + VL (R4), T_0 + MOVD $·keyMask<>(SB), R6 + VL (R6), T_1 + VN T_0, T_1, T_0 + VZERO T_2 // limbs for r + VZERO T_3 + VZERO T_4 + EXPACC2(T_0, T_2, T_3, T_4, T_1, T_5, T_7) + + // T_2, T_3, T_4: [0, r] + + // setup r*20 + VLEIG $0, $0, T_0 + VLEIG $1, $20, T_0 // T_0: [0, 20] + VZERO T_5 + VZERO T_6 + VMSLG T_0, T_3, T_5, T_5 + VMSLG T_0, T_4, T_6, T_6 + + // store r for final block in GR + VLGVG $1, T_2, RSAVE_0 // c + VLGVG $1, T_3, RSAVE_1 // c + VLGVG $1, T_4, RSAVE_2 // c + VLGVG $1, T_5, R5SAVE_1 // c + VLGVG $1, T_6, R5SAVE_2 // c + + // initialize h + VZERO H0_0 + VZERO H1_0 + VZERO H2_0 + VZERO H0_1 + VZERO H1_1 + VZERO H2_1 + + // initialize pointer for reduce constants + MOVD $·reduce<>(SB), R12 + + // calculate r**2 and 20*(r**2) + VZERO R_0 + VZERO R_1 + VZERO R_2 + SQUARE(T_2, T_3, T_4, T_6, R_0, R_1, R_2, T_1, T_5, T_7) + REDUCE2(R_0, R_1, R_2, M0, M1, M2, M3, M4, R5_1, R5_2, M5, T_1) + VZERO R5_1 + VZERO R5_2 + VMSLG T_0, R_1, R5_1, R5_1 + VMSLG T_0, R_2, R5_2, R5_2 + + // skip r**4 calculation if 3 blocks or less + CMPBLE R3, $48, b4 + + // calculate r**4 and 20*(r**4) + VZERO T_8 + VZERO T_9 + VZERO T_10 + SQUARE(R_0, R_1, R_2, R5_2, T_8, T_9, T_10, T_1, T_5, T_7) + REDUCE2(T_8, T_9, T_10, M0, M1, M2, M3, M4, T_2, T_3, M5, T_1) + VZERO T_2 + VZERO T_3 + VMSLG T_0, T_9, T_2, T_2 + VMSLG T_0, T_10, T_3, T_3 + + // put r**2 to the right and r**4 to the left of R_0, R_1, R_2 + VSLDB $8, T_8, T_8, T_8 + VSLDB $8, T_9, T_9, T_9 + VSLDB $8, T_10, T_10, T_10 + VSLDB $8, T_2, T_2, T_2 + VSLDB $8, T_3, T_3, T_3 + + VO T_8, R_0, R_0 + VO T_9, R_1, R_1 + VO T_10, R_2, R_2 + VO T_2, R5_1, R5_1 + VO T_3, R5_2, R5_2 + + CMPBLE R3, $80, load // less than or equal to 5 blocks in message + + // 6(or 5+1) blocks + SUB $81, R3 + VLM (R2), M0, M4 + VLL R3, 80(R2), M5 + ADD $1, R3 + MOVBZ $1, R0 + CMPBGE R3, $16, 2(PC) + VLVGB R3, R0, M5 + MOVD $96(R2), R2 + EXPACC(M0, M1, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3) + EXPACC(M2, M3, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3) + VLEIB $2, $1, H2_0 + VLEIB $2, $1, H2_1 + VLEIB $10, $1, H2_0 + VLEIB $10, $1, H2_1 + + VZERO M0 + VZERO M1 + VZERO M2 + VZERO M3 + VZERO T_4 + VZERO T_10 + EXPACC(M4, M5, M0, M1, M2, M3, T_4, T_10, T_0, T_1, T_2, T_3) + VLR T_4, M4 + VLEIB $10, $1, M2 + CMPBLT R3, $16, 2(PC) + VLEIB $10, $1, T_10 + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M2, M3, M4, T_4, T_5, T_2, T_7, T_8, T_9) + VMRHG V0, H0_1, H0_0 + VMRHG V0, H1_1, H1_0 + VMRHG V0, H2_1, H2_0 + VMRLG V0, H0_1, H0_1 + VMRLG V0, H1_1, H1_1 + VMRLG V0, H2_1, H2_1 + + SUB $16, R3 + CMPBLE R3, $0, square + +load: + // load EX0, EX1 and EX2 + MOVD $·c<>(SB), R5 + VLM (R5), EX0, EX2 + +loop: + CMPBLE R3, $64, add // b4 // last 4 or less blocks left + + // next 4 full blocks + VLM (R2), M2, M5 + SUB $64, R3 + MOVD $64(R2), R2 + REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, T_0, T_1, T_3, T_4, T_5, T_2, T_7, T_8, T_9) + + // expacc in-lined to create [m2, m3] limbs + VGBM $0x3f3f, T_0 // 44 bit clear mask + VGBM $0x1f1f, T_1 // 40 bit clear mask + VPERM M2, M3, EX0, T_3 + VESRLG $4, T_0, T_0 // 44 bit clear mask ready + VPERM M2, M3, EX1, T_4 + VPERM M2, M3, EX2, T_5 + VN T_0, T_3, T_3 + VESRLG $4, T_4, T_4 + VN T_1, T_5, T_5 + VN T_0, T_4, T_4 + VMRHG H0_1, T_3, H0_0 + VMRHG H1_1, T_4, H1_0 + VMRHG H2_1, T_5, H2_0 + VMRLG H0_1, T_3, H0_1 + VMRLG H1_1, T_4, H1_1 + VMRLG H2_1, T_5, H2_1 + VLEIB $10, $1, H2_0 + VLEIB $10, $1, H2_1 + VPERM M4, M5, EX0, T_3 + VPERM M4, M5, EX1, T_4 + VPERM M4, M5, EX2, T_5 + VN T_0, T_3, T_3 + VESRLG $4, T_4, T_4 + VN T_1, T_5, T_5 + VN T_0, T_4, T_4 + VMRHG V0, T_3, M0 + VMRHG V0, T_4, M1 + VMRHG V0, T_5, M2 + VMRLG V0, T_3, M3 + VMRLG V0, T_4, M4 + VMRLG V0, T_5, M5 + VLEIB $10, $1, M2 + VLEIB $10, $1, M5 + + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + CMPBNE R3, $0, loop + REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9) + VMRHG V0, H0_1, H0_0 + VMRHG V0, H1_1, H1_0 + VMRHG V0, H2_1, H2_0 + VMRLG V0, H0_1, H0_1 + VMRLG V0, H1_1, H1_1 + VMRLG V0, H2_1, H2_1 + + // load EX0, EX1, EX2 + MOVD $·constants<>(SB), R5 + VLM (R5), EX0, EX2 + + // sum vectors + VAQ H0_0, H0_1, H0_0 + VAQ H1_0, H1_1, H1_0 + VAQ H2_0, H2_1, H2_0 + + // h may be >= 2*(2**130-5) so we need to reduce it again + // M0...M4 are used as temps here + REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5) + +next: // carry h1->h2 + VLEIB $7, $0x28, T_1 + VREPIB $4, T_2 + VGBM $0x003F, T_3 + VESRLG $4, T_3 + + // byte shift + VSRLB T_1, H1_0, T_4 + + // bit shift + VSRL T_2, T_4, T_4 + + // clear h1 carry bits + VN T_3, H1_0, H1_0 + + // add carry + VAQ T_4, H2_0, H2_0 + + // h is now < 2*(2**130-5) + // pack h into h1 (hi) and h0 (lo) + PACK(H0_0, H1_0, H2_0) + + // if h > 2**130-5 then h -= 2**130-5 + MOD(H0_0, H1_0, T_0, T_1, T_2) + + // h += s + MOVD $·bswapMask<>(SB), R5 + VL (R5), T_1 + VL 16(R4), T_0 + VPERM T_0, T_0, T_1, T_0 // reverse bytes (to big) + VAQ T_0, H0_0, H0_0 + VPERM H0_0, H0_0, T_1, H0_0 // reverse bytes (to little) + VST H0_0, (R1) + RET + +add: + // load EX0, EX1, EX2 + MOVD $·constants<>(SB), R5 + VLM (R5), EX0, EX2 + + REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9) + VMRHG V0, H0_1, H0_0 + VMRHG V0, H1_1, H1_0 + VMRHG V0, H2_1, H2_0 + VMRLG V0, H0_1, H0_1 + VMRLG V0, H1_1, H1_1 + VMRLG V0, H2_1, H2_1 + CMPBLE R3, $64, b4 + +b4: + CMPBLE R3, $48, b3 // 3 blocks or less + + // 4(3+1) blocks remaining + SUB $49, R3 + VLM (R2), M0, M2 + VLL R3, 48(R2), M3 + ADD $1, R3 + MOVBZ $1, R0 + CMPBEQ R3, $16, 2(PC) + VLVGB R3, R0, M3 + MOVD $64(R2), R2 + EXPACC(M0, M1, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3) + VLEIB $10, $1, H2_0 + VLEIB $10, $1, H2_1 + VZERO M0 + VZERO M1 + VZERO M4 + VZERO M5 + VZERO T_4 + VZERO T_10 + EXPACC(M2, M3, M0, M1, M4, M5, T_4, T_10, T_0, T_1, T_2, T_3) + VLR T_4, M2 + VLEIB $10, $1, M4 + CMPBNE R3, $16, 2(PC) + VLEIB $10, $1, T_10 + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M4, M5, M2, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9) + VMRHG V0, H0_1, H0_0 + VMRHG V0, H1_1, H1_0 + VMRHG V0, H2_1, H2_0 + VMRLG V0, H0_1, H0_1 + VMRLG V0, H1_1, H1_1 + VMRLG V0, H2_1, H2_1 + SUB $16, R3 + CMPBLE R3, $0, square // this condition must always hold true! + +b3: + CMPBLE R3, $32, b2 + + // 3 blocks remaining + + // setup [r²,r] + VSLDB $8, R_0, R_0, R_0 + VSLDB $8, R_1, R_1, R_1 + VSLDB $8, R_2, R_2, R_2 + VSLDB $8, R5_1, R5_1, R5_1 + VSLDB $8, R5_2, R5_2, R5_2 + + VLVGG $1, RSAVE_0, R_0 + VLVGG $1, RSAVE_1, R_1 + VLVGG $1, RSAVE_2, R_2 + VLVGG $1, R5SAVE_1, R5_1 + VLVGG $1, R5SAVE_2, R5_2 + + // setup [h0, h1] + VSLDB $8, H0_0, H0_0, H0_0 + VSLDB $8, H1_0, H1_0, H1_0 + VSLDB $8, H2_0, H2_0, H2_0 + VO H0_1, H0_0, H0_0 + VO H1_1, H1_0, H1_0 + VO H2_1, H2_0, H2_0 + VZERO H0_1 + VZERO H1_1 + VZERO H2_1 + + VZERO M0 + VZERO M1 + VZERO M2 + VZERO M3 + VZERO M4 + VZERO M5 + + // H*[r**2, r] + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, H0_1, H1_1, T_10, M5) + + SUB $33, R3 + VLM (R2), M0, M1 + VLL R3, 32(R2), M2 + ADD $1, R3 + MOVBZ $1, R0 + CMPBEQ R3, $16, 2(PC) + VLVGB R3, R0, M2 + + // H += m0 + VZERO T_1 + VZERO T_2 + VZERO T_3 + EXPACC2(M0, T_1, T_2, T_3, T_4, T_5, T_6) + VLEIB $10, $1, T_3 + VAG H0_0, T_1, H0_0 + VAG H1_0, T_2, H1_0 + VAG H2_0, T_3, H2_0 + + VZERO M0 + VZERO M3 + VZERO M4 + VZERO M5 + VZERO T_10 + + // (H+m0)*r + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M3, M4, M5, V0, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE2(H0_0, H1_0, H2_0, M0, M3, M4, M5, T_10, H0_1, H1_1, H2_1, T_9) + + // H += m1 + VZERO V0 + VZERO T_1 + VZERO T_2 + VZERO T_3 + EXPACC2(M1, T_1, T_2, T_3, T_4, T_5, T_6) + VLEIB $10, $1, T_3 + VAQ H0_0, T_1, H0_0 + VAQ H1_0, T_2, H1_0 + VAQ H2_0, T_3, H2_0 + REDUCE2(H0_0, H1_0, H2_0, M0, M3, M4, M5, T_9, H0_1, H1_1, H2_1, T_10) + + // [H, m2] * [r**2, r] + EXPACC2(M2, H0_0, H1_0, H2_0, T_1, T_2, T_3) + CMPBNE R3, $16, 2(PC) + VLEIB $10, $1, H2_0 + VZERO M0 + VZERO M1 + VZERO M2 + VZERO M3 + VZERO M4 + VZERO M5 + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, H0_1, H1_1, M5, T_10) + SUB $16, R3 + CMPBLE R3, $0, next // this condition must always hold true! + +b2: + CMPBLE R3, $16, b1 + + // 2 blocks remaining + + // setup [r²,r] + VSLDB $8, R_0, R_0, R_0 + VSLDB $8, R_1, R_1, R_1 + VSLDB $8, R_2, R_2, R_2 + VSLDB $8, R5_1, R5_1, R5_1 + VSLDB $8, R5_2, R5_2, R5_2 + + VLVGG $1, RSAVE_0, R_0 + VLVGG $1, RSAVE_1, R_1 + VLVGG $1, RSAVE_2, R_2 + VLVGG $1, R5SAVE_1, R5_1 + VLVGG $1, R5SAVE_2, R5_2 + + // setup [h0, h1] + VSLDB $8, H0_0, H0_0, H0_0 + VSLDB $8, H1_0, H1_0, H1_0 + VSLDB $8, H2_0, H2_0, H2_0 + VO H0_1, H0_0, H0_0 + VO H1_1, H1_0, H1_0 + VO H2_1, H2_0, H2_0 + VZERO H0_1 + VZERO H1_1 + VZERO H2_1 + + VZERO M0 + VZERO M1 + VZERO M2 + VZERO M3 + VZERO M4 + VZERO M5 + + // H*[r**2, r] + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M2, M3, M4, T_4, T_5, T_2, T_7, T_8, T_9) + VMRHG V0, H0_1, H0_0 + VMRHG V0, H1_1, H1_0 + VMRHG V0, H2_1, H2_0 + VMRLG V0, H0_1, H0_1 + VMRLG V0, H1_1, H1_1 + VMRLG V0, H2_1, H2_1 + + // move h to the left and 0s at the right + VSLDB $8, H0_0, H0_0, H0_0 + VSLDB $8, H1_0, H1_0, H1_0 + VSLDB $8, H2_0, H2_0, H2_0 + + // get message blocks and append 1 to start + SUB $17, R3 + VL (R2), M0 + VLL R3, 16(R2), M1 + ADD $1, R3 + MOVBZ $1, R0 + CMPBEQ R3, $16, 2(PC) + VLVGB R3, R0, M1 + VZERO T_6 + VZERO T_7 + VZERO T_8 + EXPACC2(M0, T_6, T_7, T_8, T_1, T_2, T_3) + EXPACC2(M1, T_6, T_7, T_8, T_1, T_2, T_3) + VLEIB $2, $1, T_8 + CMPBNE R3, $16, 2(PC) + VLEIB $10, $1, T_8 + + // add [m0, m1] to h + VAG H0_0, T_6, H0_0 + VAG H1_0, T_7, H1_0 + VAG H2_0, T_8, H2_0 + + VZERO M2 + VZERO M3 + VZERO M4 + VZERO M5 + VZERO T_10 + VZERO M0 + + // at this point R_0 .. R5_2 look like [r**2, r] + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M2, M3, M4, M5, T_10, M0, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE2(H0_0, H1_0, H2_0, M2, M3, M4, M5, T_9, H0_1, H1_1, H2_1, T_10) + SUB $16, R3, R3 + CMPBLE R3, $0, next + +b1: + CMPBLE R3, $0, next + + // 1 block remaining + + // setup [r²,r] + VSLDB $8, R_0, R_0, R_0 + VSLDB $8, R_1, R_1, R_1 + VSLDB $8, R_2, R_2, R_2 + VSLDB $8, R5_1, R5_1, R5_1 + VSLDB $8, R5_2, R5_2, R5_2 + + VLVGG $1, RSAVE_0, R_0 + VLVGG $1, RSAVE_1, R_1 + VLVGG $1, RSAVE_2, R_2 + VLVGG $1, R5SAVE_1, R5_1 + VLVGG $1, R5SAVE_2, R5_2 + + // setup [h0, h1] + VSLDB $8, H0_0, H0_0, H0_0 + VSLDB $8, H1_0, H1_0, H1_0 + VSLDB $8, H2_0, H2_0, H2_0 + VO H0_1, H0_0, H0_0 + VO H1_1, H1_0, H1_0 + VO H2_1, H2_0, H2_0 + VZERO H0_1 + VZERO H1_1 + VZERO H2_1 + + VZERO M0 + VZERO M1 + VZERO M2 + VZERO M3 + VZERO M4 + VZERO M5 + + // H*[r**2, r] + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5) + + // set up [0, m0] limbs + SUB $1, R3 + VLL R3, (R2), M0 + ADD $1, R3 + MOVBZ $1, R0 + CMPBEQ R3, $16, 2(PC) + VLVGB R3, R0, M0 + VZERO T_1 + VZERO T_2 + VZERO T_3 + EXPACC2(M0, T_1, T_2, T_3, T_4, T_5, T_6)// limbs: [0, m] + CMPBNE R3, $16, 2(PC) + VLEIB $10, $1, T_3 + + // h+m0 + VAQ H0_0, T_1, H0_0 + VAQ H1_0, T_2, H1_0 + VAQ H2_0, T_3, H2_0 + + VZERO M0 + VZERO M1 + VZERO M2 + VZERO M3 + VZERO M4 + VZERO M5 + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5) + + BR next + +square: + // setup [r²,r] + VSLDB $8, R_0, R_0, R_0 + VSLDB $8, R_1, R_1, R_1 + VSLDB $8, R_2, R_2, R_2 + VSLDB $8, R5_1, R5_1, R5_1 + VSLDB $8, R5_2, R5_2, R5_2 + + VLVGG $1, RSAVE_0, R_0 + VLVGG $1, RSAVE_1, R_1 + VLVGG $1, RSAVE_2, R_2 + VLVGG $1, R5SAVE_1, R5_1 + VLVGG $1, R5SAVE_2, R5_2 + + // setup [h0, h1] + VSLDB $8, H0_0, H0_0, H0_0 + VSLDB $8, H1_0, H1_0, H1_0 + VSLDB $8, H2_0, H2_0, H2_0 + VO H0_1, H0_0, H0_0 + VO H1_1, H1_0, H1_0 + VO H2_1, H2_0, H2_0 + VZERO H0_1 + VZERO H1_1 + VZERO H2_1 + + VZERO M0 + VZERO M1 + VZERO M2 + VZERO M3 + VZERO M4 + VZERO M5 + + // (h0*r**2) + (h1*r) + MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9) + REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5) + BR next diff --git a/vendor/golang.org/x/crypto/ssh/buffer.go b/vendor/golang.org/x/crypto/ssh/buffer.go new file mode 100644 index 00000000000..1ab07d078db --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/buffer.go @@ -0,0 +1,97 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "io" + "sync" +) + +// buffer provides a linked list buffer for data exchange +// between producer and consumer. Theoretically the buffer is +// of unlimited capacity as it does no allocation of its own. +type buffer struct { + // protects concurrent access to head, tail and closed + *sync.Cond + + head *element // the buffer that will be read first + tail *element // the buffer that will be read last + + closed bool +} + +// An element represents a single link in a linked list. +type element struct { + buf []byte + next *element +} + +// newBuffer returns an empty buffer that is not closed. +func newBuffer() *buffer { + e := new(element) + b := &buffer{ + Cond: newCond(), + head: e, + tail: e, + } + return b +} + +// write makes buf available for Read to receive. +// buf must not be modified after the call to write. +func (b *buffer) write(buf []byte) { + b.Cond.L.Lock() + e := &element{buf: buf} + b.tail.next = e + b.tail = e + b.Cond.Signal() + b.Cond.L.Unlock() +} + +// eof closes the buffer. Reads from the buffer once all +// the data has been consumed will receive io.EOF. +func (b *buffer) eof() { + b.Cond.L.Lock() + b.closed = true + b.Cond.Signal() + b.Cond.L.Unlock() +} + +// Read reads data from the internal buffer in buf. Reads will block +// if no data is available, or until the buffer is closed. +func (b *buffer) Read(buf []byte) (n int, err error) { + b.Cond.L.Lock() + defer b.Cond.L.Unlock() + + for len(buf) > 0 { + // if there is data in b.head, copy it + if len(b.head.buf) > 0 { + r := copy(buf, b.head.buf) + buf, b.head.buf = buf[r:], b.head.buf[r:] + n += r + continue + } + // if there is a next buffer, make it the head + if len(b.head.buf) == 0 && b.head != b.tail { + b.head = b.head.next + continue + } + + // if at least one byte has been copied, return + if n > 0 { + break + } + + // if nothing was read, and there is nothing outstanding + // check to see if the buffer is closed. + if b.closed { + err = io.EOF + break + } + // out of buffers, wait for producer + b.Cond.Wait() + } + return +} diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go new file mode 100644 index 00000000000..0f89aec1c7f --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -0,0 +1,546 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + "sort" + "time" +) + +// These constants from [PROTOCOL.certkeys] represent the algorithm names +// for certificate types supported by this package. +const ( + CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" + CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" + CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" + CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" + CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" + CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" + CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com" + CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com" +) + +// Certificate types distinguish between host and user +// certificates. The values can be set in the CertType field of +// Certificate. +const ( + UserCert = 1 + HostCert = 2 +) + +// Signature represents a cryptographic signature. +type Signature struct { + Format string + Blob []byte + Rest []byte `ssh:"rest"` +} + +// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that +// a certificate does not expire. +const CertTimeInfinity = 1<<64 - 1 + +// An Certificate represents an OpenSSH certificate as defined in +// [PROTOCOL.certkeys]?rev=1.8. The Certificate type implements the +// PublicKey interface, so it can be unmarshaled using +// ParsePublicKey. +type Certificate struct { + Nonce []byte + Key PublicKey + Serial uint64 + CertType uint32 + KeyId string + ValidPrincipals []string + ValidAfter uint64 + ValidBefore uint64 + Permissions + Reserved []byte + SignatureKey PublicKey + Signature *Signature +} + +// genericCertData holds the key-independent part of the certificate data. +// Overall, certificates contain an nonce, public key fields and +// key-independent fields. +type genericCertData struct { + Serial uint64 + CertType uint32 + KeyId string + ValidPrincipals []byte + ValidAfter uint64 + ValidBefore uint64 + CriticalOptions []byte + Extensions []byte + Reserved []byte + SignatureKey []byte + Signature []byte +} + +func marshalStringList(namelist []string) []byte { + var to []byte + for _, name := range namelist { + s := struct{ N string }{name} + to = append(to, Marshal(&s)...) + } + return to +} + +type optionsTuple struct { + Key string + Value []byte +} + +type optionsTupleValue struct { + Value string +} + +// serialize a map of critical options or extensions +// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, +// we need two length prefixes for a non-empty string value +func marshalTuples(tups map[string]string) []byte { + keys := make([]string, 0, len(tups)) + for key := range tups { + keys = append(keys, key) + } + sort.Strings(keys) + + var ret []byte + for _, key := range keys { + s := optionsTuple{Key: key} + if value := tups[key]; len(value) > 0 { + s.Value = Marshal(&optionsTupleValue{value}) + } + ret = append(ret, Marshal(&s)...) + } + return ret +} + +// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, +// we need two length prefixes for a non-empty option value +func parseTuples(in []byte) (map[string]string, error) { + tups := map[string]string{} + var lastKey string + var haveLastKey bool + + for len(in) > 0 { + var key, val, extra []byte + var ok bool + + if key, in, ok = parseString(in); !ok { + return nil, errShortRead + } + keyStr := string(key) + // according to [PROTOCOL.certkeys], the names must be in + // lexical order. + if haveLastKey && keyStr <= lastKey { + return nil, fmt.Errorf("ssh: certificate options are not in lexical order") + } + lastKey, haveLastKey = keyStr, true + // the next field is a data field, which if non-empty has a string embedded + if val, in, ok = parseString(in); !ok { + return nil, errShortRead + } + if len(val) > 0 { + val, extra, ok = parseString(val) + if !ok { + return nil, errShortRead + } + if len(extra) > 0 { + return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value") + } + tups[keyStr] = string(val) + } else { + tups[keyStr] = "" + } + } + return tups, nil +} + +func parseCert(in []byte, privAlgo string) (*Certificate, error) { + nonce, rest, ok := parseString(in) + if !ok { + return nil, errShortRead + } + + key, rest, err := parsePubKey(rest, privAlgo) + if err != nil { + return nil, err + } + + var g genericCertData + if err := Unmarshal(rest, &g); err != nil { + return nil, err + } + + c := &Certificate{ + Nonce: nonce, + Key: key, + Serial: g.Serial, + CertType: g.CertType, + KeyId: g.KeyId, + ValidAfter: g.ValidAfter, + ValidBefore: g.ValidBefore, + } + + for principals := g.ValidPrincipals; len(principals) > 0; { + principal, rest, ok := parseString(principals) + if !ok { + return nil, errShortRead + } + c.ValidPrincipals = append(c.ValidPrincipals, string(principal)) + principals = rest + } + + c.CriticalOptions, err = parseTuples(g.CriticalOptions) + if err != nil { + return nil, err + } + c.Extensions, err = parseTuples(g.Extensions) + if err != nil { + return nil, err + } + c.Reserved = g.Reserved + k, err := ParsePublicKey(g.SignatureKey) + if err != nil { + return nil, err + } + + c.SignatureKey = k + c.Signature, rest, ok = parseSignatureBody(g.Signature) + if !ok || len(rest) > 0 { + return nil, errors.New("ssh: signature parse error") + } + + return c, nil +} + +type openSSHCertSigner struct { + pub *Certificate + signer Signer +} + +type algorithmOpenSSHCertSigner struct { + *openSSHCertSigner + algorithmSigner AlgorithmSigner +} + +// NewCertSigner returns a Signer that signs with the given Certificate, whose +// private key is held by signer. It returns an error if the public key in cert +// doesn't match the key used by signer. +func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { + if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { + return nil, errors.New("ssh: signer and cert have different public key") + } + + if algorithmSigner, ok := signer.(AlgorithmSigner); ok { + return &algorithmOpenSSHCertSigner{ + &openSSHCertSigner{cert, signer}, algorithmSigner}, nil + } else { + return &openSSHCertSigner{cert, signer}, nil + } +} + +func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { + return s.signer.Sign(rand, data) +} + +func (s *openSSHCertSigner) PublicKey() PublicKey { + return s.pub +} + +func (s *algorithmOpenSSHCertSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { + return s.algorithmSigner.SignWithAlgorithm(rand, data, algorithm) +} + +const sourceAddressCriticalOption = "source-address" + +// CertChecker does the work of verifying a certificate. Its methods +// can be plugged into ClientConfig.HostKeyCallback and +// ServerConfig.PublicKeyCallback. For the CertChecker to work, +// minimally, the IsAuthority callback should be set. +type CertChecker struct { + // SupportedCriticalOptions lists the CriticalOptions that the + // server application layer understands. These are only used + // for user certificates. + SupportedCriticalOptions []string + + // IsUserAuthority should return true if the key is recognized as an + // authority for the given user certificate. This allows for + // certificates to be signed by other certificates. This must be set + // if this CertChecker will be checking user certificates. + IsUserAuthority func(auth PublicKey) bool + + // IsHostAuthority should report whether the key is recognized as + // an authority for this host. This allows for certificates to be + // signed by other keys, and for those other keys to only be valid + // signers for particular hostnames. This must be set if this + // CertChecker will be checking host certificates. + IsHostAuthority func(auth PublicKey, address string) bool + + // Clock is used for verifying time stamps. If nil, time.Now + // is used. + Clock func() time.Time + + // UserKeyFallback is called when CertChecker.Authenticate encounters a + // public key that is not a certificate. It must implement validation + // of user keys or else, if nil, all such keys are rejected. + UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) + + // HostKeyFallback is called when CertChecker.CheckHostKey encounters a + // public key that is not a certificate. It must implement host key + // validation or else, if nil, all such keys are rejected. + HostKeyFallback HostKeyCallback + + // IsRevoked is called for each certificate so that revocation checking + // can be implemented. It should return true if the given certificate + // is revoked and false otherwise. If nil, no certificates are + // considered to have been revoked. + IsRevoked func(cert *Certificate) bool +} + +// CheckHostKey checks a host key certificate. This method can be +// plugged into ClientConfig.HostKeyCallback. +func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error { + cert, ok := key.(*Certificate) + if !ok { + if c.HostKeyFallback != nil { + return c.HostKeyFallback(addr, remote, key) + } + return errors.New("ssh: non-certificate host key") + } + if cert.CertType != HostCert { + return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) + } + if !c.IsHostAuthority(cert.SignatureKey, addr) { + return fmt.Errorf("ssh: no authorities for hostname: %v", addr) + } + + hostname, _, err := net.SplitHostPort(addr) + if err != nil { + return err + } + + // Pass hostname only as principal for host certificates (consistent with OpenSSH) + return c.CheckCert(hostname, cert) +} + +// Authenticate checks a user certificate. Authenticate can be used as +// a value for ServerConfig.PublicKeyCallback. +func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) { + cert, ok := pubKey.(*Certificate) + if !ok { + if c.UserKeyFallback != nil { + return c.UserKeyFallback(conn, pubKey) + } + return nil, errors.New("ssh: normal key pairs not accepted") + } + + if cert.CertType != UserCert { + return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) + } + if !c.IsUserAuthority(cert.SignatureKey) { + return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority") + } + + if err := c.CheckCert(conn.User(), cert); err != nil { + return nil, err + } + + return &cert.Permissions, nil +} + +// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and +// the signature of the certificate. +func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { + if c.IsRevoked != nil && c.IsRevoked(cert) { + return fmt.Errorf("ssh: certificate serial %d revoked", cert.Serial) + } + + for opt := range cert.CriticalOptions { + // sourceAddressCriticalOption will be enforced by + // serverAuthenticate + if opt == sourceAddressCriticalOption { + continue + } + + found := false + for _, supp := range c.SupportedCriticalOptions { + if supp == opt { + found = true + break + } + } + if !found { + return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt) + } + } + + if len(cert.ValidPrincipals) > 0 { + // By default, certs are valid for all users/hosts. + found := false + for _, p := range cert.ValidPrincipals { + if p == principal { + found = true + break + } + } + if !found { + return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals) + } + } + + clock := c.Clock + if clock == nil { + clock = time.Now + } + + unixNow := clock().Unix() + if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { + return fmt.Errorf("ssh: cert is not yet valid") + } + if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { + return fmt.Errorf("ssh: cert has expired") + } + if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { + return fmt.Errorf("ssh: certificate signature does not verify") + } + + return nil +} + +// SignCert sets c.SignatureKey to the authority's public key and stores a +// Signature, by authority, in the certificate. +func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { + c.Nonce = make([]byte, 32) + if _, err := io.ReadFull(rand, c.Nonce); err != nil { + return err + } + c.SignatureKey = authority.PublicKey() + + sig, err := authority.Sign(rand, c.bytesForSigning()) + if err != nil { + return err + } + c.Signature = sig + return nil +} + +var certAlgoNames = map[string]string{ + KeyAlgoRSA: CertAlgoRSAv01, + KeyAlgoDSA: CertAlgoDSAv01, + KeyAlgoECDSA256: CertAlgoECDSA256v01, + KeyAlgoECDSA384: CertAlgoECDSA384v01, + KeyAlgoECDSA521: CertAlgoECDSA521v01, + KeyAlgoSKECDSA256: CertAlgoSKECDSA256v01, + KeyAlgoED25519: CertAlgoED25519v01, + KeyAlgoSKED25519: CertAlgoSKED25519v01, +} + +// certToPrivAlgo returns the underlying algorithm for a certificate algorithm. +// Panics if a non-certificate algorithm is passed. +func certToPrivAlgo(algo string) string { + for privAlgo, pubAlgo := range certAlgoNames { + if pubAlgo == algo { + return privAlgo + } + } + panic("unknown cert algorithm") +} + +func (cert *Certificate) bytesForSigning() []byte { + c2 := *cert + c2.Signature = nil + out := c2.Marshal() + // Drop trailing signature length. + return out[:len(out)-4] +} + +// Marshal serializes c into OpenSSH's wire format. It is part of the +// PublicKey interface. +func (c *Certificate) Marshal() []byte { + generic := genericCertData{ + Serial: c.Serial, + CertType: c.CertType, + KeyId: c.KeyId, + ValidPrincipals: marshalStringList(c.ValidPrincipals), + ValidAfter: uint64(c.ValidAfter), + ValidBefore: uint64(c.ValidBefore), + CriticalOptions: marshalTuples(c.CriticalOptions), + Extensions: marshalTuples(c.Extensions), + Reserved: c.Reserved, + SignatureKey: c.SignatureKey.Marshal(), + } + if c.Signature != nil { + generic.Signature = Marshal(c.Signature) + } + genericBytes := Marshal(&generic) + keyBytes := c.Key.Marshal() + _, keyBytes, _ = parseString(keyBytes) + prefix := Marshal(&struct { + Name string + Nonce []byte + Key []byte `ssh:"rest"` + }{c.Type(), c.Nonce, keyBytes}) + + result := make([]byte, 0, len(prefix)+len(genericBytes)) + result = append(result, prefix...) + result = append(result, genericBytes...) + return result +} + +// Type returns the key name. It is part of the PublicKey interface. +func (c *Certificate) Type() string { + algo, ok := certAlgoNames[c.Key.Type()] + if !ok { + panic("unknown cert key type " + c.Key.Type()) + } + return algo +} + +// Verify verifies a signature against the certificate's public +// key. It is part of the PublicKey interface. +func (c *Certificate) Verify(data []byte, sig *Signature) error { + return c.Key.Verify(data, sig) +} + +func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) { + format, in, ok := parseString(in) + if !ok { + return + } + + out = &Signature{ + Format: string(format), + } + + if out.Blob, in, ok = parseString(in); !ok { + return + } + + switch out.Format { + case KeyAlgoSKECDSA256, CertAlgoSKECDSA256v01, KeyAlgoSKED25519, CertAlgoSKED25519v01: + out.Rest = in + return out, nil, ok + } + + return out, in, ok +} + +func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) { + sigBytes, rest, ok := parseString(in) + if !ok { + return + } + + out, trailing, ok := parseSignatureBody(sigBytes) + if !ok || len(trailing) > 0 { + return nil, nil, false + } + return +} diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go new file mode 100644 index 00000000000..c0834c00dfe --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -0,0 +1,633 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "log" + "sync" +) + +const ( + minPacketLength = 9 + // channelMaxPacket contains the maximum number of bytes that will be + // sent in a single packet. As per RFC 4253, section 6.1, 32k is also + // the minimum. + channelMaxPacket = 1 << 15 + // We follow OpenSSH here. + channelWindowSize = 64 * channelMaxPacket +) + +// NewChannel represents an incoming request to a channel. It must either be +// accepted for use by calling Accept, or rejected by calling Reject. +type NewChannel interface { + // Accept accepts the channel creation request. It returns the Channel + // and a Go channel containing SSH requests. The Go channel must be + // serviced otherwise the Channel will hang. + Accept() (Channel, <-chan *Request, error) + + // Reject rejects the channel creation request. After calling + // this, no other methods on the Channel may be called. + Reject(reason RejectionReason, message string) error + + // ChannelType returns the type of the channel, as supplied by the + // client. + ChannelType() string + + // ExtraData returns the arbitrary payload for this channel, as supplied + // by the client. This data is specific to the channel type. + ExtraData() []byte +} + +// A Channel is an ordered, reliable, flow-controlled, duplex stream +// that is multiplexed over an SSH connection. +type Channel interface { + // Read reads up to len(data) bytes from the channel. + Read(data []byte) (int, error) + + // Write writes len(data) bytes to the channel. + Write(data []byte) (int, error) + + // Close signals end of channel use. No data may be sent after this + // call. + Close() error + + // CloseWrite signals the end of sending in-band + // data. Requests may still be sent, and the other side may + // still send data + CloseWrite() error + + // SendRequest sends a channel request. If wantReply is true, + // it will wait for a reply and return the result as a + // boolean, otherwise the return value will be false. Channel + // requests are out-of-band messages so they may be sent even + // if the data stream is closed or blocked by flow control. + // If the channel is closed before a reply is returned, io.EOF + // is returned. + SendRequest(name string, wantReply bool, payload []byte) (bool, error) + + // Stderr returns an io.ReadWriter that writes to this channel + // with the extended data type set to stderr. Stderr may + // safely be read and written from a different goroutine than + // Read and Write respectively. + Stderr() io.ReadWriter +} + +// Request is a request sent outside of the normal stream of +// data. Requests can either be specific to an SSH channel, or they +// can be global. +type Request struct { + Type string + WantReply bool + Payload []byte + + ch *channel + mux *mux +} + +// Reply sends a response to a request. It must be called for all requests +// where WantReply is true and is a no-op otherwise. The payload argument is +// ignored for replies to channel-specific requests. +func (r *Request) Reply(ok bool, payload []byte) error { + if !r.WantReply { + return nil + } + + if r.ch == nil { + return r.mux.ackRequest(ok, payload) + } + + return r.ch.ackRequest(ok) +} + +// RejectionReason is an enumeration used when rejecting channel creation +// requests. See RFC 4254, section 5.1. +type RejectionReason uint32 + +const ( + Prohibited RejectionReason = iota + 1 + ConnectionFailed + UnknownChannelType + ResourceShortage +) + +// String converts the rejection reason to human readable form. +func (r RejectionReason) String() string { + switch r { + case Prohibited: + return "administratively prohibited" + case ConnectionFailed: + return "connect failed" + case UnknownChannelType: + return "unknown channel type" + case ResourceShortage: + return "resource shortage" + } + return fmt.Sprintf("unknown reason %d", int(r)) +} + +func min(a uint32, b int) uint32 { + if a < uint32(b) { + return a + } + return uint32(b) +} + +type channelDirection uint8 + +const ( + channelInbound channelDirection = iota + channelOutbound +) + +// channel is an implementation of the Channel interface that works +// with the mux class. +type channel struct { + // R/O after creation + chanType string + extraData []byte + localId, remoteId uint32 + + // maxIncomingPayload and maxRemotePayload are the maximum + // payload sizes of normal and extended data packets for + // receiving and sending, respectively. The wire packet will + // be 9 or 13 bytes larger (excluding encryption overhead). + maxIncomingPayload uint32 + maxRemotePayload uint32 + + mux *mux + + // decided is set to true if an accept or reject message has been sent + // (for outbound channels) or received (for inbound channels). + decided bool + + // direction contains either channelOutbound, for channels created + // locally, or channelInbound, for channels created by the peer. + direction channelDirection + + // Pending internal channel messages. + msg chan interface{} + + // Since requests have no ID, there can be only one request + // with WantReply=true outstanding. This lock is held by a + // goroutine that has such an outgoing request pending. + sentRequestMu sync.Mutex + + incomingRequests chan *Request + + sentEOF bool + + // thread-safe data + remoteWin window + pending *buffer + extPending *buffer + + // windowMu protects myWindow, the flow-control window. + windowMu sync.Mutex + myWindow uint32 + + // writeMu serializes calls to mux.conn.writePacket() and + // protects sentClose and packetPool. This mutex must be + // different from windowMu, as writePacket can block if there + // is a key exchange pending. + writeMu sync.Mutex + sentClose bool + + // packetPool has a buffer for each extended channel ID to + // save allocations during writes. + packetPool map[uint32][]byte +} + +// writePacket sends a packet. If the packet is a channel close, it updates +// sentClose. This method takes the lock c.writeMu. +func (ch *channel) writePacket(packet []byte) error { + ch.writeMu.Lock() + if ch.sentClose { + ch.writeMu.Unlock() + return io.EOF + } + ch.sentClose = (packet[0] == msgChannelClose) + err := ch.mux.conn.writePacket(packet) + ch.writeMu.Unlock() + return err +} + +func (ch *channel) sendMessage(msg interface{}) error { + if debugMux { + log.Printf("send(%d): %#v", ch.mux.chanList.offset, msg) + } + + p := Marshal(msg) + binary.BigEndian.PutUint32(p[1:], ch.remoteId) + return ch.writePacket(p) +} + +// WriteExtended writes data to a specific extended stream. These streams are +// used, for example, for stderr. +func (ch *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) { + if ch.sentEOF { + return 0, io.EOF + } + // 1 byte message type, 4 bytes remoteId, 4 bytes data length + opCode := byte(msgChannelData) + headerLength := uint32(9) + if extendedCode > 0 { + headerLength += 4 + opCode = msgChannelExtendedData + } + + ch.writeMu.Lock() + packet := ch.packetPool[extendedCode] + // We don't remove the buffer from packetPool, so + // WriteExtended calls from different goroutines will be + // flagged as errors by the race detector. + ch.writeMu.Unlock() + + for len(data) > 0 { + space := min(ch.maxRemotePayload, len(data)) + if space, err = ch.remoteWin.reserve(space); err != nil { + return n, err + } + if want := headerLength + space; uint32(cap(packet)) < want { + packet = make([]byte, want) + } else { + packet = packet[:want] + } + + todo := data[:space] + + packet[0] = opCode + binary.BigEndian.PutUint32(packet[1:], ch.remoteId) + if extendedCode > 0 { + binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode)) + } + binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo))) + copy(packet[headerLength:], todo) + if err = ch.writePacket(packet); err != nil { + return n, err + } + + n += len(todo) + data = data[len(todo):] + } + + ch.writeMu.Lock() + ch.packetPool[extendedCode] = packet + ch.writeMu.Unlock() + + return n, err +} + +func (ch *channel) handleData(packet []byte) error { + headerLen := 9 + isExtendedData := packet[0] == msgChannelExtendedData + if isExtendedData { + headerLen = 13 + } + if len(packet) < headerLen { + // malformed data packet + return parseError(packet[0]) + } + + var extended uint32 + if isExtendedData { + extended = binary.BigEndian.Uint32(packet[5:]) + } + + length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen]) + if length == 0 { + return nil + } + if length > ch.maxIncomingPayload { + // TODO(hanwen): should send Disconnect? + return errors.New("ssh: incoming packet exceeds maximum payload size") + } + + data := packet[headerLen:] + if length != uint32(len(data)) { + return errors.New("ssh: wrong packet length") + } + + ch.windowMu.Lock() + if ch.myWindow < length { + ch.windowMu.Unlock() + // TODO(hanwen): should send Disconnect with reason? + return errors.New("ssh: remote side wrote too much") + } + ch.myWindow -= length + ch.windowMu.Unlock() + + if extended == 1 { + ch.extPending.write(data) + } else if extended > 0 { + // discard other extended data. + } else { + ch.pending.write(data) + } + return nil +} + +func (c *channel) adjustWindow(n uint32) error { + c.windowMu.Lock() + // Since myWindow is managed on our side, and can never exceed + // the initial window setting, we don't worry about overflow. + c.myWindow += uint32(n) + c.windowMu.Unlock() + return c.sendMessage(windowAdjustMsg{ + AdditionalBytes: uint32(n), + }) +} + +func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) { + switch extended { + case 1: + n, err = c.extPending.Read(data) + case 0: + n, err = c.pending.Read(data) + default: + return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended) + } + + if n > 0 { + err = c.adjustWindow(uint32(n)) + // sendWindowAdjust can return io.EOF if the remote + // peer has closed the connection, however we want to + // defer forwarding io.EOF to the caller of Read until + // the buffer has been drained. + if n > 0 && err == io.EOF { + err = nil + } + } + + return n, err +} + +func (c *channel) close() { + c.pending.eof() + c.extPending.eof() + close(c.msg) + close(c.incomingRequests) + c.writeMu.Lock() + // This is not necessary for a normal channel teardown, but if + // there was another error, it is. + c.sentClose = true + c.writeMu.Unlock() + // Unblock writers. + c.remoteWin.close() +} + +// responseMessageReceived is called when a success or failure message is +// received on a channel to check that such a message is reasonable for the +// given channel. +func (ch *channel) responseMessageReceived() error { + if ch.direction == channelInbound { + return errors.New("ssh: channel response message received on inbound channel") + } + if ch.decided { + return errors.New("ssh: duplicate response received for channel") + } + ch.decided = true + return nil +} + +func (ch *channel) handlePacket(packet []byte) error { + switch packet[0] { + case msgChannelData, msgChannelExtendedData: + return ch.handleData(packet) + case msgChannelClose: + ch.sendMessage(channelCloseMsg{PeersID: ch.remoteId}) + ch.mux.chanList.remove(ch.localId) + ch.close() + return nil + case msgChannelEOF: + // RFC 4254 is mute on how EOF affects dataExt messages but + // it is logical to signal EOF at the same time. + ch.extPending.eof() + ch.pending.eof() + return nil + } + + decoded, err := decode(packet) + if err != nil { + return err + } + + switch msg := decoded.(type) { + case *channelOpenFailureMsg: + if err := ch.responseMessageReceived(); err != nil { + return err + } + ch.mux.chanList.remove(msg.PeersID) + ch.msg <- msg + case *channelOpenConfirmMsg: + if err := ch.responseMessageReceived(); err != nil { + return err + } + if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { + return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize) + } + ch.remoteId = msg.MyID + ch.maxRemotePayload = msg.MaxPacketSize + ch.remoteWin.add(msg.MyWindow) + ch.msg <- msg + case *windowAdjustMsg: + if !ch.remoteWin.add(msg.AdditionalBytes) { + return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes) + } + case *channelRequestMsg: + req := Request{ + Type: msg.Request, + WantReply: msg.WantReply, + Payload: msg.RequestSpecificData, + ch: ch, + } + + ch.incomingRequests <- &req + default: + ch.msg <- msg + } + return nil +} + +func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel { + ch := &channel{ + remoteWin: window{Cond: newCond()}, + myWindow: channelWindowSize, + pending: newBuffer(), + extPending: newBuffer(), + direction: direction, + incomingRequests: make(chan *Request, chanSize), + msg: make(chan interface{}, chanSize), + chanType: chanType, + extraData: extraData, + mux: m, + packetPool: make(map[uint32][]byte), + } + ch.localId = m.chanList.add(ch) + return ch +} + +var errUndecided = errors.New("ssh: must Accept or Reject channel") +var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once") + +type extChannel struct { + code uint32 + ch *channel +} + +func (e *extChannel) Write(data []byte) (n int, err error) { + return e.ch.WriteExtended(data, e.code) +} + +func (e *extChannel) Read(data []byte) (n int, err error) { + return e.ch.ReadExtended(data, e.code) +} + +func (ch *channel) Accept() (Channel, <-chan *Request, error) { + if ch.decided { + return nil, nil, errDecidedAlready + } + ch.maxIncomingPayload = channelMaxPacket + confirm := channelOpenConfirmMsg{ + PeersID: ch.remoteId, + MyID: ch.localId, + MyWindow: ch.myWindow, + MaxPacketSize: ch.maxIncomingPayload, + } + ch.decided = true + if err := ch.sendMessage(confirm); err != nil { + return nil, nil, err + } + + return ch, ch.incomingRequests, nil +} + +func (ch *channel) Reject(reason RejectionReason, message string) error { + if ch.decided { + return errDecidedAlready + } + reject := channelOpenFailureMsg{ + PeersID: ch.remoteId, + Reason: reason, + Message: message, + Language: "en", + } + ch.decided = true + return ch.sendMessage(reject) +} + +func (ch *channel) Read(data []byte) (int, error) { + if !ch.decided { + return 0, errUndecided + } + return ch.ReadExtended(data, 0) +} + +func (ch *channel) Write(data []byte) (int, error) { + if !ch.decided { + return 0, errUndecided + } + return ch.WriteExtended(data, 0) +} + +func (ch *channel) CloseWrite() error { + if !ch.decided { + return errUndecided + } + ch.sentEOF = true + return ch.sendMessage(channelEOFMsg{ + PeersID: ch.remoteId}) +} + +func (ch *channel) Close() error { + if !ch.decided { + return errUndecided + } + + return ch.sendMessage(channelCloseMsg{ + PeersID: ch.remoteId}) +} + +// Extended returns an io.ReadWriter that sends and receives data on the given, +// SSH extended stream. Such streams are used, for example, for stderr. +func (ch *channel) Extended(code uint32) io.ReadWriter { + if !ch.decided { + return nil + } + return &extChannel{code, ch} +} + +func (ch *channel) Stderr() io.ReadWriter { + return ch.Extended(1) +} + +func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { + if !ch.decided { + return false, errUndecided + } + + if wantReply { + ch.sentRequestMu.Lock() + defer ch.sentRequestMu.Unlock() + } + + msg := channelRequestMsg{ + PeersID: ch.remoteId, + Request: name, + WantReply: wantReply, + RequestSpecificData: payload, + } + + if err := ch.sendMessage(msg); err != nil { + return false, err + } + + if wantReply { + m, ok := (<-ch.msg) + if !ok { + return false, io.EOF + } + switch m.(type) { + case *channelRequestFailureMsg: + return false, nil + case *channelRequestSuccessMsg: + return true, nil + default: + return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m) + } + } + + return false, nil +} + +// ackRequest either sends an ack or nack to the channel request. +func (ch *channel) ackRequest(ok bool) error { + if !ch.decided { + return errUndecided + } + + var msg interface{} + if !ok { + msg = channelRequestFailureMsg{ + PeersID: ch.remoteId, + } + } else { + msg = channelRequestSuccessMsg{ + PeersID: ch.remoteId, + } + } + return ch.sendMessage(msg) +} + +func (ch *channel) ChannelType() string { + return ch.chanType +} + +func (ch *channel) ExtraData() []byte { + return ch.extraData +} diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go new file mode 100644 index 00000000000..b0204ee59f2 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/cipher.go @@ -0,0 +1,781 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/rc4" + "crypto/subtle" + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + "io/ioutil" + + "golang.org/x/crypto/chacha20" + "golang.org/x/crypto/poly1305" +) + +const ( + packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. + + // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations + // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC + // indicates implementations SHOULD be able to handle larger packet sizes, but then + // waffles on about reasonable limits. + // + // OpenSSH caps their maxPacket at 256kB so we choose to do + // the same. maxPacket is also used to ensure that uint32 + // length fields do not overflow, so it should remain well + // below 4G. + maxPacket = 256 * 1024 +) + +// noneCipher implements cipher.Stream and provides no encryption. It is used +// by the transport before the first key-exchange. +type noneCipher struct{} + +func (c noneCipher) XORKeyStream(dst, src []byte) { + copy(dst, src) +} + +func newAESCTR(key, iv []byte) (cipher.Stream, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewCTR(c, iv), nil +} + +func newRC4(key, iv []byte) (cipher.Stream, error) { + return rc4.NewCipher(key) +} + +type cipherMode struct { + keySize int + ivSize int + create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) +} + +func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + return func(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + stream, err := createFunc(key, iv) + if err != nil { + return nil, err + } + + var streamDump []byte + if skip > 0 { + streamDump = make([]byte, 512) + } + + for remainingToDump := skip; remainingToDump > 0; { + dumpThisTime := remainingToDump + if dumpThisTime > len(streamDump) { + dumpThisTime = len(streamDump) + } + stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) + remainingToDump -= dumpThisTime + } + + mac := macModes[algs.MAC].new(macKey) + return &streamPacketCipher{ + mac: mac, + etm: macModes[algs.MAC].etm, + macResult: make([]byte, mac.Size()), + cipher: stream, + }, nil + } +} + +// cipherModes documents properties of supported ciphers. Ciphers not included +// are not supported and will not be negotiated, even if explicitly requested in +// ClientConfig.Crypto.Ciphers. +var cipherModes = map[string]*cipherMode{ + // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms + // are defined in the order specified in the RFC. + "aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)}, + "aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)}, + "aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)}, + + // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. + // They are defined in the order specified in the RFC. + "arcfour128": {16, 0, streamCipherMode(1536, newRC4)}, + "arcfour256": {32, 0, streamCipherMode(1536, newRC4)}, + + // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. + // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and + // RC4) has problems with weak keys, and should be used with caution." + // RFC4345 introduces improved versions of Arcfour. + "arcfour": {16, 0, streamCipherMode(0, newRC4)}, + + // AEAD ciphers + gcmCipherID: {16, 12, newGCMCipher}, + chacha20Poly1305ID: {64, 0, newChaCha20Cipher}, + + // CBC mode is insecure and so is not included in the default config. + // (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely + // needed, it's possible to specify a custom Config to enable it. + // You should expect that an active attacker can recover plaintext if + // you do. + aes128cbcID: {16, aes.BlockSize, newAESCBCCipher}, + + // 3des-cbc is insecure and is not included in the default + // config. + tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher}, +} + +// prefixLen is the length of the packet prefix that contains the packet length +// and number of padding bytes. +const prefixLen = 5 + +// streamPacketCipher is a packetCipher using a stream cipher. +type streamPacketCipher struct { + mac hash.Hash + cipher cipher.Stream + etm bool + + // The following members are to avoid per-packet allocations. + prefix [prefixLen]byte + seqNumBytes [4]byte + padding [2 * packetSizeMultiple]byte + packetData []byte + macResult []byte +} + +// readCipherPacket reads and decrypt a single packet from the reader argument. +func (s *streamPacketCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { + if _, err := io.ReadFull(r, s.prefix[:]); err != nil { + return nil, err + } + + var encryptedPaddingLength [1]byte + if s.mac != nil && s.etm { + copy(encryptedPaddingLength[:], s.prefix[4:5]) + s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) + } else { + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + } + + length := binary.BigEndian.Uint32(s.prefix[0:4]) + paddingLength := uint32(s.prefix[4]) + + var macSize uint32 + if s.mac != nil { + s.mac.Reset() + binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) + s.mac.Write(s.seqNumBytes[:]) + if s.etm { + s.mac.Write(s.prefix[:4]) + s.mac.Write(encryptedPaddingLength[:]) + } else { + s.mac.Write(s.prefix[:]) + } + macSize = uint32(s.mac.Size()) + } + + if length <= paddingLength+1 { + return nil, errors.New("ssh: invalid packet length, packet too small") + } + + if length > maxPacket { + return nil, errors.New("ssh: invalid packet length, packet too large") + } + + // the maxPacket check above ensures that length-1+macSize + // does not overflow. + if uint32(cap(s.packetData)) < length-1+macSize { + s.packetData = make([]byte, length-1+macSize) + } else { + s.packetData = s.packetData[:length-1+macSize] + } + + if _, err := io.ReadFull(r, s.packetData); err != nil { + return nil, err + } + mac := s.packetData[length-1:] + data := s.packetData[:length-1] + + if s.mac != nil && s.etm { + s.mac.Write(data) + } + + s.cipher.XORKeyStream(data, data) + + if s.mac != nil { + if !s.etm { + s.mac.Write(data) + } + s.macResult = s.mac.Sum(s.macResult[:0]) + if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { + return nil, errors.New("ssh: MAC failure") + } + } + + return s.packetData[:length-paddingLength-1], nil +} + +// writeCipherPacket encrypts and sends a packet of data to the writer argument +func (s *streamPacketCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + if len(packet) > maxPacket { + return errors.New("ssh: packet too large") + } + + aadlen := 0 + if s.mac != nil && s.etm { + // packet length is not encrypted for EtM modes + aadlen = 4 + } + + paddingLength := packetSizeMultiple - (prefixLen+len(packet)-aadlen)%packetSizeMultiple + if paddingLength < 4 { + paddingLength += packetSizeMultiple + } + + length := len(packet) + 1 + paddingLength + binary.BigEndian.PutUint32(s.prefix[:], uint32(length)) + s.prefix[4] = byte(paddingLength) + padding := s.padding[:paddingLength] + if _, err := io.ReadFull(rand, padding); err != nil { + return err + } + + if s.mac != nil { + s.mac.Reset() + binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) + s.mac.Write(s.seqNumBytes[:]) + + if s.etm { + // For EtM algorithms, the packet length must stay unencrypted, + // but the following data (padding length) must be encrypted + s.cipher.XORKeyStream(s.prefix[4:5], s.prefix[4:5]) + } + + s.mac.Write(s.prefix[:]) + + if !s.etm { + // For non-EtM algorithms, the algorithm is applied on unencrypted data + s.mac.Write(packet) + s.mac.Write(padding) + } + } + + if !(s.mac != nil && s.etm) { + // For EtM algorithms, the padding length has already been encrypted + // and the packet length must remain unencrypted + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + } + + s.cipher.XORKeyStream(packet, packet) + s.cipher.XORKeyStream(padding, padding) + + if s.mac != nil && s.etm { + // For EtM algorithms, packet and padding must be encrypted + s.mac.Write(packet) + s.mac.Write(padding) + } + + if _, err := w.Write(s.prefix[:]); err != nil { + return err + } + if _, err := w.Write(packet); err != nil { + return err + } + if _, err := w.Write(padding); err != nil { + return err + } + + if s.mac != nil { + s.macResult = s.mac.Sum(s.macResult[:0]) + if _, err := w.Write(s.macResult); err != nil { + return err + } + } + + return nil +} + +type gcmCipher struct { + aead cipher.AEAD + prefix [4]byte + iv []byte + buf []byte +} + +func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + aead, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + return &gcmCipher{ + aead: aead, + iv: iv, + }, nil +} + +const gcmTagSize = 16 + +func (c *gcmCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + // Pad out to multiple of 16 bytes. This is different from the + // stream cipher because that encrypts the length too. + padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) + if padding < 4 { + padding += packetSizeMultiple + } + + length := uint32(len(packet) + int(padding) + 1) + binary.BigEndian.PutUint32(c.prefix[:], length) + if _, err := w.Write(c.prefix[:]); err != nil { + return err + } + + if cap(c.buf) < int(length) { + c.buf = make([]byte, length) + } else { + c.buf = c.buf[:length] + } + + c.buf[0] = padding + copy(c.buf[1:], packet) + if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil { + return err + } + c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:]) + if _, err := w.Write(c.buf); err != nil { + return err + } + c.incIV() + + return nil +} + +func (c *gcmCipher) incIV() { + for i := 4 + 7; i >= 4; i-- { + c.iv[i]++ + if c.iv[i] != 0 { + break + } + } +} + +func (c *gcmCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { + if _, err := io.ReadFull(r, c.prefix[:]); err != nil { + return nil, err + } + length := binary.BigEndian.Uint32(c.prefix[:]) + if length > maxPacket { + return nil, errors.New("ssh: max packet length exceeded") + } + + if cap(c.buf) < int(length+gcmTagSize) { + c.buf = make([]byte, length+gcmTagSize) + } else { + c.buf = c.buf[:length+gcmTagSize] + } + + if _, err := io.ReadFull(r, c.buf); err != nil { + return nil, err + } + + plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:]) + if err != nil { + return nil, err + } + c.incIV() + + padding := plain[0] + if padding < 4 { + // padding is a byte, so it automatically satisfies + // the maximum size, which is 255. + return nil, fmt.Errorf("ssh: illegal padding %d", padding) + } + + if int(padding+1) >= len(plain) { + return nil, fmt.Errorf("ssh: padding %d too large", padding) + } + plain = plain[1 : length-uint32(padding)] + return plain, nil +} + +// cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1 +type cbcCipher struct { + mac hash.Hash + macSize uint32 + decrypter cipher.BlockMode + encrypter cipher.BlockMode + + // The following members are to avoid per-packet allocations. + seqNumBytes [4]byte + packetData []byte + macResult []byte + + // Amount of data we should still read to hide which + // verification error triggered. + oracleCamouflage uint32 +} + +func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + cbc := &cbcCipher{ + mac: macModes[algs.MAC].new(macKey), + decrypter: cipher.NewCBCDecrypter(c, iv), + encrypter: cipher.NewCBCEncrypter(c, iv), + packetData: make([]byte, 1024), + } + if cbc.mac != nil { + cbc.macSize = uint32(cbc.mac.Size()) + } + + return cbc, nil +} + +func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + cbc, err := newCBCCipher(c, key, iv, macKey, algs) + if err != nil { + return nil, err + } + + return cbc, nil +} + +func newTripleDESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + c, err := des.NewTripleDESCipher(key) + if err != nil { + return nil, err + } + + cbc, err := newCBCCipher(c, key, iv, macKey, algs) + if err != nil { + return nil, err + } + + return cbc, nil +} + +func maxUInt32(a, b int) uint32 { + if a > b { + return uint32(a) + } + return uint32(b) +} + +const ( + cbcMinPacketSizeMultiple = 8 + cbcMinPacketSize = 16 + cbcMinPaddingSize = 4 +) + +// cbcError represents a verification error that may leak information. +type cbcError string + +func (e cbcError) Error() string { return string(e) } + +func (c *cbcCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { + p, err := c.readCipherPacketLeaky(seqNum, r) + if err != nil { + if _, ok := err.(cbcError); ok { + // Verification error: read a fixed amount of + // data, to make distinguishing between + // failing MAC and failing length check more + // difficult. + io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage)) + } + } + return p, err +} + +func (c *cbcCipher) readCipherPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { + blockSize := c.decrypter.BlockSize() + + // Read the header, which will include some of the subsequent data in the + // case of block ciphers - this is copied back to the payload later. + // How many bytes of payload/padding will be read with this first read. + firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize) + firstBlock := c.packetData[:firstBlockLength] + if _, err := io.ReadFull(r, firstBlock); err != nil { + return nil, err + } + + c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength + + c.decrypter.CryptBlocks(firstBlock, firstBlock) + length := binary.BigEndian.Uint32(firstBlock[:4]) + if length > maxPacket { + return nil, cbcError("ssh: packet too large") + } + if length+4 < maxUInt32(cbcMinPacketSize, blockSize) { + // The minimum size of a packet is 16 (or the cipher block size, whichever + // is larger) bytes. + return nil, cbcError("ssh: packet too small") + } + // The length of the packet (including the length field but not the MAC) must + // be a multiple of the block size or 8, whichever is larger. + if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 { + return nil, cbcError("ssh: invalid packet length multiple") + } + + paddingLength := uint32(firstBlock[4]) + if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 { + return nil, cbcError("ssh: invalid packet length") + } + + // Positions within the c.packetData buffer: + macStart := 4 + length + paddingStart := macStart - paddingLength + + // Entire packet size, starting before length, ending at end of mac. + entirePacketSize := macStart + c.macSize + + // Ensure c.packetData is large enough for the entire packet data. + if uint32(cap(c.packetData)) < entirePacketSize { + // Still need to upsize and copy, but this should be rare at runtime, only + // on upsizing the packetData buffer. + c.packetData = make([]byte, entirePacketSize) + copy(c.packetData, firstBlock) + } else { + c.packetData = c.packetData[:entirePacketSize] + } + + n, err := io.ReadFull(r, c.packetData[firstBlockLength:]) + if err != nil { + return nil, err + } + c.oracleCamouflage -= uint32(n) + + remainingCrypted := c.packetData[firstBlockLength:macStart] + c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted) + + mac := c.packetData[macStart:] + if c.mac != nil { + c.mac.Reset() + binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) + c.mac.Write(c.seqNumBytes[:]) + c.mac.Write(c.packetData[:macStart]) + c.macResult = c.mac.Sum(c.macResult[:0]) + if subtle.ConstantTimeCompare(c.macResult, mac) != 1 { + return nil, cbcError("ssh: MAC failure") + } + } + + return c.packetData[prefixLen:paddingStart], nil +} + +func (c *cbcCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize()) + + // Length of encrypted portion of the packet (header, payload, padding). + // Enforce minimum padding and packet size. + encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize) + // Enforce block size. + encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize + + length := encLength - 4 + paddingLength := int(length) - (1 + len(packet)) + + // Overall buffer contains: header, payload, padding, mac. + // Space for the MAC is reserved in the capacity but not the slice length. + bufferSize := encLength + c.macSize + if uint32(cap(c.packetData)) < bufferSize { + c.packetData = make([]byte, encLength, bufferSize) + } else { + c.packetData = c.packetData[:encLength] + } + + p := c.packetData + + // Packet header. + binary.BigEndian.PutUint32(p, length) + p = p[4:] + p[0] = byte(paddingLength) + + // Payload. + p = p[1:] + copy(p, packet) + + // Padding. + p = p[len(packet):] + if _, err := io.ReadFull(rand, p); err != nil { + return err + } + + if c.mac != nil { + c.mac.Reset() + binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) + c.mac.Write(c.seqNumBytes[:]) + c.mac.Write(c.packetData) + // The MAC is now appended into the capacity reserved for it earlier. + c.packetData = c.mac.Sum(c.packetData) + } + + c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength]) + + if _, err := w.Write(c.packetData); err != nil { + return err + } + + return nil +} + +const chacha20Poly1305ID = "chacha20-poly1305@openssh.com" + +// chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com +// AEAD, which is described here: +// +// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00 +// +// the methods here also implement padding, which RFC4253 Section 6 +// also requires of stream ciphers. +type chacha20Poly1305Cipher struct { + lengthKey [32]byte + contentKey [32]byte + buf []byte +} + +func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) { + if len(key) != 64 { + panic(len(key)) + } + + c := &chacha20Poly1305Cipher{ + buf: make([]byte, 256), + } + + copy(c.contentKey[:], key[:32]) + copy(c.lengthKey[:], key[32:]) + return c, nil +} + +func (c *chacha20Poly1305Cipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) { + nonce := make([]byte, 12) + binary.BigEndian.PutUint32(nonce[8:], seqNum) + s, err := chacha20.NewUnauthenticatedCipher(c.contentKey[:], nonce) + if err != nil { + return nil, err + } + var polyKey, discardBuf [32]byte + s.XORKeyStream(polyKey[:], polyKey[:]) + s.XORKeyStream(discardBuf[:], discardBuf[:]) // skip the next 32 bytes + + encryptedLength := c.buf[:4] + if _, err := io.ReadFull(r, encryptedLength); err != nil { + return nil, err + } + + var lenBytes [4]byte + ls, err := chacha20.NewUnauthenticatedCipher(c.lengthKey[:], nonce) + if err != nil { + return nil, err + } + ls.XORKeyStream(lenBytes[:], encryptedLength) + + length := binary.BigEndian.Uint32(lenBytes[:]) + if length > maxPacket { + return nil, errors.New("ssh: invalid packet length, packet too large") + } + + contentEnd := 4 + length + packetEnd := contentEnd + poly1305.TagSize + if uint32(cap(c.buf)) < packetEnd { + c.buf = make([]byte, packetEnd) + copy(c.buf[:], encryptedLength) + } else { + c.buf = c.buf[:packetEnd] + } + + if _, err := io.ReadFull(r, c.buf[4:packetEnd]); err != nil { + return nil, err + } + + var mac [poly1305.TagSize]byte + copy(mac[:], c.buf[contentEnd:packetEnd]) + if !poly1305.Verify(&mac, c.buf[:contentEnd], &polyKey) { + return nil, errors.New("ssh: MAC failure") + } + + plain := c.buf[4:contentEnd] + s.XORKeyStream(plain, plain) + + padding := plain[0] + if padding < 4 { + // padding is a byte, so it automatically satisfies + // the maximum size, which is 255. + return nil, fmt.Errorf("ssh: illegal padding %d", padding) + } + + if int(padding)+1 >= len(plain) { + return nil, fmt.Errorf("ssh: padding %d too large", padding) + } + + plain = plain[1 : len(plain)-int(padding)] + + return plain, nil +} + +func (c *chacha20Poly1305Cipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error { + nonce := make([]byte, 12) + binary.BigEndian.PutUint32(nonce[8:], seqNum) + s, err := chacha20.NewUnauthenticatedCipher(c.contentKey[:], nonce) + if err != nil { + return err + } + var polyKey, discardBuf [32]byte + s.XORKeyStream(polyKey[:], polyKey[:]) + s.XORKeyStream(discardBuf[:], discardBuf[:]) // skip the next 32 bytes + + // There is no blocksize, so fall back to multiple of 8 byte + // padding, as described in RFC 4253, Sec 6. + const packetSizeMultiple = 8 + + padding := packetSizeMultiple - (1+len(payload))%packetSizeMultiple + if padding < 4 { + padding += packetSizeMultiple + } + + // size (4 bytes), padding (1), payload, padding, tag. + totalLength := 4 + 1 + len(payload) + padding + poly1305.TagSize + if cap(c.buf) < totalLength { + c.buf = make([]byte, totalLength) + } else { + c.buf = c.buf[:totalLength] + } + + binary.BigEndian.PutUint32(c.buf, uint32(1+len(payload)+padding)) + ls, err := chacha20.NewUnauthenticatedCipher(c.lengthKey[:], nonce) + if err != nil { + return err + } + ls.XORKeyStream(c.buf, c.buf[:4]) + c.buf[4] = byte(padding) + copy(c.buf[5:], payload) + packetEnd := 5 + len(payload) + padding + if _, err := io.ReadFull(rand, c.buf[5+len(payload):packetEnd]); err != nil { + return err + } + + s.XORKeyStream(c.buf[4:], c.buf[4:packetEnd]) + + var mac [poly1305.TagSize]byte + poly1305.Sum(&mac, c.buf[:packetEnd], &polyKey) + + copy(c.buf[packetEnd:], mac[:]) + + if _, err := w.Write(c.buf); err != nil { + return err + } + return nil +} diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go new file mode 100644 index 00000000000..7b00bff1caa --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -0,0 +1,278 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "net" + "os" + "sync" + "time" +) + +// Client implements a traditional SSH client that supports shells, +// subprocesses, TCP port/streamlocal forwarding and tunneled dialing. +type Client struct { + Conn + + handleForwardsOnce sync.Once // guards calling (*Client).handleForwards + + forwards forwardList // forwarded tcpip connections from the remote side + mu sync.Mutex + channelHandlers map[string]chan NewChannel +} + +// HandleChannelOpen returns a channel on which NewChannel requests +// for the given type are sent. If the type already is being handled, +// nil is returned. The channel is closed when the connection is closed. +func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { + c.mu.Lock() + defer c.mu.Unlock() + if c.channelHandlers == nil { + // The SSH channel has been closed. + c := make(chan NewChannel) + close(c) + return c + } + + ch := c.channelHandlers[channelType] + if ch != nil { + return nil + } + + ch = make(chan NewChannel, chanSize) + c.channelHandlers[channelType] = ch + return ch +} + +// NewClient creates a Client on top of the given connection. +func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { + conn := &Client{ + Conn: c, + channelHandlers: make(map[string]chan NewChannel, 1), + } + + go conn.handleGlobalRequests(reqs) + go conn.handleChannelOpens(chans) + go func() { + conn.Wait() + conn.forwards.closeAll() + }() + return conn +} + +// NewClientConn establishes an authenticated SSH connection using c +// as the underlying transport. The Request and NewChannel channels +// must be serviced or the connection will hang. +func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { + fullConf := *config + fullConf.SetDefaults() + if fullConf.HostKeyCallback == nil { + c.Close() + return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback") + } + + conn := &connection{ + sshConn: sshConn{conn: c}, + } + + if err := conn.clientHandshake(addr, &fullConf); err != nil { + c.Close() + return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) + } + conn.mux = newMux(conn.transport) + return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil +} + +// clientHandshake performs the client side key exchange. See RFC 4253 Section +// 7. +func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error { + if config.ClientVersion != "" { + c.clientVersion = []byte(config.ClientVersion) + } else { + c.clientVersion = []byte(packageVersion) + } + var err error + c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion) + if err != nil { + return err + } + + c.transport = newClientTransport( + newTransport(c.sshConn.conn, config.Rand, true /* is client */), + c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) + if err := c.transport.waitSession(); err != nil { + return err + } + + c.sessionID = c.transport.getSessionID() + return c.clientAuthenticate(config) +} + +// verifyHostKeySignature verifies the host key obtained in the key +// exchange. +func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error { + sig, rest, ok := parseSignatureBody(result.Signature) + if len(rest) > 0 || !ok { + return errors.New("ssh: signature parse error") + } + + return hostKey.Verify(result.H, sig) +} + +// NewSession opens a new Session for this client. (A session is a remote +// execution of a program.) +func (c *Client) NewSession() (*Session, error) { + ch, in, err := c.OpenChannel("session", nil) + if err != nil { + return nil, err + } + return newSession(ch, in) +} + +func (c *Client) handleGlobalRequests(incoming <-chan *Request) { + for r := range incoming { + // This handles keepalive messages and matches + // the behaviour of OpenSSH. + r.Reply(false, nil) + } +} + +// handleChannelOpens channel open messages from the remote side. +func (c *Client) handleChannelOpens(in <-chan NewChannel) { + for ch := range in { + c.mu.Lock() + handler := c.channelHandlers[ch.ChannelType()] + c.mu.Unlock() + + if handler != nil { + handler <- ch + } else { + ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType())) + } + } + + c.mu.Lock() + for _, ch := range c.channelHandlers { + close(ch) + } + c.channelHandlers = nil + c.mu.Unlock() +} + +// Dial starts a client connection to the given SSH server. It is a +// convenience function that connects to the given network address, +// initiates the SSH handshake, and then sets up a Client. For access +// to incoming channels and requests, use net.Dial with NewClientConn +// instead. +func Dial(network, addr string, config *ClientConfig) (*Client, error) { + conn, err := net.DialTimeout(network, addr, config.Timeout) + if err != nil { + return nil, err + } + c, chans, reqs, err := NewClientConn(conn, addr, config) + if err != nil { + return nil, err + } + return NewClient(c, chans, reqs), nil +} + +// HostKeyCallback is the function type used for verifying server +// keys. A HostKeyCallback must return nil if the host key is OK, or +// an error to reject it. It receives the hostname as passed to Dial +// or NewClientConn. The remote address is the RemoteAddr of the +// net.Conn underlying the SSH connection. +type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + +// BannerCallback is the function type used for treat the banner sent by +// the server. A BannerCallback receives the message sent by the remote server. +type BannerCallback func(message string) error + +// A ClientConfig structure is used to configure a Client. It must not be +// modified after having been passed to an SSH function. +type ClientConfig struct { + // Config contains configuration that is shared between clients and + // servers. + Config + + // User contains the username to authenticate as. + User string + + // Auth contains possible authentication methods to use with the + // server. Only the first instance of a particular RFC 4252 method will + // be used during authentication. + Auth []AuthMethod + + // HostKeyCallback is called during the cryptographic + // handshake to validate the server's host key. The client + // configuration must supply this callback for the connection + // to succeed. The functions InsecureIgnoreHostKey or + // FixedHostKey can be used for simplistic host key checks. + HostKeyCallback HostKeyCallback + + // BannerCallback is called during the SSH dance to display a custom + // server's message. The client configuration can supply this callback to + // handle it as wished. The function BannerDisplayStderr can be used for + // simplistic display on Stderr. + BannerCallback BannerCallback + + // ClientVersion contains the version identification string that will + // be used for the connection. If empty, a reasonable default is used. + ClientVersion string + + // HostKeyAlgorithms lists the key types that the client will + // accept from the server as host key, in order of + // preference. If empty, a reasonable default is used. Any + // string returned from PublicKey.Type method may be used, or + // any of the CertAlgoXxxx and KeyAlgoXxxx constants. + HostKeyAlgorithms []string + + // Timeout is the maximum amount of time for the TCP connection to establish. + // + // A Timeout of zero means no timeout. + Timeout time.Duration +} + +// InsecureIgnoreHostKey returns a function that can be used for +// ClientConfig.HostKeyCallback to accept any host key. It should +// not be used for production code. +func InsecureIgnoreHostKey() HostKeyCallback { + return func(hostname string, remote net.Addr, key PublicKey) error { + return nil + } +} + +type fixedHostKey struct { + key PublicKey +} + +func (f *fixedHostKey) check(hostname string, remote net.Addr, key PublicKey) error { + if f.key == nil { + return fmt.Errorf("ssh: required host key was nil") + } + if !bytes.Equal(key.Marshal(), f.key.Marshal()) { + return fmt.Errorf("ssh: host key mismatch") + } + return nil +} + +// FixedHostKey returns a function for use in +// ClientConfig.HostKeyCallback to accept only a specific host key. +func FixedHostKey(key PublicKey) HostKeyCallback { + hk := &fixedHostKey{key} + return hk.check +} + +// BannerDisplayStderr returns a function that can be used for +// ClientConfig.BannerCallback to display banners on os.Stderr. +func BannerDisplayStderr() BannerCallback { + return func(banner string) error { + _, err := os.Stderr.WriteString(banner) + + return err + } +} diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go new file mode 100644 index 00000000000..0590070e220 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -0,0 +1,639 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" +) + +type authResult int + +const ( + authFailure authResult = iota + authPartialSuccess + authSuccess +) + +// clientAuthenticate authenticates with the remote server. See RFC 4252. +func (c *connection) clientAuthenticate(config *ClientConfig) error { + // initiate user auth session + if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil { + return err + } + packet, err := c.transport.readPacket() + if err != nil { + return err + } + var serviceAccept serviceAcceptMsg + if err := Unmarshal(packet, &serviceAccept); err != nil { + return err + } + + // during the authentication phase the client first attempts the "none" method + // then any untried methods suggested by the server. + tried := make(map[string]bool) + var lastMethods []string + + sessionID := c.transport.getSessionID() + for auth := AuthMethod(new(noneAuth)); auth != nil; { + ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand) + if err != nil { + return err + } + if ok == authSuccess { + // success + return nil + } else if ok == authFailure { + tried[auth.method()] = true + } + if methods == nil { + methods = lastMethods + } + lastMethods = methods + + auth = nil + + findNext: + for _, a := range config.Auth { + candidateMethod := a.method() + if tried[candidateMethod] { + continue + } + for _, meth := range methods { + if meth == candidateMethod { + auth = a + break findNext + } + } + } + } + return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried)) +} + +func keys(m map[string]bool) []string { + s := make([]string, 0, len(m)) + + for key := range m { + s = append(s, key) + } + return s +} + +// An AuthMethod represents an instance of an RFC 4252 authentication method. +type AuthMethod interface { + // auth authenticates user over transport t. + // Returns true if authentication is successful. + // If authentication is not successful, a []string of alternative + // method names is returned. If the slice is nil, it will be ignored + // and the previous set of possible methods will be reused. + auth(session []byte, user string, p packetConn, rand io.Reader) (authResult, []string, error) + + // method returns the RFC 4252 method name. + method() string +} + +// "none" authentication, RFC 4252 section 5.2. +type noneAuth int + +func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { + if err := c.writePacket(Marshal(&userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: "none", + })); err != nil { + return authFailure, nil, err + } + + return handleAuthResponse(c) +} + +func (n *noneAuth) method() string { + return "none" +} + +// passwordCallback is an AuthMethod that fetches the password through +// a function call, e.g. by prompting the user. +type passwordCallback func() (password string, err error) + +func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { + type passwordAuthMsg struct { + User string `sshtype:"50"` + Service string + Method string + Reply bool + Password string + } + + pw, err := cb() + // REVIEW NOTE: is there a need to support skipping a password attempt? + // The program may only find out that the user doesn't have a password + // when prompting. + if err != nil { + return authFailure, nil, err + } + + if err := c.writePacket(Marshal(&passwordAuthMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + Reply: false, + Password: pw, + })); err != nil { + return authFailure, nil, err + } + + return handleAuthResponse(c) +} + +func (cb passwordCallback) method() string { + return "password" +} + +// Password returns an AuthMethod using the given password. +func Password(secret string) AuthMethod { + return passwordCallback(func() (string, error) { return secret, nil }) +} + +// PasswordCallback returns an AuthMethod that uses a callback for +// fetching a password. +func PasswordCallback(prompt func() (secret string, err error)) AuthMethod { + return passwordCallback(prompt) +} + +type publickeyAuthMsg struct { + User string `sshtype:"50"` + Service string + Method string + // HasSig indicates to the receiver packet that the auth request is signed and + // should be used for authentication of the request. + HasSig bool + Algoname string + PubKey []byte + // Sig is tagged with "rest" so Marshal will exclude it during + // validateKey + Sig []byte `ssh:"rest"` +} + +// publicKeyCallback is an AuthMethod that uses a set of key +// pairs for authentication. +type publicKeyCallback func() ([]Signer, error) + +func (cb publicKeyCallback) method() string { + return "publickey" +} + +func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { + // Authentication is performed by sending an enquiry to test if a key is + // acceptable to the remote. If the key is acceptable, the client will + // attempt to authenticate with the valid key. If not the client will repeat + // the process with the remaining keys. + + signers, err := cb() + if err != nil { + return authFailure, nil, err + } + var methods []string + for _, signer := range signers { + ok, err := validateKey(signer.PublicKey(), user, c) + if err != nil { + return authFailure, nil, err + } + if !ok { + continue + } + + pub := signer.PublicKey() + pubKey := pub.Marshal() + sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + }, []byte(pub.Type()), pubKey)) + if err != nil { + return authFailure, nil, err + } + + // manually wrap the serialized signature in a string + s := Marshal(sign) + sig := make([]byte, stringLength(len(s))) + marshalString(sig, s) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + HasSig: true, + Algoname: pub.Type(), + PubKey: pubKey, + Sig: sig, + } + p := Marshal(&msg) + if err := c.writePacket(p); err != nil { + return authFailure, nil, err + } + var success authResult + success, methods, err = handleAuthResponse(c) + if err != nil { + return authFailure, nil, err + } + + // If authentication succeeds or the list of available methods does not + // contain the "publickey" method, do not attempt to authenticate with any + // other keys. According to RFC 4252 Section 7, the latter can occur when + // additional authentication methods are required. + if success == authSuccess || !containsMethod(methods, cb.method()) { + return success, methods, err + } + } + + return authFailure, methods, nil +} + +func containsMethod(methods []string, method string) bool { + for _, m := range methods { + if m == method { + return true + } + } + + return false +} + +// validateKey validates the key provided is acceptable to the server. +func validateKey(key PublicKey, user string, c packetConn) (bool, error) { + pubKey := key.Marshal() + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: "publickey", + HasSig: false, + Algoname: key.Type(), + PubKey: pubKey, + } + if err := c.writePacket(Marshal(&msg)); err != nil { + return false, err + } + + return confirmKeyAck(key, c) +} + +func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { + pubKey := key.Marshal() + algoname := key.Type() + + for { + packet, err := c.readPacket() + if err != nil { + return false, err + } + switch packet[0] { + case msgUserAuthBanner: + if err := handleBannerResponse(c, packet); err != nil { + return false, err + } + case msgUserAuthPubKeyOk: + var msg userAuthPubKeyOkMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, err + } + if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) { + return false, nil + } + return true, nil + case msgUserAuthFailure: + return false, nil + default: + return false, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + } + } +} + +// PublicKeys returns an AuthMethod that uses the given key +// pairs. +func PublicKeys(signers ...Signer) AuthMethod { + return publicKeyCallback(func() ([]Signer, error) { return signers, nil }) +} + +// PublicKeysCallback returns an AuthMethod that runs the given +// function to obtain a list of key pairs. +func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod { + return publicKeyCallback(getSigners) +} + +// handleAuthResponse returns whether the preceding authentication request succeeded +// along with a list of remaining authentication methods to try next and +// an error if an unexpected response was received. +func handleAuthResponse(c packetConn) (authResult, []string, error) { + for { + packet, err := c.readPacket() + if err != nil { + return authFailure, nil, err + } + + switch packet[0] { + case msgUserAuthBanner: + if err := handleBannerResponse(c, packet); err != nil { + return authFailure, nil, err + } + case msgUserAuthFailure: + var msg userAuthFailureMsg + if err := Unmarshal(packet, &msg); err != nil { + return authFailure, nil, err + } + if msg.PartialSuccess { + return authPartialSuccess, msg.Methods, nil + } + return authFailure, msg.Methods, nil + case msgUserAuthSuccess: + return authSuccess, nil, nil + default: + return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + } + } +} + +func handleBannerResponse(c packetConn, packet []byte) error { + var msg userAuthBannerMsg + if err := Unmarshal(packet, &msg); err != nil { + return err + } + + transport, ok := c.(*handshakeTransport) + if !ok { + return nil + } + + if transport.bannerCallback != nil { + return transport.bannerCallback(msg.Message) + } + + return nil +} + +// KeyboardInteractiveChallenge should print questions, optionally +// disabling echoing (e.g. for passwords), and return all the answers. +// Challenge may be called multiple times in a single session. After +// successful authentication, the server may send a challenge with no +// questions, for which the user and instruction messages should be +// printed. RFC 4256 section 3.3 details how the UI should behave for +// both CLI and GUI environments. +type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) + +// KeyboardInteractive returns an AuthMethod using a prompt/response +// sequence controlled by the server. +func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod { + return challenge +} + +func (cb KeyboardInteractiveChallenge) method() string { + return "keyboard-interactive" +} + +func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { + type initiateMsg struct { + User string `sshtype:"50"` + Service string + Method string + Language string + Submethods string + } + + if err := c.writePacket(Marshal(&initiateMsg{ + User: user, + Service: serviceSSH, + Method: "keyboard-interactive", + })); err != nil { + return authFailure, nil, err + } + + for { + packet, err := c.readPacket() + if err != nil { + return authFailure, nil, err + } + + // like handleAuthResponse, but with less options. + switch packet[0] { + case msgUserAuthBanner: + if err := handleBannerResponse(c, packet); err != nil { + return authFailure, nil, err + } + continue + case msgUserAuthInfoRequest: + // OK + case msgUserAuthFailure: + var msg userAuthFailureMsg + if err := Unmarshal(packet, &msg); err != nil { + return authFailure, nil, err + } + if msg.PartialSuccess { + return authPartialSuccess, msg.Methods, nil + } + return authFailure, msg.Methods, nil + case msgUserAuthSuccess: + return authSuccess, nil, nil + default: + return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) + } + + var msg userAuthInfoRequestMsg + if err := Unmarshal(packet, &msg); err != nil { + return authFailure, nil, err + } + + // Manually unpack the prompt/echo pairs. + rest := msg.Prompts + var prompts []string + var echos []bool + for i := 0; i < int(msg.NumPrompts); i++ { + prompt, r, ok := parseString(rest) + if !ok || len(r) == 0 { + return authFailure, nil, errors.New("ssh: prompt format error") + } + prompts = append(prompts, string(prompt)) + echos = append(echos, r[0] != 0) + rest = r[1:] + } + + if len(rest) != 0 { + return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs") + } + + answers, err := cb(msg.User, msg.Instruction, prompts, echos) + if err != nil { + return authFailure, nil, err + } + + if len(answers) != len(prompts) { + return authFailure, nil, errors.New("ssh: not enough answers from keyboard-interactive callback") + } + responseLength := 1 + 4 + for _, a := range answers { + responseLength += stringLength(len(a)) + } + serialized := make([]byte, responseLength) + p := serialized + p[0] = msgUserAuthInfoResponse + p = p[1:] + p = marshalUint32(p, uint32(len(answers))) + for _, a := range answers { + p = marshalString(p, []byte(a)) + } + + if err := c.writePacket(serialized); err != nil { + return authFailure, nil, err + } + } +} + +type retryableAuthMethod struct { + authMethod AuthMethod + maxTries int +} + +func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok authResult, methods []string, err error) { + for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ { + ok, methods, err = r.authMethod.auth(session, user, c, rand) + if ok != authFailure || err != nil { // either success, partial success or error terminate + return ok, methods, err + } + } + return ok, methods, err +} + +func (r *retryableAuthMethod) method() string { + return r.authMethod.method() +} + +// RetryableAuthMethod is a decorator for other auth methods enabling them to +// be retried up to maxTries before considering that AuthMethod itself failed. +// If maxTries is <= 0, will retry indefinitely +// +// This is useful for interactive clients using challenge/response type +// authentication (e.g. Keyboard-Interactive, Password, etc) where the user +// could mistype their response resulting in the server issuing a +// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4 +// [keyboard-interactive]); Without this decorator, the non-retryable +// AuthMethod would be removed from future consideration, and never tried again +// (and so the user would never be able to retry their entry). +func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod { + return &retryableAuthMethod{authMethod: auth, maxTries: maxTries} +} + +// GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication. +// See RFC 4462 section 3 +// gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details. +// target is the server host you want to log in to. +func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod { + if gssAPIClient == nil { + panic("gss-api client must be not nil with enable gssapi-with-mic") + } + return &gssAPIWithMICCallback{gssAPIClient: gssAPIClient, target: target} +} + +type gssAPIWithMICCallback struct { + gssAPIClient GSSAPIClient + target string +} + +func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) { + m := &userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: g.method(), + } + // The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST. + // See RFC 4462 section 3.2. + m.Payload = appendU32(m.Payload, 1) + m.Payload = appendString(m.Payload, string(krb5OID)) + if err := c.writePacket(Marshal(m)); err != nil { + return authFailure, nil, err + } + // The server responds to the SSH_MSG_USERAUTH_REQUEST with either an + // SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or + // with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE. + // See RFC 4462 section 3.3. + // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check + // selected mech if it is valid. + packet, err := c.readPacket() + if err != nil { + return authFailure, nil, err + } + userAuthGSSAPIResp := &userAuthGSSAPIResponse{} + if err := Unmarshal(packet, userAuthGSSAPIResp); err != nil { + return authFailure, nil, err + } + // Start the loop into the exchange token. + // See RFC 4462 section 3.4. + var token []byte + defer g.gssAPIClient.DeleteSecContext() + for { + // Initiates the establishment of a security context between the application and a remote peer. + nextToken, needContinue, err := g.gssAPIClient.InitSecContext("host@"+g.target, token, false) + if err != nil { + return authFailure, nil, err + } + if len(nextToken) > 0 { + if err := c.writePacket(Marshal(&userAuthGSSAPIToken{ + Token: nextToken, + })); err != nil { + return authFailure, nil, err + } + } + if !needContinue { + break + } + packet, err = c.readPacket() + if err != nil { + return authFailure, nil, err + } + switch packet[0] { + case msgUserAuthFailure: + var msg userAuthFailureMsg + if err := Unmarshal(packet, &msg); err != nil { + return authFailure, nil, err + } + if msg.PartialSuccess { + return authPartialSuccess, msg.Methods, nil + } + return authFailure, msg.Methods, nil + case msgUserAuthGSSAPIError: + userAuthGSSAPIErrorResp := &userAuthGSSAPIError{} + if err := Unmarshal(packet, userAuthGSSAPIErrorResp); err != nil { + return authFailure, nil, err + } + return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+ + "Major Status: %d\n"+ + "Minor Status: %d\n"+ + "Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus, + userAuthGSSAPIErrorResp.Message) + case msgUserAuthGSSAPIToken: + userAuthGSSAPITokenReq := &userAuthGSSAPIToken{} + if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil { + return authFailure, nil, err + } + token = userAuthGSSAPITokenReq.Token + } + } + // Binding Encryption Keys. + // See RFC 4462 section 3.5. + micField := buildMIC(string(session), user, "ssh-connection", "gssapi-with-mic") + micToken, err := g.gssAPIClient.GetMIC(micField) + if err != nil { + return authFailure, nil, err + } + if err := c.writePacket(Marshal(&userAuthGSSAPIMIC{ + MIC: micToken, + })); err != nil { + return authFailure, nil, err + } + return handleAuthResponse(c) +} + +func (g *gssAPIWithMICCallback) method() string { + return "gssapi-with-mic" +} diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go new file mode 100644 index 00000000000..290382d059e --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -0,0 +1,404 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto" + "crypto/rand" + "fmt" + "io" + "math" + "sync" + + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" +) + +// These are string constants in the SSH protocol. +const ( + compressionNone = "none" + serviceUserAuth = "ssh-userauth" + serviceSSH = "ssh-connection" +) + +// supportedCiphers lists ciphers we support but might not recommend. +var supportedCiphers = []string{ + "aes128-ctr", "aes192-ctr", "aes256-ctr", + "aes128-gcm@openssh.com", + chacha20Poly1305ID, + "arcfour256", "arcfour128", "arcfour", + aes128cbcID, + tripledescbcID, +} + +// preferredCiphers specifies the default preference for ciphers. +var preferredCiphers = []string{ + "aes128-gcm@openssh.com", + chacha20Poly1305ID, + "aes128-ctr", "aes192-ctr", "aes256-ctr", +} + +// supportedKexAlgos specifies the supported key-exchange algorithms in +// preference order. +var supportedKexAlgos = []string{ + kexAlgoCurve25519SHA256, + // P384 and P521 are not constant-time yet, but since we don't + // reuse ephemeral keys, using them for ECDH should be OK. + kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, + kexAlgoDH14SHA1, kexAlgoDH1SHA1, +} + +// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden +// for the server half. +var serverForbiddenKexAlgos = map[string]struct{}{ + kexAlgoDHGEXSHA1: {}, // server half implementation is only minimal to satisfy the automated tests + kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests +} + +// preferredKexAlgos specifies the default preference for key-exchange algorithms +// in preference order. +var preferredKexAlgos = []string{ + kexAlgoCurve25519SHA256, + kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, + kexAlgoDH14SHA1, +} + +// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods +// of authenticating servers) in preference order. +var supportedHostKeyAlgos = []string{ + CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, + CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01, + + KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, + KeyAlgoRSA, KeyAlgoDSA, + + KeyAlgoED25519, +} + +// supportedMACs specifies a default set of MAC algorithms in preference order. +// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed +// because they have reached the end of their useful life. +var supportedMACs = []string{ + "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", +} + +var supportedCompressions = []string{compressionNone} + +// hashFuncs keeps the mapping of supported algorithms to their respective +// hashes needed for signature verification. +var hashFuncs = map[string]crypto.Hash{ + KeyAlgoRSA: crypto.SHA1, + KeyAlgoDSA: crypto.SHA1, + KeyAlgoECDSA256: crypto.SHA256, + KeyAlgoECDSA384: crypto.SHA384, + KeyAlgoECDSA521: crypto.SHA512, + CertAlgoRSAv01: crypto.SHA1, + CertAlgoDSAv01: crypto.SHA1, + CertAlgoECDSA256v01: crypto.SHA256, + CertAlgoECDSA384v01: crypto.SHA384, + CertAlgoECDSA521v01: crypto.SHA512, +} + +// unexpectedMessageError results when the SSH message that we received didn't +// match what we wanted. +func unexpectedMessageError(expected, got uint8) error { + return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected) +} + +// parseError results from a malformed SSH message. +func parseError(tag uint8) error { + return fmt.Errorf("ssh: parse error in message type %d", tag) +} + +func findCommon(what string, client []string, server []string) (common string, err error) { + for _, c := range client { + for _, s := range server { + if c == s { + return c, nil + } + } + } + return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server) +} + +// directionAlgorithms records algorithm choices in one direction (either read or write) +type directionAlgorithms struct { + Cipher string + MAC string + Compression string +} + +// rekeyBytes returns a rekeying intervals in bytes. +func (a *directionAlgorithms) rekeyBytes() int64 { + // According to RFC4344 block ciphers should rekey after + // 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is + // 128. + switch a.Cipher { + case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcmCipherID, aes128cbcID: + return 16 * (1 << 32) + + } + + // For others, stick with RFC4253 recommendation to rekey after 1 Gb of data. + return 1 << 30 +} + +type algorithms struct { + kex string + hostKey string + w directionAlgorithms + r directionAlgorithms +} + +func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) { + result := &algorithms{} + + result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos) + if err != nil { + return + } + + result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) + if err != nil { + return + } + + stoc, ctos := &result.w, &result.r + if isClient { + ctos, stoc = stoc, ctos + } + + ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) + if err != nil { + return + } + + stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) + if err != nil { + return + } + + ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) + if err != nil { + return + } + + stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) + if err != nil { + return + } + + ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) + if err != nil { + return + } + + stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) + if err != nil { + return + } + + return result, nil +} + +// If rekeythreshold is too small, we can't make any progress sending +// stuff. +const minRekeyThreshold uint64 = 256 + +// Config contains configuration data common to both ServerConfig and +// ClientConfig. +type Config struct { + // Rand provides the source of entropy for cryptographic + // primitives. If Rand is nil, the cryptographic random reader + // in package crypto/rand will be used. + Rand io.Reader + + // The maximum number of bytes sent or received after which a + // new key is negotiated. It must be at least 256. If + // unspecified, a size suitable for the chosen cipher is used. + RekeyThreshold uint64 + + // The allowed key exchanges algorithms. If unspecified then a + // default set of algorithms is used. + KeyExchanges []string + + // The allowed cipher algorithms. If unspecified then a sensible + // default is used. + Ciphers []string + + // The allowed MAC algorithms. If unspecified then a sensible default + // is used. + MACs []string +} + +// SetDefaults sets sensible values for unset fields in config. This is +// exported for testing: Configs passed to SSH functions are copied and have +// default values set automatically. +func (c *Config) SetDefaults() { + if c.Rand == nil { + c.Rand = rand.Reader + } + if c.Ciphers == nil { + c.Ciphers = preferredCiphers + } + var ciphers []string + for _, c := range c.Ciphers { + if cipherModes[c] != nil { + // reject the cipher if we have no cipherModes definition + ciphers = append(ciphers, c) + } + } + c.Ciphers = ciphers + + if c.KeyExchanges == nil { + c.KeyExchanges = preferredKexAlgos + } + + if c.MACs == nil { + c.MACs = supportedMACs + } + + if c.RekeyThreshold == 0 { + // cipher specific default + } else if c.RekeyThreshold < minRekeyThreshold { + c.RekeyThreshold = minRekeyThreshold + } else if c.RekeyThreshold >= math.MaxInt64 { + // Avoid weirdness if somebody uses -1 as a threshold. + c.RekeyThreshold = math.MaxInt64 + } +} + +// buildDataSignedForAuth returns the data that is signed in order to prove +// possession of a private key. See RFC 4252, section 7. +func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { + data := struct { + Session []byte + Type byte + User string + Service string + Method string + Sign bool + Algo []byte + PubKey []byte + }{ + sessionID, + msgUserAuthRequest, + req.User, + req.Service, + req.Method, + true, + algo, + pubKey, + } + return Marshal(data) +} + +func appendU16(buf []byte, n uint16) []byte { + return append(buf, byte(n>>8), byte(n)) +} + +func appendU32(buf []byte, n uint32) []byte { + return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) +} + +func appendU64(buf []byte, n uint64) []byte { + return append(buf, + byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), + byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) +} + +func appendInt(buf []byte, n int) []byte { + return appendU32(buf, uint32(n)) +} + +func appendString(buf []byte, s string) []byte { + buf = appendU32(buf, uint32(len(s))) + buf = append(buf, s...) + return buf +} + +func appendBool(buf []byte, b bool) []byte { + if b { + return append(buf, 1) + } + return append(buf, 0) +} + +// newCond is a helper to hide the fact that there is no usable zero +// value for sync.Cond. +func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } + +// window represents the buffer available to clients +// wishing to write to a channel. +type window struct { + *sync.Cond + win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 + writeWaiters int + closed bool +} + +// add adds win to the amount of window available +// for consumers. +func (w *window) add(win uint32) bool { + // a zero sized window adjust is a noop. + if win == 0 { + return true + } + w.L.Lock() + if w.win+win < win { + w.L.Unlock() + return false + } + w.win += win + // It is unusual that multiple goroutines would be attempting to reserve + // window space, but not guaranteed. Use broadcast to notify all waiters + // that additional window is available. + w.Broadcast() + w.L.Unlock() + return true +} + +// close sets the window to closed, so all reservations fail +// immediately. +func (w *window) close() { + w.L.Lock() + w.closed = true + w.Broadcast() + w.L.Unlock() +} + +// reserve reserves win from the available window capacity. +// If no capacity remains, reserve will block. reserve may +// return less than requested. +func (w *window) reserve(win uint32) (uint32, error) { + var err error + w.L.Lock() + w.writeWaiters++ + w.Broadcast() + for w.win == 0 && !w.closed { + w.Wait() + } + w.writeWaiters-- + if w.win < win { + win = w.win + } + w.win -= win + if w.closed { + err = io.EOF + } + w.L.Unlock() + return win, err +} + +// waitWriterBlocked waits until some goroutine is blocked for further +// writes. It is used in tests only. +func (w *window) waitWriterBlocked() { + w.Cond.L.Lock() + for w.writeWaiters == 0 { + w.Cond.Wait() + } + w.Cond.L.Unlock() +} diff --git a/vendor/golang.org/x/crypto/ssh/connection.go b/vendor/golang.org/x/crypto/ssh/connection.go new file mode 100644 index 00000000000..fd6b0681b51 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/connection.go @@ -0,0 +1,143 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "fmt" + "net" +) + +// OpenChannelError is returned if the other side rejects an +// OpenChannel request. +type OpenChannelError struct { + Reason RejectionReason + Message string +} + +func (e *OpenChannelError) Error() string { + return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) +} + +// ConnMetadata holds metadata for the connection. +type ConnMetadata interface { + // User returns the user ID for this connection. + User() string + + // SessionID returns the session hash, also denoted by H. + SessionID() []byte + + // ClientVersion returns the client's version string as hashed + // into the session ID. + ClientVersion() []byte + + // ServerVersion returns the server's version string as hashed + // into the session ID. + ServerVersion() []byte + + // RemoteAddr returns the remote address for this connection. + RemoteAddr() net.Addr + + // LocalAddr returns the local address for this connection. + LocalAddr() net.Addr +} + +// Conn represents an SSH connection for both server and client roles. +// Conn is the basis for implementing an application layer, such +// as ClientConn, which implements the traditional shell access for +// clients. +type Conn interface { + ConnMetadata + + // SendRequest sends a global request, and returns the + // reply. If wantReply is true, it returns the response status + // and payload. See also RFC4254, section 4. + SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) + + // OpenChannel tries to open an channel. If the request is + // rejected, it returns *OpenChannelError. On success it returns + // the SSH Channel and a Go channel for incoming, out-of-band + // requests. The Go channel must be serviced, or the + // connection will hang. + OpenChannel(name string, data []byte) (Channel, <-chan *Request, error) + + // Close closes the underlying network connection + Close() error + + // Wait blocks until the connection has shut down, and returns the + // error causing the shutdown. + Wait() error + + // TODO(hanwen): consider exposing: + // RequestKeyChange + // Disconnect +} + +// DiscardRequests consumes and rejects all requests from the +// passed-in channel. +func DiscardRequests(in <-chan *Request) { + for req := range in { + if req.WantReply { + req.Reply(false, nil) + } + } +} + +// A connection represents an incoming connection. +type connection struct { + transport *handshakeTransport + sshConn + + // The connection protocol. + *mux +} + +func (c *connection) Close() error { + return c.sshConn.conn.Close() +} + +// sshconn provides net.Conn metadata, but disallows direct reads and +// writes. +type sshConn struct { + conn net.Conn + + user string + sessionID []byte + clientVersion []byte + serverVersion []byte +} + +func dup(src []byte) []byte { + dst := make([]byte, len(src)) + copy(dst, src) + return dst +} + +func (c *sshConn) User() string { + return c.user +} + +func (c *sshConn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *sshConn) Close() error { + return c.conn.Close() +} + +func (c *sshConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *sshConn) SessionID() []byte { + return dup(c.sessionID) +} + +func (c *sshConn) ClientVersion() []byte { + return dup(c.clientVersion) +} + +func (c *sshConn) ServerVersion() []byte { + return dup(c.serverVersion) +} diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go new file mode 100644 index 00000000000..67b7322c058 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -0,0 +1,21 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package ssh implements an SSH client and server. + +SSH is a transport security protocol, an authentication protocol and a +family of application protocols. The most typical application level +protocol is a remote shell and this is specifically implemented. However, +the multiplexed nature of SSH is exposed to users that wish to support +others. + +References: + [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD + [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 + +This package does not fall under the stability promise of the Go language itself, +so its API may be changed when pressing needs arise. +*/ +package ssh // import "golang.org/x/crypto/ssh" diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go new file mode 100644 index 00000000000..2b10b05a498 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -0,0 +1,647 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto/rand" + "errors" + "fmt" + "io" + "log" + "net" + "sync" +) + +// debugHandshake, if set, prints messages sent and received. Key +// exchange messages are printed as if DH were used, so the debug +// messages are wrong when using ECDH. +const debugHandshake = false + +// chanSize sets the amount of buffering SSH connections. This is +// primarily for testing: setting chanSize=0 uncovers deadlocks more +// quickly. +const chanSize = 16 + +// keyingTransport is a packet based transport that supports key +// changes. It need not be thread-safe. It should pass through +// msgNewKeys in both directions. +type keyingTransport interface { + packetConn + + // prepareKeyChange sets up a key change. The key change for a + // direction will be effected if a msgNewKeys message is sent + // or received. + prepareKeyChange(*algorithms, *kexResult) error +} + +// handshakeTransport implements rekeying on top of a keyingTransport +// and offers a thread-safe writePacket() interface. +type handshakeTransport struct { + conn keyingTransport + config *Config + + serverVersion []byte + clientVersion []byte + + // hostKeys is non-empty if we are the server. In that case, + // it contains all host keys that can be used to sign the + // connection. + hostKeys []Signer + + // hostKeyAlgorithms is non-empty if we are the client. In that case, + // we accept these key types from the server as host key. + hostKeyAlgorithms []string + + // On read error, incoming is closed, and readError is set. + incoming chan []byte + readError error + + mu sync.Mutex + writeError error + sentInitPacket []byte + sentInitMsg *kexInitMsg + pendingPackets [][]byte // Used when a key exchange is in progress. + + // If the read loop wants to schedule a kex, it pings this + // channel, and the write loop will send out a kex + // message. + requestKex chan struct{} + + // If the other side requests or confirms a kex, its kexInit + // packet is sent here for the write loop to find it. + startKex chan *pendingKex + + // data for host key checking + hostKeyCallback HostKeyCallback + dialAddress string + remoteAddr net.Addr + + // bannerCallback is non-empty if we are the client and it has been set in + // ClientConfig. In that case it is called during the user authentication + // dance to handle a custom server's message. + bannerCallback BannerCallback + + // Algorithms agreed in the last key exchange. + algorithms *algorithms + + readPacketsLeft uint32 + readBytesLeft int64 + + writePacketsLeft uint32 + writeBytesLeft int64 + + // The session ID or nil if first kex did not complete yet. + sessionID []byte +} + +type pendingKex struct { + otherInit []byte + done chan error +} + +func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { + t := &handshakeTransport{ + conn: conn, + serverVersion: serverVersion, + clientVersion: clientVersion, + incoming: make(chan []byte, chanSize), + requestKex: make(chan struct{}, 1), + startKex: make(chan *pendingKex, 1), + + config: config, + } + t.resetReadThresholds() + t.resetWriteThresholds() + + // We always start with a mandatory key exchange. + t.requestKex <- struct{}{} + return t +} + +func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ClientConfig, dialAddr string, addr net.Addr) *handshakeTransport { + t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) + t.dialAddress = dialAddr + t.remoteAddr = addr + t.hostKeyCallback = config.HostKeyCallback + t.bannerCallback = config.BannerCallback + if config.HostKeyAlgorithms != nil { + t.hostKeyAlgorithms = config.HostKeyAlgorithms + } else { + t.hostKeyAlgorithms = supportedHostKeyAlgos + } + go t.readLoop() + go t.kexLoop() + return t +} + +func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport { + t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) + t.hostKeys = config.hostKeys + go t.readLoop() + go t.kexLoop() + return t +} + +func (t *handshakeTransport) getSessionID() []byte { + return t.sessionID +} + +// waitSession waits for the session to be established. This should be +// the first thing to call after instantiating handshakeTransport. +func (t *handshakeTransport) waitSession() error { + p, err := t.readPacket() + if err != nil { + return err + } + if p[0] != msgNewKeys { + return fmt.Errorf("ssh: first packet should be msgNewKeys") + } + + return nil +} + +func (t *handshakeTransport) id() string { + if len(t.hostKeys) > 0 { + return "server" + } + return "client" +} + +func (t *handshakeTransport) printPacket(p []byte, write bool) { + action := "got" + if write { + action = "sent" + } + + if p[0] == msgChannelData || p[0] == msgChannelExtendedData { + log.Printf("%s %s data (packet %d bytes)", t.id(), action, len(p)) + } else { + msg, err := decode(p) + log.Printf("%s %s %T %v (%v)", t.id(), action, msg, msg, err) + } +} + +func (t *handshakeTransport) readPacket() ([]byte, error) { + p, ok := <-t.incoming + if !ok { + return nil, t.readError + } + return p, nil +} + +func (t *handshakeTransport) readLoop() { + first := true + for { + p, err := t.readOnePacket(first) + first = false + if err != nil { + t.readError = err + close(t.incoming) + break + } + if p[0] == msgIgnore || p[0] == msgDebug { + continue + } + t.incoming <- p + } + + // Stop writers too. + t.recordWriteError(t.readError) + + // Unblock the writer should it wait for this. + close(t.startKex) + + // Don't close t.requestKex; it's also written to from writePacket. +} + +func (t *handshakeTransport) pushPacket(p []byte) error { + if debugHandshake { + t.printPacket(p, true) + } + return t.conn.writePacket(p) +} + +func (t *handshakeTransport) getWriteError() error { + t.mu.Lock() + defer t.mu.Unlock() + return t.writeError +} + +func (t *handshakeTransport) recordWriteError(err error) { + t.mu.Lock() + defer t.mu.Unlock() + if t.writeError == nil && err != nil { + t.writeError = err + } +} + +func (t *handshakeTransport) requestKeyExchange() { + select { + case t.requestKex <- struct{}{}: + default: + // something already requested a kex, so do nothing. + } +} + +func (t *handshakeTransport) resetWriteThresholds() { + t.writePacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.writeBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.writeBytesLeft = t.algorithms.w.rekeyBytes() + } else { + t.writeBytesLeft = 1 << 30 + } +} + +func (t *handshakeTransport) kexLoop() { + +write: + for t.getWriteError() == nil { + var request *pendingKex + var sent bool + + for request == nil || !sent { + var ok bool + select { + case request, ok = <-t.startKex: + if !ok { + break write + } + case <-t.requestKex: + break + } + + if !sent { + if err := t.sendKexInit(); err != nil { + t.recordWriteError(err) + break + } + sent = true + } + } + + if err := t.getWriteError(); err != nil { + if request != nil { + request.done <- err + } + break + } + + // We're not servicing t.requestKex, but that is OK: + // we never block on sending to t.requestKex. + + // We're not servicing t.startKex, but the remote end + // has just sent us a kexInitMsg, so it can't send + // another key change request, until we close the done + // channel on the pendingKex request. + + err := t.enterKeyExchange(request.otherInit) + + t.mu.Lock() + t.writeError = err + t.sentInitPacket = nil + t.sentInitMsg = nil + + t.resetWriteThresholds() + + // we have completed the key exchange. Since the + // reader is still blocked, it is safe to clear out + // the requestKex channel. This avoids the situation + // where: 1) we consumed our own request for the + // initial kex, and 2) the kex from the remote side + // caused another send on the requestKex channel, + clear: + for { + select { + case <-t.requestKex: + // + default: + break clear + } + } + + request.done <- t.writeError + + // kex finished. Push packets that we received while + // the kex was in progress. Don't look at t.startKex + // and don't increment writtenSinceKex: if we trigger + // another kex while we are still busy with the last + // one, things will become very confusing. + for _, p := range t.pendingPackets { + t.writeError = t.pushPacket(p) + if t.writeError != nil { + break + } + } + t.pendingPackets = t.pendingPackets[:0] + t.mu.Unlock() + } + + // drain startKex channel. We don't service t.requestKex + // because nobody does blocking sends there. + go func() { + for init := range t.startKex { + init.done <- t.writeError + } + }() + + // Unblock reader. + t.conn.Close() +} + +// The protocol uses uint32 for packet counters, so we can't let them +// reach 1<<32. We will actually read and write more packets than +// this, though: the other side may send more packets, and after we +// hit this limit on writing we will send a few more packets for the +// key exchange itself. +const packetRekeyThreshold = (1 << 31) + +func (t *handshakeTransport) resetReadThresholds() { + t.readPacketsLeft = packetRekeyThreshold + if t.config.RekeyThreshold > 0 { + t.readBytesLeft = int64(t.config.RekeyThreshold) + } else if t.algorithms != nil { + t.readBytesLeft = t.algorithms.r.rekeyBytes() + } else { + t.readBytesLeft = 1 << 30 + } +} + +func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { + p, err := t.conn.readPacket() + if err != nil { + return nil, err + } + + if t.readPacketsLeft > 0 { + t.readPacketsLeft-- + } else { + t.requestKeyExchange() + } + + if t.readBytesLeft > 0 { + t.readBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() + } + + if debugHandshake { + t.printPacket(p, false) + } + + if first && p[0] != msgKexInit { + return nil, fmt.Errorf("ssh: first packet should be msgKexInit") + } + + if p[0] != msgKexInit { + return p, nil + } + + firstKex := t.sessionID == nil + + kex := pendingKex{ + done: make(chan error, 1), + otherInit: p, + } + t.startKex <- &kex + err = <-kex.done + + if debugHandshake { + log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err) + } + + if err != nil { + return nil, err + } + + t.resetReadThresholds() + + // By default, a key exchange is hidden from higher layers by + // translating it into msgIgnore. + successPacket := []byte{msgIgnore} + if firstKex { + // sendKexInit() for the first kex waits for + // msgNewKeys so the authentication process is + // guaranteed to happen over an encrypted transport. + successPacket = []byte{msgNewKeys} + } + + return successPacket, nil +} + +// sendKexInit sends a key change message. +func (t *handshakeTransport) sendKexInit() error { + t.mu.Lock() + defer t.mu.Unlock() + if t.sentInitMsg != nil { + // kexInits may be sent either in response to the other side, + // or because our side wants to initiate a key change, so we + // may have already sent a kexInit. In that case, don't send a + // second kexInit. + return nil + } + + msg := &kexInitMsg{ + KexAlgos: t.config.KeyExchanges, + CiphersClientServer: t.config.Ciphers, + CiphersServerClient: t.config.Ciphers, + MACsClientServer: t.config.MACs, + MACsServerClient: t.config.MACs, + CompressionClientServer: supportedCompressions, + CompressionServerClient: supportedCompressions, + } + io.ReadFull(rand.Reader, msg.Cookie[:]) + + if len(t.hostKeys) > 0 { + for _, k := range t.hostKeys { + msg.ServerHostKeyAlgos = append( + msg.ServerHostKeyAlgos, k.PublicKey().Type()) + } + } else { + msg.ServerHostKeyAlgos = t.hostKeyAlgorithms + } + packet := Marshal(msg) + + // writePacket destroys the contents, so save a copy. + packetCopy := make([]byte, len(packet)) + copy(packetCopy, packet) + + if err := t.pushPacket(packetCopy); err != nil { + return err + } + + t.sentInitMsg = msg + t.sentInitPacket = packet + + return nil +} + +func (t *handshakeTransport) writePacket(p []byte) error { + switch p[0] { + case msgKexInit: + return errors.New("ssh: only handshakeTransport can send kexInit") + case msgNewKeys: + return errors.New("ssh: only handshakeTransport can send newKeys") + } + + t.mu.Lock() + defer t.mu.Unlock() + if t.writeError != nil { + return t.writeError + } + + if t.sentInitMsg != nil { + // Copy the packet so the writer can reuse the buffer. + cp := make([]byte, len(p)) + copy(cp, p) + t.pendingPackets = append(t.pendingPackets, cp) + return nil + } + + if t.writeBytesLeft > 0 { + t.writeBytesLeft -= int64(len(p)) + } else { + t.requestKeyExchange() + } + + if t.writePacketsLeft > 0 { + t.writePacketsLeft-- + } else { + t.requestKeyExchange() + } + + if err := t.pushPacket(p); err != nil { + t.writeError = err + } + + return nil +} + +func (t *handshakeTransport) Close() error { + return t.conn.Close() +} + +func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { + if debugHandshake { + log.Printf("%s entered key exchange", t.id()) + } + + otherInit := &kexInitMsg{} + if err := Unmarshal(otherInitPacket, otherInit); err != nil { + return err + } + + magics := handshakeMagics{ + clientVersion: t.clientVersion, + serverVersion: t.serverVersion, + clientKexInit: otherInitPacket, + serverKexInit: t.sentInitPacket, + } + + clientInit := otherInit + serverInit := t.sentInitMsg + isClient := len(t.hostKeys) == 0 + if isClient { + clientInit, serverInit = serverInit, clientInit + + magics.clientKexInit = t.sentInitPacket + magics.serverKexInit = otherInitPacket + } + + var err error + t.algorithms, err = findAgreedAlgorithms(isClient, clientInit, serverInit) + if err != nil { + return err + } + + // We don't send FirstKexFollows, but we handle receiving it. + // + // RFC 4253 section 7 defines the kex and the agreement method for + // first_kex_packet_follows. It states that the guessed packet + // should be ignored if the "kex algorithm and/or the host + // key algorithm is guessed wrong (server and client have + // different preferred algorithm), or if any of the other + // algorithms cannot be agreed upon". The other algorithms have + // already been checked above so the kex algorithm and host key + // algorithm are checked here. + if otherInit.FirstKexFollows && (clientInit.KexAlgos[0] != serverInit.KexAlgos[0] || clientInit.ServerHostKeyAlgos[0] != serverInit.ServerHostKeyAlgos[0]) { + // other side sent a kex message for the wrong algorithm, + // which we have to ignore. + if _, err := t.conn.readPacket(); err != nil { + return err + } + } + + kex, ok := kexAlgoMap[t.algorithms.kex] + if !ok { + return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex) + } + + var result *kexResult + if len(t.hostKeys) > 0 { + result, err = t.server(kex, t.algorithms, &magics) + } else { + result, err = t.client(kex, t.algorithms, &magics) + } + + if err != nil { + return err + } + + if t.sessionID == nil { + t.sessionID = result.H + } + result.SessionID = t.sessionID + + if err := t.conn.prepareKeyChange(t.algorithms, result); err != nil { + return err + } + if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { + return err + } + if packet, err := t.conn.readPacket(); err != nil { + return err + } else if packet[0] != msgNewKeys { + return unexpectedMessageError(msgNewKeys, packet[0]) + } + + return nil +} + +func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { + var hostKey Signer + for _, k := range t.hostKeys { + if algs.hostKey == k.PublicKey().Type() { + hostKey = k + } + } + + r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey) + return r, err +} + +func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { + result, err := kex.Client(t.conn, t.config.Rand, magics) + if err != nil { + return nil, err + } + + hostKey, err := ParsePublicKey(result.HostKey) + if err != nil { + return nil, err + } + + if err := verifyHostKeySignature(hostKey, result); err != nil { + return nil, err + } + + err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/internal/bcrypt_pbkdf/bcrypt_pbkdf.go b/vendor/golang.org/x/crypto/ssh/internal/bcrypt_pbkdf/bcrypt_pbkdf.go new file mode 100644 index 00000000000..af81d266546 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/internal/bcrypt_pbkdf/bcrypt_pbkdf.go @@ -0,0 +1,93 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bcrypt_pbkdf implements bcrypt_pbkdf(3) from OpenBSD. +// +// See https://flak.tedunangst.com/post/bcrypt-pbkdf and +// https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libutil/bcrypt_pbkdf.c. +package bcrypt_pbkdf + +import ( + "crypto/sha512" + "errors" + "golang.org/x/crypto/blowfish" +) + +const blockSize = 32 + +// Key derives a key from the password, salt and rounds count, returning a +// []byte of length keyLen that can be used as cryptographic key. +func Key(password, salt []byte, rounds, keyLen int) ([]byte, error) { + if rounds < 1 { + return nil, errors.New("bcrypt_pbkdf: number of rounds is too small") + } + if len(password) == 0 { + return nil, errors.New("bcrypt_pbkdf: empty password") + } + if len(salt) == 0 || len(salt) > 1<<20 { + return nil, errors.New("bcrypt_pbkdf: bad salt length") + } + if keyLen > 1024 { + return nil, errors.New("bcrypt_pbkdf: keyLen is too large") + } + + numBlocks := (keyLen + blockSize - 1) / blockSize + key := make([]byte, numBlocks*blockSize) + + h := sha512.New() + h.Write(password) + shapass := h.Sum(nil) + + shasalt := make([]byte, 0, sha512.Size) + cnt, tmp := make([]byte, 4), make([]byte, blockSize) + for block := 1; block <= numBlocks; block++ { + h.Reset() + h.Write(salt) + cnt[0] = byte(block >> 24) + cnt[1] = byte(block >> 16) + cnt[2] = byte(block >> 8) + cnt[3] = byte(block) + h.Write(cnt) + bcryptHash(tmp, shapass, h.Sum(shasalt)) + + out := make([]byte, blockSize) + copy(out, tmp) + for i := 2; i <= rounds; i++ { + h.Reset() + h.Write(tmp) + bcryptHash(tmp, shapass, h.Sum(shasalt)) + for j := 0; j < len(out); j++ { + out[j] ^= tmp[j] + } + } + + for i, v := range out { + key[i*numBlocks+(block-1)] = v + } + } + return key[:keyLen], nil +} + +var magic = []byte("OxychromaticBlowfishSwatDynamite") + +func bcryptHash(out, shapass, shasalt []byte) { + c, err := blowfish.NewSaltedCipher(shapass, shasalt) + if err != nil { + panic(err) + } + for i := 0; i < 64; i++ { + blowfish.ExpandKey(shasalt, c) + blowfish.ExpandKey(shapass, c) + } + copy(out, magic) + for i := 0; i < 32; i += 8 { + for j := 0; j < 64; j++ { + c.Encrypt(out[i:i+8], out[i:i+8]) + } + } + // Swap bytes due to different endianness. + for i := 0; i < 32; i += 4 { + out[i+3], out[i+2], out[i+1], out[i] = out[i], out[i+1], out[i+2], out[i+3] + } +} diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go new file mode 100644 index 00000000000..6c3c648fc95 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -0,0 +1,789 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/subtle" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + + "golang.org/x/crypto/curve25519" +) + +const ( + kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" + kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" + kexAlgoECDH256 = "ecdh-sha2-nistp256" + kexAlgoECDH384 = "ecdh-sha2-nistp384" + kexAlgoECDH521 = "ecdh-sha2-nistp521" + kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org" + + // For the following kex only the client half contains a production + // ready implementation. The server half only consists of a minimal + // implementation to satisfy the automated tests. + kexAlgoDHGEXSHA1 = "diffie-hellman-group-exchange-sha1" + kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256" +) + +// kexResult captures the outcome of a key exchange. +type kexResult struct { + // Session hash. See also RFC 4253, section 8. + H []byte + + // Shared secret. See also RFC 4253, section 8. + K []byte + + // Host key as hashed into H. + HostKey []byte + + // Signature of H. + Signature []byte + + // A cryptographic hash function that matches the security + // level of the key exchange algorithm. It is used for + // calculating H, and for deriving keys from H and K. + Hash crypto.Hash + + // The session ID, which is the first H computed. This is used + // to derive key material inside the transport. + SessionID []byte +} + +// handshakeMagics contains data that is always included in the +// session hash. +type handshakeMagics struct { + clientVersion, serverVersion []byte + clientKexInit, serverKexInit []byte +} + +func (m *handshakeMagics) write(w io.Writer) { + writeString(w, m.clientVersion) + writeString(w, m.serverVersion) + writeString(w, m.clientKexInit) + writeString(w, m.serverKexInit) +} + +// kexAlgorithm abstracts different key exchange algorithms. +type kexAlgorithm interface { + // Server runs server-side key agreement, signing the result + // with a hostkey. + Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) + + // Client runs the client-side key agreement. Caller is + // responsible for verifying the host key signature. + Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) +} + +// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. +type dhGroup struct { + g, p, pMinus1 *big.Int +} + +func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { + if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 { + return nil, errors.New("ssh: DH parameter out of bounds") + } + return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil +} + +func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { + hashFunc := crypto.SHA1 + + var x *big.Int + for { + var err error + if x, err = rand.Int(randSource, group.pMinus1); err != nil { + return nil, err + } + if x.Sign() > 0 { + break + } + } + + X := new(big.Int).Exp(group.g, x, group.p) + kexDHInit := kexDHInitMsg{ + X: X, + } + if err := c.writePacket(Marshal(&kexDHInit)); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var kexDHReply kexDHReplyMsg + if err = Unmarshal(packet, &kexDHReply); err != nil { + return nil, err + } + + ki, err := group.diffieHellman(kexDHReply.Y, x) + if err != nil { + return nil, err + } + + h := hashFunc.New() + magics.write(h) + writeString(h, kexDHReply.HostKey) + writeInt(h, X) + writeInt(h, kexDHReply.Y) + K := make([]byte, intLength(ki)) + marshalInt(K, ki) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: kexDHReply.HostKey, + Signature: kexDHReply.Signature, + Hash: crypto.SHA1, + }, nil +} + +func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + hashFunc := crypto.SHA1 + packet, err := c.readPacket() + if err != nil { + return + } + var kexDHInit kexDHInitMsg + if err = Unmarshal(packet, &kexDHInit); err != nil { + return + } + + var y *big.Int + for { + if y, err = rand.Int(randSource, group.pMinus1); err != nil { + return + } + if y.Sign() > 0 { + break + } + } + + Y := new(big.Int).Exp(group.g, y, group.p) + ki, err := group.diffieHellman(kexDHInit.X, y) + if err != nil { + return nil, err + } + + hostKeyBytes := priv.PublicKey().Marshal() + + h := hashFunc.New() + magics.write(h) + writeString(h, hostKeyBytes) + writeInt(h, kexDHInit.X) + writeInt(h, Y) + + K := make([]byte, intLength(ki)) + marshalInt(K, ki) + h.Write(K) + + H := h.Sum(nil) + + // H is already a hash, but the hostkey signing will apply its + // own key-specific hash algorithm. + sig, err := signAndMarshal(priv, randSource, H) + if err != nil { + return nil, err + } + + kexDHReply := kexDHReplyMsg{ + HostKey: hostKeyBytes, + Y: Y, + Signature: sig, + } + packet = Marshal(&kexDHReply) + + err = c.writePacket(packet) + return &kexResult{ + H: H, + K: K, + HostKey: hostKeyBytes, + Signature: sig, + Hash: crypto.SHA1, + }, err +} + +// ecdh performs Elliptic Curve Diffie-Hellman key exchange as +// described in RFC 5656, section 4. +type ecdh struct { + curve elliptic.Curve +} + +func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { + ephKey, err := ecdsa.GenerateKey(kex.curve, rand) + if err != nil { + return nil, err + } + + kexInit := kexECDHInitMsg{ + ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), + } + + serialized := Marshal(&kexInit) + if err := c.writePacket(serialized); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var reply kexECDHReplyMsg + if err = Unmarshal(packet, &reply); err != nil { + return nil, err + } + + x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) + if err != nil { + return nil, err + } + + // generate shared secret + secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) + + h := ecHash(kex.curve).New() + magics.write(h) + writeString(h, reply.HostKey) + writeString(h, kexInit.ClientPubKey) + writeString(h, reply.EphemeralPubKey) + K := make([]byte, intLength(secret)) + marshalInt(K, secret) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: reply.HostKey, + Signature: reply.Signature, + Hash: ecHash(kex.curve), + }, nil +} + +// unmarshalECKey parses and checks an EC key. +func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { + x, y = elliptic.Unmarshal(curve, pubkey) + if x == nil { + return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") + } + if !validateECPublicKey(curve, x, y) { + return nil, nil, errors.New("ssh: public key not on curve") + } + return x, y, nil +} + +// validateECPublicKey checks that the point is a valid public key for +// the given curve. See [SEC1], 3.2.2 +func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { + if x.Sign() == 0 && y.Sign() == 0 { + return false + } + + if x.Cmp(curve.Params().P) >= 0 { + return false + } + + if y.Cmp(curve.Params().P) >= 0 { + return false + } + + if !curve.IsOnCurve(x, y) { + return false + } + + // We don't check if N * PubKey == 0, since + // + // - the NIST curves have cofactor = 1, so this is implicit. + // (We don't foresee an implementation that supports non NIST + // curves) + // + // - for ephemeral keys, we don't need to worry about small + // subgroup attacks. + return true +} + +func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var kexECDHInit kexECDHInitMsg + if err = Unmarshal(packet, &kexECDHInit); err != nil { + return nil, err + } + + clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) + if err != nil { + return nil, err + } + + // We could cache this key across multiple users/multiple + // connection attempts, but the benefit is small. OpenSSH + // generates a new key for each incoming connection. + ephKey, err := ecdsa.GenerateKey(kex.curve, rand) + if err != nil { + return nil, err + } + + hostKeyBytes := priv.PublicKey().Marshal() + + serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) + + // generate shared secret + secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) + + h := ecHash(kex.curve).New() + magics.write(h) + writeString(h, hostKeyBytes) + writeString(h, kexECDHInit.ClientPubKey) + writeString(h, serializedEphKey) + + K := make([]byte, intLength(secret)) + marshalInt(K, secret) + h.Write(K) + + H := h.Sum(nil) + + // H is already a hash, but the hostkey signing will apply its + // own key-specific hash algorithm. + sig, err := signAndMarshal(priv, rand, H) + if err != nil { + return nil, err + } + + reply := kexECDHReplyMsg{ + EphemeralPubKey: serializedEphKey, + HostKey: hostKeyBytes, + Signature: sig, + } + + serialized := Marshal(&reply) + if err := c.writePacket(serialized); err != nil { + return nil, err + } + + return &kexResult{ + H: H, + K: K, + HostKey: reply.HostKey, + Signature: sig, + Hash: ecHash(kex.curve), + }, nil +} + +var kexAlgoMap = map[string]kexAlgorithm{} + +func init() { + // This is the group called diffie-hellman-group1-sha1 in RFC + // 4253 and Oakley Group 2 in RFC 2409. + p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) + kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + pMinus1: new(big.Int).Sub(p, bigOne), + } + + // This is the group called diffie-hellman-group14-sha1 in RFC + // 4253 and Oakley Group 14 in RFC 3526. + p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) + + kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + pMinus1: new(big.Int).Sub(p, bigOne), + } + + kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} + kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} + kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} + kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{} + kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1} + kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256} +} + +// curve25519sha256 implements the curve25519-sha256@libssh.org key +// agreement protocol, as described in +// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt +type curve25519sha256 struct{} + +type curve25519KeyPair struct { + priv [32]byte + pub [32]byte +} + +func (kp *curve25519KeyPair) generate(rand io.Reader) error { + if _, err := io.ReadFull(rand, kp.priv[:]); err != nil { + return err + } + curve25519.ScalarBaseMult(&kp.pub, &kp.priv) + return nil +} + +// curve25519Zeros is just an array of 32 zero bytes so that we have something +// convenient to compare against in order to reject curve25519 points with the +// wrong order. +var curve25519Zeros [32]byte + +func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { + var kp curve25519KeyPair + if err := kp.generate(rand); err != nil { + return nil, err + } + if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var reply kexECDHReplyMsg + if err = Unmarshal(packet, &reply); err != nil { + return nil, err + } + if len(reply.EphemeralPubKey) != 32 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong length") + } + + var servPub, secret [32]byte + copy(servPub[:], reply.EphemeralPubKey) + curve25519.ScalarMult(&secret, &kp.priv, &servPub) + if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong order") + } + + h := crypto.SHA256.New() + magics.write(h) + writeString(h, reply.HostKey) + writeString(h, kp.pub[:]) + writeString(h, reply.EphemeralPubKey) + + ki := new(big.Int).SetBytes(secret[:]) + K := make([]byte, intLength(ki)) + marshalInt(K, ki) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: reply.HostKey, + Signature: reply.Signature, + Hash: crypto.SHA256, + }, nil +} + +func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + packet, err := c.readPacket() + if err != nil { + return + } + var kexInit kexECDHInitMsg + if err = Unmarshal(packet, &kexInit); err != nil { + return + } + + if len(kexInit.ClientPubKey) != 32 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong length") + } + + var kp curve25519KeyPair + if err := kp.generate(rand); err != nil { + return nil, err + } + + var clientPub, secret [32]byte + copy(clientPub[:], kexInit.ClientPubKey) + curve25519.ScalarMult(&secret, &kp.priv, &clientPub) + if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 { + return nil, errors.New("ssh: peer's curve25519 public value has wrong order") + } + + hostKeyBytes := priv.PublicKey().Marshal() + + h := crypto.SHA256.New() + magics.write(h) + writeString(h, hostKeyBytes) + writeString(h, kexInit.ClientPubKey) + writeString(h, kp.pub[:]) + + ki := new(big.Int).SetBytes(secret[:]) + K := make([]byte, intLength(ki)) + marshalInt(K, ki) + h.Write(K) + + H := h.Sum(nil) + + sig, err := signAndMarshal(priv, rand, H) + if err != nil { + return nil, err + } + + reply := kexECDHReplyMsg{ + EphemeralPubKey: kp.pub[:], + HostKey: hostKeyBytes, + Signature: sig, + } + if err := c.writePacket(Marshal(&reply)); err != nil { + return nil, err + } + return &kexResult{ + H: H, + K: K, + HostKey: hostKeyBytes, + Signature: sig, + Hash: crypto.SHA256, + }, nil +} + +// dhGEXSHA implements the diffie-hellman-group-exchange-sha1 and +// diffie-hellman-group-exchange-sha256 key agreement protocols, +// as described in RFC 4419 +type dhGEXSHA struct { + g, p *big.Int + hashFunc crypto.Hash +} + +const numMRTests = 64 + +const ( + dhGroupExchangeMinimumBits = 2048 + dhGroupExchangePreferredBits = 2048 + dhGroupExchangeMaximumBits = 8192 +) + +func (gex *dhGEXSHA) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { + if theirPublic.Sign() <= 0 || theirPublic.Cmp(gex.p) >= 0 { + return nil, fmt.Errorf("ssh: DH parameter out of bounds") + } + return new(big.Int).Exp(theirPublic, myPrivate, gex.p), nil +} + +func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { + // Send GexRequest + kexDHGexRequest := kexDHGexRequestMsg{ + MinBits: dhGroupExchangeMinimumBits, + PreferedBits: dhGroupExchangePreferredBits, + MaxBits: dhGroupExchangeMaximumBits, + } + if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil { + return nil, err + } + + // Receive GexGroup + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var kexDHGexGroup kexDHGexGroupMsg + if err = Unmarshal(packet, &kexDHGexGroup); err != nil { + return nil, err + } + + // reject if p's bit length < dhGroupExchangeMinimumBits or > dhGroupExchangeMaximumBits + if kexDHGexGroup.P.BitLen() < dhGroupExchangeMinimumBits || kexDHGexGroup.P.BitLen() > dhGroupExchangeMaximumBits { + return nil, fmt.Errorf("ssh: server-generated gex p is out of range (%d bits)", kexDHGexGroup.P.BitLen()) + } + + gex.p = kexDHGexGroup.P + gex.g = kexDHGexGroup.G + + // Check if p is safe by verifing that p and (p-1)/2 are primes + one := big.NewInt(1) + var pHalf = &big.Int{} + pHalf.Rsh(gex.p, 1) + if !gex.p.ProbablyPrime(numMRTests) || !pHalf.ProbablyPrime(numMRTests) { + return nil, fmt.Errorf("ssh: server provided gex p is not safe") + } + + // Check if g is safe by verifing that g > 1 and g < p - 1 + var pMinusOne = &big.Int{} + pMinusOne.Sub(gex.p, one) + if gex.g.Cmp(one) != 1 && gex.g.Cmp(pMinusOne) != -1 { + return nil, fmt.Errorf("ssh: server provided gex g is not safe") + } + + // Send GexInit + x, err := rand.Int(randSource, pHalf) + if err != nil { + return nil, err + } + X := new(big.Int).Exp(gex.g, x, gex.p) + kexDHGexInit := kexDHGexInitMsg{ + X: X, + } + if err := c.writePacket(Marshal(&kexDHGexInit)); err != nil { + return nil, err + } + + // Receive GexReply + packet, err = c.readPacket() + if err != nil { + return nil, err + } + + var kexDHGexReply kexDHGexReplyMsg + if err = Unmarshal(packet, &kexDHGexReply); err != nil { + return nil, err + } + + kInt, err := gex.diffieHellman(kexDHGexReply.Y, x) + if err != nil { + return nil, err + } + + // Check if k is safe by verifing that k > 1 and k < p - 1 + if kInt.Cmp(one) != 1 && kInt.Cmp(pMinusOne) != -1 { + return nil, fmt.Errorf("ssh: derived k is not safe") + } + + h := gex.hashFunc.New() + magics.write(h) + writeString(h, kexDHGexReply.HostKey) + binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits)) + binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits)) + binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits)) + writeInt(h, gex.p) + writeInt(h, gex.g) + writeInt(h, X) + writeInt(h, kexDHGexReply.Y) + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: kexDHGexReply.HostKey, + Signature: kexDHGexReply.Signature, + Hash: gex.hashFunc, + }, nil +} + +// Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256. +// +// This is a minimal implementation to satisfy the automated tests. +func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + // Receive GexRequest + packet, err := c.readPacket() + if err != nil { + return + } + var kexDHGexRequest kexDHGexRequestMsg + if err = Unmarshal(packet, &kexDHGexRequest); err != nil { + return + } + + // smoosh the user's preferred size into our own limits + if kexDHGexRequest.PreferedBits > dhGroupExchangeMaximumBits { + kexDHGexRequest.PreferedBits = dhGroupExchangeMaximumBits + } + if kexDHGexRequest.PreferedBits < dhGroupExchangeMinimumBits { + kexDHGexRequest.PreferedBits = dhGroupExchangeMinimumBits + } + // fix min/max if they're inconsistent. technically, we could just pout + // and hang up, but there's no harm in giving them the benefit of the + // doubt and just picking a bitsize for them. + if kexDHGexRequest.MinBits > kexDHGexRequest.PreferedBits { + kexDHGexRequest.MinBits = kexDHGexRequest.PreferedBits + } + if kexDHGexRequest.MaxBits < kexDHGexRequest.PreferedBits { + kexDHGexRequest.MaxBits = kexDHGexRequest.PreferedBits + } + + // Send GexGroup + // This is the group called diffie-hellman-group14-sha1 in RFC + // 4253 and Oakley Group 14 in RFC 3526. + p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) + gex.p = p + gex.g = big.NewInt(2) + + kexDHGexGroup := kexDHGexGroupMsg{ + P: gex.p, + G: gex.g, + } + if err := c.writePacket(Marshal(&kexDHGexGroup)); err != nil { + return nil, err + } + + // Receive GexInit + packet, err = c.readPacket() + if err != nil { + return + } + var kexDHGexInit kexDHGexInitMsg + if err = Unmarshal(packet, &kexDHGexInit); err != nil { + return + } + + var pHalf = &big.Int{} + pHalf.Rsh(gex.p, 1) + + y, err := rand.Int(randSource, pHalf) + if err != nil { + return + } + + Y := new(big.Int).Exp(gex.g, y, gex.p) + kInt, err := gex.diffieHellman(kexDHGexInit.X, y) + if err != nil { + return nil, err + } + + hostKeyBytes := priv.PublicKey().Marshal() + + h := gex.hashFunc.New() + magics.write(h) + writeString(h, hostKeyBytes) + binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits)) + binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits)) + binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits)) + writeInt(h, gex.p) + writeInt(h, gex.g) + writeInt(h, kexDHGexInit.X) + writeInt(h, Y) + + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + H := h.Sum(nil) + + // H is already a hash, but the hostkey signing will apply its + // own key-specific hash algorithm. + sig, err := signAndMarshal(priv, randSource, H) + if err != nil { + return nil, err + } + + kexDHGexReply := kexDHGexReplyMsg{ + HostKey: hostKeyBytes, + Y: Y, + Signature: sig, + } + packet = Marshal(&kexDHGexReply) + + err = c.writePacket(packet) + + return &kexResult{ + H: H, + K: K, + HostKey: hostKeyBytes, + Signature: sig, + Hash: gex.hashFunc, + }, err +} diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go new file mode 100644 index 00000000000..5377ec8c3b5 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -0,0 +1,1403 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/md5" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/base64" + "encoding/hex" + "encoding/pem" + "errors" + "fmt" + "io" + "math/big" + "strings" + + "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh/internal/bcrypt_pbkdf" +) + +// These constants represent the algorithm names for key types supported by this +// package. +const ( + KeyAlgoRSA = "ssh-rsa" + KeyAlgoDSA = "ssh-dss" + KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" + KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com" + KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" + KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" + KeyAlgoED25519 = "ssh-ed25519" + KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com" +) + +// These constants represent non-default signature algorithms that are supported +// as algorithm parameters to AlgorithmSigner.SignWithAlgorithm methods. See +// [PROTOCOL.agent] section 4.5.1 and +// https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-10 +const ( + SigAlgoRSA = "ssh-rsa" + SigAlgoRSASHA2256 = "rsa-sha2-256" + SigAlgoRSASHA2512 = "rsa-sha2-512" +) + +// parsePubKey parses a public key of the given algorithm. +// Use ParsePublicKey for keys with prepended algorithm. +func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { + switch algo { + case KeyAlgoRSA: + return parseRSA(in) + case KeyAlgoDSA: + return parseDSA(in) + case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: + return parseECDSA(in) + case KeyAlgoSKECDSA256: + return parseSKECDSA(in) + case KeyAlgoED25519: + return parseED25519(in) + case KeyAlgoSKED25519: + return parseSKEd25519(in) + case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01: + cert, err := parseCert(in, certToPrivAlgo(algo)) + if err != nil { + return nil, nil, err + } + return cert, nil, nil + } + return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo) +} + +// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format +// (see sshd(8) manual page) once the options and key type fields have been +// removed. +func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { + in = bytes.TrimSpace(in) + + i := bytes.IndexAny(in, " \t") + if i == -1 { + i = len(in) + } + base64Key := in[:i] + + key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) + n, err := base64.StdEncoding.Decode(key, base64Key) + if err != nil { + return nil, "", err + } + key = key[:n] + out, err = ParsePublicKey(key) + if err != nil { + return nil, "", err + } + comment = string(bytes.TrimSpace(in[i:])) + return out, comment, nil +} + +// ParseKnownHosts parses an entry in the format of the known_hosts file. +// +// The known_hosts format is documented in the sshd(8) manual page. This +// function will parse a single entry from in. On successful return, marker +// will contain the optional marker value (i.e. "cert-authority" or "revoked") +// or else be empty, hosts will contain the hosts that this entry matches, +// pubKey will contain the public key and comment will contain any trailing +// comment at the end of the line. See the sshd(8) manual page for the various +// forms that a host string can take. +// +// The unparsed remainder of the input will be returned in rest. This function +// can be called repeatedly to parse multiple entries. +// +// If no entries were found in the input then err will be io.EOF. Otherwise a +// non-nil err value indicates a parse error. +func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) { + for len(in) > 0 { + end := bytes.IndexByte(in, '\n') + if end != -1 { + rest = in[end+1:] + in = in[:end] + } else { + rest = nil + } + + end = bytes.IndexByte(in, '\r') + if end != -1 { + in = in[:end] + } + + in = bytes.TrimSpace(in) + if len(in) == 0 || in[0] == '#' { + in = rest + continue + } + + i := bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + // Strip out the beginning of the known_host key. + // This is either an optional marker or a (set of) hostname(s). + keyFields := bytes.Fields(in) + if len(keyFields) < 3 || len(keyFields) > 5 { + return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data") + } + + // keyFields[0] is either "@cert-authority", "@revoked" or a comma separated + // list of hosts + marker := "" + if keyFields[0][0] == '@' { + marker = string(keyFields[0][1:]) + keyFields = keyFields[1:] + } + + hosts := string(keyFields[0]) + // keyFields[1] contains the key type (e.g. “ssh-rsa”). + // However, that information is duplicated inside the + // base64-encoded key and so is ignored here. + + key := bytes.Join(keyFields[2:], []byte(" ")) + if pubKey, comment, err = parseAuthorizedKey(key); err != nil { + return "", nil, nil, "", nil, err + } + + return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil + } + + return "", nil, nil, "", nil, io.EOF +} + +// ParseAuthorizedKeys parses a public key from an authorized_keys +// file used in OpenSSH according to the sshd(8) manual page. +func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { + for len(in) > 0 { + end := bytes.IndexByte(in, '\n') + if end != -1 { + rest = in[end+1:] + in = in[:end] + } else { + rest = nil + } + + end = bytes.IndexByte(in, '\r') + if end != -1 { + in = in[:end] + } + + in = bytes.TrimSpace(in) + if len(in) == 0 || in[0] == '#' { + in = rest + continue + } + + i := bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { + return out, comment, options, rest, nil + } + + // No key type recognised. Maybe there's an options field at + // the beginning. + var b byte + inQuote := false + var candidateOptions []string + optionStart := 0 + for i, b = range in { + isEnd := !inQuote && (b == ' ' || b == '\t') + if (b == ',' && !inQuote) || isEnd { + if i-optionStart > 0 { + candidateOptions = append(candidateOptions, string(in[optionStart:i])) + } + optionStart = i + 1 + } + if isEnd { + break + } + if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { + inQuote = !inQuote + } + } + for i < len(in) && (in[i] == ' ' || in[i] == '\t') { + i++ + } + if i == len(in) { + // Invalid line: unmatched quote + in = rest + continue + } + + in = in[i:] + i = bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { + options = candidateOptions + return out, comment, options, rest, nil + } + + in = rest + continue + } + + return nil, "", nil, nil, errors.New("ssh: no key found") +} + +// ParsePublicKey parses an SSH public key formatted for use in +// the SSH wire protocol according to RFC 4253, section 6.6. +func ParsePublicKey(in []byte) (out PublicKey, err error) { + algo, in, ok := parseString(in) + if !ok { + return nil, errShortRead + } + var rest []byte + out, rest, err = parsePubKey(in, string(algo)) + if len(rest) > 0 { + return nil, errors.New("ssh: trailing junk in public key") + } + + return out, err +} + +// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH +// authorized_keys file. The return value ends with newline. +func MarshalAuthorizedKey(key PublicKey) []byte { + b := &bytes.Buffer{} + b.WriteString(key.Type()) + b.WriteByte(' ') + e := base64.NewEncoder(base64.StdEncoding, b) + e.Write(key.Marshal()) + e.Close() + b.WriteByte('\n') + return b.Bytes() +} + +// PublicKey is an abstraction of different types of public keys. +type PublicKey interface { + // Type returns the key's type, e.g. "ssh-rsa". + Type() string + + // Marshal returns the serialized key data in SSH wire format, + // with the name prefix. To unmarshal the returned data, use + // the ParsePublicKey function. + Marshal() []byte + + // Verify that sig is a signature on the given data using this + // key. This function will hash the data appropriately first. + Verify(data []byte, sig *Signature) error +} + +// CryptoPublicKey, if implemented by a PublicKey, +// returns the underlying crypto.PublicKey form of the key. +type CryptoPublicKey interface { + CryptoPublicKey() crypto.PublicKey +} + +// A Signer can create signatures that verify against a public key. +type Signer interface { + // PublicKey returns an associated PublicKey instance. + PublicKey() PublicKey + + // Sign returns raw signature for the given data. This method + // will apply the hash specified for the keytype to the data. + Sign(rand io.Reader, data []byte) (*Signature, error) +} + +// A AlgorithmSigner is a Signer that also supports specifying a specific +// algorithm to use for signing. +type AlgorithmSigner interface { + Signer + + // SignWithAlgorithm is like Signer.Sign, but allows specification of a + // non-default signing algorithm. See the SigAlgo* constants in this + // package for signature algorithms supported by this package. Callers may + // pass an empty string for the algorithm in which case the AlgorithmSigner + // will use its default algorithm. + SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) +} + +type rsaPublicKey rsa.PublicKey + +func (r *rsaPublicKey) Type() string { + return "ssh-rsa" +} + +// parseRSA parses an RSA key according to RFC 4253, section 6.6. +func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + E *big.Int + N *big.Int + Rest []byte `ssh:"rest"` + } + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + if w.E.BitLen() > 24 { + return nil, nil, errors.New("ssh: exponent too large") + } + e := w.E.Int64() + if e < 3 || e&1 == 0 { + return nil, nil, errors.New("ssh: incorrect exponent") + } + + var key rsa.PublicKey + key.E = int(e) + key.N = w.N + return (*rsaPublicKey)(&key), w.Rest, nil +} + +func (r *rsaPublicKey) Marshal() []byte { + e := new(big.Int).SetInt64(int64(r.E)) + // RSA publickey struct layout should match the struct used by + // parseRSACert in the x/crypto/ssh/agent package. + wirekey := struct { + Name string + E *big.Int + N *big.Int + }{ + KeyAlgoRSA, + e, + r.N, + } + return Marshal(&wirekey) +} + +func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { + var hash crypto.Hash + switch sig.Format { + case SigAlgoRSA: + hash = crypto.SHA1 + case SigAlgoRSASHA2256: + hash = crypto.SHA256 + case SigAlgoRSASHA2512: + hash = crypto.SHA512 + default: + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) + } + h := hash.New() + h.Write(data) + digest := h.Sum(nil) + return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, sig.Blob) +} + +func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { + return (*rsa.PublicKey)(r) +} + +type dsaPublicKey dsa.PublicKey + +func (k *dsaPublicKey) Type() string { + return "ssh-dss" +} + +func checkDSAParams(param *dsa.Parameters) error { + // SSH specifies FIPS 186-2, which only provided a single size + // (1024 bits) DSA key. FIPS 186-3 allows for larger key + // sizes, which would confuse SSH. + if l := param.P.BitLen(); l != 1024 { + return fmt.Errorf("ssh: unsupported DSA key size %d", l) + } + + return nil +} + +// parseDSA parses an DSA key according to RFC 4253, section 6.6. +func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + P, Q, G, Y *big.Int + Rest []byte `ssh:"rest"` + } + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + param := dsa.Parameters{ + P: w.P, + Q: w.Q, + G: w.G, + } + if err := checkDSAParams(¶m); err != nil { + return nil, nil, err + } + + key := &dsaPublicKey{ + Parameters: param, + Y: w.Y, + } + return key, w.Rest, nil +} + +func (k *dsaPublicKey) Marshal() []byte { + // DSA publickey struct layout should match the struct used by + // parseDSACert in the x/crypto/ssh/agent package. + w := struct { + Name string + P, Q, G, Y *big.Int + }{ + k.Type(), + k.P, + k.Q, + k.G, + k.Y, + } + + return Marshal(&w) +} + +func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + + // Per RFC 4253, section 6.6, + // The value for 'dss_signature_blob' is encoded as a string containing + // r, followed by s (which are 160-bit integers, without lengths or + // padding, unsigned, and in network byte order). + // For DSS purposes, sig.Blob should be exactly 40 bytes in length. + if len(sig.Blob) != 40 { + return errors.New("ssh: DSA signature parse error") + } + r := new(big.Int).SetBytes(sig.Blob[:20]) + s := new(big.Int).SetBytes(sig.Blob[20:]) + if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) { + return nil + } + return errors.New("ssh: signature did not verify") +} + +func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey { + return (*dsa.PublicKey)(k) +} + +type dsaPrivateKey struct { + *dsa.PrivateKey +} + +func (k *dsaPrivateKey) PublicKey() PublicKey { + return (*dsaPublicKey)(&k.PrivateKey.PublicKey) +} + +func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { + return k.SignWithAlgorithm(rand, data, "") +} + +func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { + if algorithm != "" && algorithm != k.PublicKey().Type() { + return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) + } + + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + r, s, err := dsa.Sign(rand, k.PrivateKey, digest) + if err != nil { + return nil, err + } + + sig := make([]byte, 40) + rb := r.Bytes() + sb := s.Bytes() + + copy(sig[20-len(rb):20], rb) + copy(sig[40-len(sb):], sb) + + return &Signature{ + Format: k.PublicKey().Type(), + Blob: sig, + }, nil +} + +type ecdsaPublicKey ecdsa.PublicKey + +func (k *ecdsaPublicKey) Type() string { + return "ecdsa-sha2-" + k.nistID() +} + +func (k *ecdsaPublicKey) nistID() string { + switch k.Params().BitSize { + case 256: + return "nistp256" + case 384: + return "nistp384" + case 521: + return "nistp521" + } + panic("ssh: unsupported ecdsa key size") +} + +type ed25519PublicKey ed25519.PublicKey + +func (k ed25519PublicKey) Type() string { + return KeyAlgoED25519 +} + +func parseED25519(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + KeyBytes []byte + Rest []byte `ssh:"rest"` + } + + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := ed25519.PublicKey(w.KeyBytes) + + return (ed25519PublicKey)(key), w.Rest, nil +} + +func (k ed25519PublicKey) Marshal() []byte { + w := struct { + Name string + KeyBytes []byte + }{ + KeyAlgoED25519, + []byte(k), + } + return Marshal(&w) +} + +func (k ed25519PublicKey) Verify(b []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } + + edKey := (ed25519.PublicKey)(k) + if ok := ed25519.Verify(edKey, b, sig.Blob); !ok { + return errors.New("ssh: signature did not verify") + } + + return nil +} + +func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey { + return ed25519.PublicKey(k) +} + +func supportedEllipticCurve(curve elliptic.Curve) bool { + return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() +} + +// ecHash returns the hash to match the given elliptic curve, see RFC +// 5656, section 6.2.1 +func ecHash(curve elliptic.Curve) crypto.Hash { + bitSize := curve.Params().BitSize + switch { + case bitSize <= 256: + return crypto.SHA256 + case bitSize <= 384: + return crypto.SHA384 + } + return crypto.SHA512 +} + +// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. +func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + Curve string + KeyBytes []byte + Rest []byte `ssh:"rest"` + } + + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := new(ecdsa.PublicKey) + + switch w.Curve { + case "nistp256": + key.Curve = elliptic.P256() + case "nistp384": + key.Curve = elliptic.P384() + case "nistp521": + key.Curve = elliptic.P521() + default: + return nil, nil, errors.New("ssh: unsupported curve") + } + + key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) + if key.X == nil || key.Y == nil { + return nil, nil, errors.New("ssh: invalid curve point") + } + return (*ecdsaPublicKey)(key), w.Rest, nil +} + +func (k *ecdsaPublicKey) Marshal() []byte { + // See RFC 5656, section 3.1. + keyBytes := elliptic.Marshal(k.Curve, k.X, k.Y) + // ECDSA publickey struct layout should match the struct used by + // parseECDSACert in the x/crypto/ssh/agent package. + w := struct { + Name string + ID string + Key []byte + }{ + k.Type(), + k.nistID(), + keyBytes, + } + + return Marshal(&w) +} + +func (k *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } + + h := ecHash(k.Curve).New() + h.Write(data) + digest := h.Sum(nil) + + // Per RFC 5656, section 3.1.2, + // The ecdsa_signature_blob value has the following specific encoding: + // mpint r + // mpint s + var ecSig struct { + R *big.Int + S *big.Int + } + + if err := Unmarshal(sig.Blob, &ecSig); err != nil { + return err + } + + if ecdsa.Verify((*ecdsa.PublicKey)(k), digest, ecSig.R, ecSig.S) { + return nil + } + return errors.New("ssh: signature did not verify") +} + +func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey { + return (*ecdsa.PublicKey)(k) +} + +// skFields holds the additional fields present in U2F/FIDO2 signatures. +// See openssh/PROTOCOL.u2f 'SSH U2F Signatures' for details. +type skFields struct { + // Flags contains U2F/FIDO2 flags such as 'user present' + Flags byte + // Counter is a monotonic signature counter which can be + // used to detect concurrent use of a private key, should + // it be extracted from hardware. + Counter uint32 +} + +type skECDSAPublicKey struct { + // application is a URL-like string, typically "ssh:" for SSH. + // see openssh/PROTOCOL.u2f for details. + application string + ecdsa.PublicKey +} + +func (k *skECDSAPublicKey) Type() string { + return KeyAlgoSKECDSA256 +} + +func (k *skECDSAPublicKey) nistID() string { + return "nistp256" +} + +func parseSKECDSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + Curve string + KeyBytes []byte + Application string + Rest []byte `ssh:"rest"` + } + + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := new(skECDSAPublicKey) + key.application = w.Application + + if w.Curve != "nistp256" { + return nil, nil, errors.New("ssh: unsupported curve") + } + key.Curve = elliptic.P256() + + key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) + if key.X == nil || key.Y == nil { + return nil, nil, errors.New("ssh: invalid curve point") + } + + return key, w.Rest, nil +} + +func (k *skECDSAPublicKey) Marshal() []byte { + // See RFC 5656, section 3.1. + keyBytes := elliptic.Marshal(k.Curve, k.X, k.Y) + w := struct { + Name string + ID string + Key []byte + Application string + }{ + k.Type(), + k.nistID(), + keyBytes, + k.application, + } + + return Marshal(&w) +} + +func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } + + h := ecHash(k.Curve).New() + h.Write([]byte(k.application)) + appDigest := h.Sum(nil) + + h.Reset() + h.Write(data) + dataDigest := h.Sum(nil) + + var ecSig struct { + R *big.Int + S *big.Int + } + if err := Unmarshal(sig.Blob, &ecSig); err != nil { + return err + } + + var skf skFields + if err := Unmarshal(sig.Rest, &skf); err != nil { + return err + } + + blob := struct { + ApplicationDigest []byte `ssh:"rest"` + Flags byte + Counter uint32 + MessageDigest []byte `ssh:"rest"` + }{ + appDigest, + skf.Flags, + skf.Counter, + dataDigest, + } + + original := Marshal(blob) + + h.Reset() + h.Write(original) + digest := h.Sum(nil) + + if ecdsa.Verify((*ecdsa.PublicKey)(&k.PublicKey), digest, ecSig.R, ecSig.S) { + return nil + } + return errors.New("ssh: signature did not verify") +} + +type skEd25519PublicKey struct { + // application is a URL-like string, typically "ssh:" for SSH. + // see openssh/PROTOCOL.u2f for details. + application string + ed25519.PublicKey +} + +func (k *skEd25519PublicKey) Type() string { + return KeyAlgoSKED25519 +} + +func parseSKEd25519(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + KeyBytes []byte + Application string + Rest []byte `ssh:"rest"` + } + + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := new(skEd25519PublicKey) + key.application = w.Application + key.PublicKey = ed25519.PublicKey(w.KeyBytes) + + return key, w.Rest, nil +} + +func (k *skEd25519PublicKey) Marshal() []byte { + w := struct { + Name string + KeyBytes []byte + Application string + }{ + KeyAlgoSKED25519, + []byte(k.PublicKey), + k.application, + } + return Marshal(&w) +} + +func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } + + h := sha256.New() + h.Write([]byte(k.application)) + appDigest := h.Sum(nil) + + h.Reset() + h.Write(data) + dataDigest := h.Sum(nil) + + var edSig struct { + Signature []byte `ssh:"rest"` + } + + if err := Unmarshal(sig.Blob, &edSig); err != nil { + return err + } + + var skf skFields + if err := Unmarshal(sig.Rest, &skf); err != nil { + return err + } + + blob := struct { + ApplicationDigest []byte `ssh:"rest"` + Flags byte + Counter uint32 + MessageDigest []byte `ssh:"rest"` + }{ + appDigest, + skf.Flags, + skf.Counter, + dataDigest, + } + + original := Marshal(blob) + + edKey := (ed25519.PublicKey)(k.PublicKey) + if ok := ed25519.Verify(edKey, original, edSig.Signature); !ok { + return errors.New("ssh: signature did not verify") + } + + return nil +} + +// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, +// *ecdsa.PrivateKey or any other crypto.Signer and returns a +// corresponding Signer instance. ECDSA keys must use P-256, P-384 or +// P-521. DSA keys must use parameter size L1024N160. +func NewSignerFromKey(key interface{}) (Signer, error) { + switch key := key.(type) { + case crypto.Signer: + return NewSignerFromSigner(key) + case *dsa.PrivateKey: + return newDSAPrivateKey(key) + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", key) + } +} + +func newDSAPrivateKey(key *dsa.PrivateKey) (Signer, error) { + if err := checkDSAParams(&key.PublicKey.Parameters); err != nil { + return nil, err + } + + return &dsaPrivateKey{key}, nil +} + +type wrappedSigner struct { + signer crypto.Signer + pubKey PublicKey +} + +// NewSignerFromSigner takes any crypto.Signer implementation and +// returns a corresponding Signer interface. This can be used, for +// example, with keys kept in hardware modules. +func NewSignerFromSigner(signer crypto.Signer) (Signer, error) { + pubKey, err := NewPublicKey(signer.Public()) + if err != nil { + return nil, err + } + + return &wrappedSigner{signer, pubKey}, nil +} + +func (s *wrappedSigner) PublicKey() PublicKey { + return s.pubKey +} + +func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { + return s.SignWithAlgorithm(rand, data, "") +} + +func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { + var hashFunc crypto.Hash + + if _, ok := s.pubKey.(*rsaPublicKey); ok { + // RSA keys support a few hash functions determined by the requested signature algorithm + switch algorithm { + case "", SigAlgoRSA: + algorithm = SigAlgoRSA + hashFunc = crypto.SHA1 + case SigAlgoRSASHA2256: + hashFunc = crypto.SHA256 + case SigAlgoRSASHA2512: + hashFunc = crypto.SHA512 + default: + return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) + } + } else { + // The only supported algorithm for all other key types is the same as the type of the key + if algorithm == "" { + algorithm = s.pubKey.Type() + } else if algorithm != s.pubKey.Type() { + return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) + } + + switch key := s.pubKey.(type) { + case *dsaPublicKey: + hashFunc = crypto.SHA1 + case *ecdsaPublicKey: + hashFunc = ecHash(key.Curve) + case ed25519PublicKey: + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", key) + } + } + + var digest []byte + if hashFunc != 0 { + h := hashFunc.New() + h.Write(data) + digest = h.Sum(nil) + } else { + digest = data + } + + signature, err := s.signer.Sign(rand, digest, hashFunc) + if err != nil { + return nil, err + } + + // crypto.Signer.Sign is expected to return an ASN.1-encoded signature + // for ECDSA and DSA, but that's not the encoding expected by SSH, so + // re-encode. + switch s.pubKey.(type) { + case *ecdsaPublicKey, *dsaPublicKey: + type asn1Signature struct { + R, S *big.Int + } + asn1Sig := new(asn1Signature) + _, err := asn1.Unmarshal(signature, asn1Sig) + if err != nil { + return nil, err + } + + switch s.pubKey.(type) { + case *ecdsaPublicKey: + signature = Marshal(asn1Sig) + + case *dsaPublicKey: + signature = make([]byte, 40) + r := asn1Sig.R.Bytes() + s := asn1Sig.S.Bytes() + copy(signature[20-len(r):20], r) + copy(signature[40-len(s):40], s) + } + } + + return &Signature{ + Format: algorithm, + Blob: signature, + }, nil +} + +// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, +// or ed25519.PublicKey returns a corresponding PublicKey instance. +// ECDSA keys must use P-256, P-384 or P-521. +func NewPublicKey(key interface{}) (PublicKey, error) { + switch key := key.(type) { + case *rsa.PublicKey: + return (*rsaPublicKey)(key), nil + case *ecdsa.PublicKey: + if !supportedEllipticCurve(key.Curve) { + return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported") + } + return (*ecdsaPublicKey)(key), nil + case *dsa.PublicKey: + return (*dsaPublicKey)(key), nil + case ed25519.PublicKey: + return (ed25519PublicKey)(key), nil + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", key) + } +} + +// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports +// the same keys as ParseRawPrivateKey. If the private key is encrypted, it +// will return a PassphraseMissingError. +func ParsePrivateKey(pemBytes []byte) (Signer, error) { + key, err := ParseRawPrivateKey(pemBytes) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + +// ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private +// key and passphrase. It supports the same keys as +// ParseRawPrivateKeyWithPassphrase. +func ParsePrivateKeyWithPassphrase(pemBytes, passphrase []byte) (Signer, error) { + key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + +// encryptedBlock tells whether a private key is +// encrypted by examining its Proc-Type header +// for a mention of ENCRYPTED +// according to RFC 1421 Section 4.6.1.1. +func encryptedBlock(block *pem.Block) bool { + return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED") +} + +// A PassphraseMissingError indicates that parsing this private key requires a +// passphrase. Use ParsePrivateKeyWithPassphrase. +type PassphraseMissingError struct { + // PublicKey will be set if the private key format includes an unencrypted + // public key along with the encrypted private key. + PublicKey PublicKey +} + +func (*PassphraseMissingError) Error() string { + return "ssh: this private key is passphrase protected" +} + +// ParseRawPrivateKey returns a private key from a PEM encoded private key. It +// supports RSA (PKCS#1), PKCS#8, DSA (OpenSSL), and ECDSA private keys. If the +// private key is encrypted, it will return a PassphraseMissingError. +func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("ssh: no key found") + } + + if encryptedBlock(block) { + return nil, &PassphraseMissingError{} + } + + switch block.Type { + case "RSA PRIVATE KEY": + return x509.ParsePKCS1PrivateKey(block.Bytes) + // RFC5208 - https://tools.ietf.org/html/rfc5208 + case "PRIVATE KEY": + return x509.ParsePKCS8PrivateKey(block.Bytes) + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(block.Bytes) + case "DSA PRIVATE KEY": + return ParseDSAPrivateKey(block.Bytes) + case "OPENSSH PRIVATE KEY": + return parseOpenSSHPrivateKey(block.Bytes, unencryptedOpenSSHKey) + default: + return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) + } +} + +// ParseRawPrivateKeyWithPassphrase returns a private key decrypted with +// passphrase from a PEM encoded private key. If the passphrase is wrong, it +// will return x509.IncorrectPasswordError. +func ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase []byte) (interface{}, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("ssh: no key found") + } + + if block.Type == "OPENSSH PRIVATE KEY" { + return parseOpenSSHPrivateKey(block.Bytes, passphraseProtectedOpenSSHKey(passphrase)) + } + + if !encryptedBlock(block) || !x509.IsEncryptedPEMBlock(block) { + return nil, errors.New("ssh: not an encrypted key") + } + + buf, err := x509.DecryptPEMBlock(block, passphrase) + if err != nil { + if err == x509.IncorrectPasswordError { + return nil, err + } + return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err) + } + + switch block.Type { + case "RSA PRIVATE KEY": + return x509.ParsePKCS1PrivateKey(buf) + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(buf) + case "DSA PRIVATE KEY": + return ParseDSAPrivateKey(buf) + default: + return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) + } +} + +// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as +// specified by the OpenSSL DSA man page. +func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { + var k struct { + Version int + P *big.Int + Q *big.Int + G *big.Int + Pub *big.Int + Priv *big.Int + } + rest, err := asn1.Unmarshal(der, &k) + if err != nil { + return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) + } + if len(rest) > 0 { + return nil, errors.New("ssh: garbage after DSA key") + } + + return &dsa.PrivateKey{ + PublicKey: dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: k.P, + Q: k.Q, + G: k.G, + }, + Y: k.Pub, + }, + X: k.Priv, + }, nil +} + +func unencryptedOpenSSHKey(cipherName, kdfName, kdfOpts string, privKeyBlock []byte) ([]byte, error) { + if kdfName != "none" || cipherName != "none" { + return nil, &PassphraseMissingError{} + } + if kdfOpts != "" { + return nil, errors.New("ssh: invalid openssh private key") + } + return privKeyBlock, nil +} + +func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc { + return func(cipherName, kdfName, kdfOpts string, privKeyBlock []byte) ([]byte, error) { + if kdfName == "none" || cipherName == "none" { + return nil, errors.New("ssh: key is not password protected") + } + if kdfName != "bcrypt" { + return nil, fmt.Errorf("ssh: unknown KDF %q, only supports %q", kdfName, "bcrypt") + } + + var opts struct { + Salt string + Rounds uint32 + } + if err := Unmarshal([]byte(kdfOpts), &opts); err != nil { + return nil, err + } + + k, err := bcrypt_pbkdf.Key(passphrase, []byte(opts.Salt), int(opts.Rounds), 32+16) + if err != nil { + return nil, err + } + key, iv := k[:32], k[32:] + + if cipherName != "aes256-ctr" { + return nil, fmt.Errorf("ssh: unknown cipher %q, only supports %q", cipherName, "aes256-ctr") + } + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + ctr := cipher.NewCTR(c, iv) + ctr.XORKeyStream(privKeyBlock, privKeyBlock) + + return privKeyBlock, nil + } +} + +type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error) + +// parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt +// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used +// as the decrypt function to parse an unencrypted private key. See +// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key. +func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) { + const magic = "openssh-key-v1\x00" + if len(key) < len(magic) || string(key[:len(magic)]) != magic { + return nil, errors.New("ssh: invalid openssh private key format") + } + remaining := key[len(magic):] + + var w struct { + CipherName string + KdfName string + KdfOpts string + NumKeys uint32 + PubKey []byte + PrivKeyBlock []byte + } + + if err := Unmarshal(remaining, &w); err != nil { + return nil, err + } + if w.NumKeys != 1 { + // We only support single key files, and so does OpenSSH. + // https://github.com/openssh/openssh-portable/blob/4103a3ec7/sshkey.c#L4171 + return nil, errors.New("ssh: multi-key files are not supported") + } + + privKeyBlock, err := decrypt(w.CipherName, w.KdfName, w.KdfOpts, w.PrivKeyBlock) + if err != nil { + if err, ok := err.(*PassphraseMissingError); ok { + pub, errPub := ParsePublicKey(w.PubKey) + if errPub != nil { + return nil, fmt.Errorf("ssh: failed to parse embedded public key: %v", errPub) + } + err.PublicKey = pub + } + return nil, err + } + + pk1 := struct { + Check1 uint32 + Check2 uint32 + Keytype string + Rest []byte `ssh:"rest"` + }{} + + if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 { + if w.CipherName != "none" { + return nil, x509.IncorrectPasswordError + } + return nil, errors.New("ssh: malformed OpenSSH key") + } + + // we only handle ed25519 and rsa keys currently + switch pk1.Keytype { + case KeyAlgoRSA: + // https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773 + key := struct { + N *big.Int + E *big.Int + D *big.Int + Iqmp *big.Int + P *big.Int + Q *big.Int + Comment string + Pad []byte `ssh:"rest"` + }{} + + if err := Unmarshal(pk1.Rest, &key); err != nil { + return nil, err + } + + if err := checkOpenSSHKeyPadding(key.Pad); err != nil { + return nil, err + } + + pk := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + N: key.N, + E: int(key.E.Int64()), + }, + D: key.D, + Primes: []*big.Int{key.P, key.Q}, + } + + if err := pk.Validate(); err != nil { + return nil, err + } + + pk.Precompute() + + return pk, nil + case KeyAlgoED25519: + key := struct { + Pub []byte + Priv []byte + Comment string + Pad []byte `ssh:"rest"` + }{} + + if err := Unmarshal(pk1.Rest, &key); err != nil { + return nil, err + } + + if len(key.Priv) != ed25519.PrivateKeySize { + return nil, errors.New("ssh: private key unexpected length") + } + + if err := checkOpenSSHKeyPadding(key.Pad); err != nil { + return nil, err + } + + pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) + copy(pk, key.Priv) + return &pk, nil + default: + return nil, errors.New("ssh: unhandled key type") + } +} + +func checkOpenSSHKeyPadding(pad []byte) error { + for i, b := range pad { + if int(b) != i+1 { + return errors.New("ssh: padding not as expected") + } + } + return nil +} + +// FingerprintLegacyMD5 returns the user presentation of the key's +// fingerprint as described by RFC 4716 section 4. +func FingerprintLegacyMD5(pubKey PublicKey) string { + md5sum := md5.Sum(pubKey.Marshal()) + hexarray := make([]string, len(md5sum)) + for i, c := range md5sum { + hexarray[i] = hex.EncodeToString([]byte{c}) + } + return strings.Join(hexarray, ":") +} + +// FingerprintSHA256 returns the user presentation of the key's +// fingerprint as unpadded base64 encoded sha256 hash. +// This format was introduced from OpenSSH 6.8. +// https://www.openssh.com/txt/release-6.8 +// https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding) +func FingerprintSHA256(pubKey PublicKey) string { + sha256sum := sha256.Sum256(pubKey.Marshal()) + hash := base64.RawStdEncoding.EncodeToString(sha256sum[:]) + return "SHA256:" + hash +} diff --git a/vendor/golang.org/x/crypto/ssh/mac.go b/vendor/golang.org/x/crypto/ssh/mac.go new file mode 100644 index 00000000000..c07a06285e6 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mac.go @@ -0,0 +1,61 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Message authentication support + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "hash" +) + +type macMode struct { + keySize int + etm bool + new func(key []byte) hash.Hash +} + +// truncatingMAC wraps around a hash.Hash and truncates the output digest to +// a given size. +type truncatingMAC struct { + length int + hmac hash.Hash +} + +func (t truncatingMAC) Write(data []byte) (int, error) { + return t.hmac.Write(data) +} + +func (t truncatingMAC) Sum(in []byte) []byte { + out := t.hmac.Sum(in) + return out[:len(in)+t.length] +} + +func (t truncatingMAC) Reset() { + t.hmac.Reset() +} + +func (t truncatingMAC) Size() int { + return t.length +} + +func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } + +var macModes = map[string]*macMode{ + "hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash { + return hmac.New(sha256.New, key) + }}, + "hmac-sha2-256": {32, false, func(key []byte) hash.Hash { + return hmac.New(sha256.New, key) + }}, + "hmac-sha1": {20, false, func(key []byte) hash.Hash { + return hmac.New(sha1.New, key) + }}, + "hmac-sha1-96": {20, false, func(key []byte) hash.Hash { + return truncatingMAC{12, hmac.New(sha1.New, key)} + }}, +} diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go new file mode 100644 index 00000000000..ac41a4168bf --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -0,0 +1,866 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + "reflect" + "strconv" + "strings" +) + +// These are SSH message type numbers. They are scattered around several +// documents but many were taken from [SSH-PARAMETERS]. +const ( + msgIgnore = 2 + msgUnimplemented = 3 + msgDebug = 4 + msgNewKeys = 21 +) + +// SSH messages: +// +// These structures mirror the wire format of the corresponding SSH messages. +// They are marshaled using reflection with the marshal and unmarshal functions +// in this file. The only wrinkle is that a final member of type []byte with a +// ssh tag of "rest" receives the remainder of a packet when unmarshaling. + +// See RFC 4253, section 11.1. +const msgDisconnect = 1 + +// disconnectMsg is the message that signals a disconnect. It is also +// the error type returned from mux.Wait() +type disconnectMsg struct { + Reason uint32 `sshtype:"1"` + Message string + Language string +} + +func (d *disconnectMsg) Error() string { + return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message) +} + +// See RFC 4253, section 7.1. +const msgKexInit = 20 + +type kexInitMsg struct { + Cookie [16]byte `sshtype:"20"` + KexAlgos []string + ServerHostKeyAlgos []string + CiphersClientServer []string + CiphersServerClient []string + MACsClientServer []string + MACsServerClient []string + CompressionClientServer []string + CompressionServerClient []string + LanguagesClientServer []string + LanguagesServerClient []string + FirstKexFollows bool + Reserved uint32 +} + +// See RFC 4253, section 8. + +// Diffie-Helman +const msgKexDHInit = 30 + +type kexDHInitMsg struct { + X *big.Int `sshtype:"30"` +} + +const msgKexECDHInit = 30 + +type kexECDHInitMsg struct { + ClientPubKey []byte `sshtype:"30"` +} + +const msgKexECDHReply = 31 + +type kexECDHReplyMsg struct { + HostKey []byte `sshtype:"31"` + EphemeralPubKey []byte + Signature []byte +} + +const msgKexDHReply = 31 + +type kexDHReplyMsg struct { + HostKey []byte `sshtype:"31"` + Y *big.Int + Signature []byte +} + +// See RFC 4419, section 5. +const msgKexDHGexGroup = 31 + +type kexDHGexGroupMsg struct { + P *big.Int `sshtype:"31"` + G *big.Int +} + +const msgKexDHGexInit = 32 + +type kexDHGexInitMsg struct { + X *big.Int `sshtype:"32"` +} + +const msgKexDHGexReply = 33 + +type kexDHGexReplyMsg struct { + HostKey []byte `sshtype:"33"` + Y *big.Int + Signature []byte +} + +const msgKexDHGexRequest = 34 + +type kexDHGexRequestMsg struct { + MinBits uint32 `sshtype:"34"` + PreferedBits uint32 + MaxBits uint32 +} + +// See RFC 4253, section 10. +const msgServiceRequest = 5 + +type serviceRequestMsg struct { + Service string `sshtype:"5"` +} + +// See RFC 4253, section 10. +const msgServiceAccept = 6 + +type serviceAcceptMsg struct { + Service string `sshtype:"6"` +} + +// See RFC 4252, section 5. +const msgUserAuthRequest = 50 + +type userAuthRequestMsg struct { + User string `sshtype:"50"` + Service string + Method string + Payload []byte `ssh:"rest"` +} + +// Used for debug printouts of packets. +type userAuthSuccessMsg struct { +} + +// See RFC 4252, section 5.1 +const msgUserAuthFailure = 51 + +type userAuthFailureMsg struct { + Methods []string `sshtype:"51"` + PartialSuccess bool +} + +// See RFC 4252, section 5.1 +const msgUserAuthSuccess = 52 + +// See RFC 4252, section 5.4 +const msgUserAuthBanner = 53 + +type userAuthBannerMsg struct { + Message string `sshtype:"53"` + // unused, but required to allow message parsing + Language string +} + +// See RFC 4256, section 3.2 +const msgUserAuthInfoRequest = 60 +const msgUserAuthInfoResponse = 61 + +type userAuthInfoRequestMsg struct { + User string `sshtype:"60"` + Instruction string + DeprecatedLanguage string + NumPrompts uint32 + Prompts []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpen = 90 + +type channelOpenMsg struct { + ChanType string `sshtype:"90"` + PeersID uint32 + PeersWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +const msgChannelExtendedData = 95 +const msgChannelData = 94 + +// Used for debug print outs of packets. +type channelDataMsg struct { + PeersID uint32 `sshtype:"94"` + Length uint32 + Rest []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpenConfirm = 91 + +type channelOpenConfirmMsg struct { + PeersID uint32 `sshtype:"91"` + MyID uint32 + MyWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpenFailure = 92 + +type channelOpenFailureMsg struct { + PeersID uint32 `sshtype:"92"` + Reason RejectionReason + Message string + Language string +} + +const msgChannelRequest = 98 + +type channelRequestMsg struct { + PeersID uint32 `sshtype:"98"` + Request string + WantReply bool + RequestSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.4. +const msgChannelSuccess = 99 + +type channelRequestSuccessMsg struct { + PeersID uint32 `sshtype:"99"` +} + +// See RFC 4254, section 5.4. +const msgChannelFailure = 100 + +type channelRequestFailureMsg struct { + PeersID uint32 `sshtype:"100"` +} + +// See RFC 4254, section 5.3 +const msgChannelClose = 97 + +type channelCloseMsg struct { + PeersID uint32 `sshtype:"97"` +} + +// See RFC 4254, section 5.3 +const msgChannelEOF = 96 + +type channelEOFMsg struct { + PeersID uint32 `sshtype:"96"` +} + +// See RFC 4254, section 4 +const msgGlobalRequest = 80 + +type globalRequestMsg struct { + Type string `sshtype:"80"` + WantReply bool + Data []byte `ssh:"rest"` +} + +// See RFC 4254, section 4 +const msgRequestSuccess = 81 + +type globalRequestSuccessMsg struct { + Data []byte `ssh:"rest" sshtype:"81"` +} + +// See RFC 4254, section 4 +const msgRequestFailure = 82 + +type globalRequestFailureMsg struct { + Data []byte `ssh:"rest" sshtype:"82"` +} + +// See RFC 4254, section 5.2 +const msgChannelWindowAdjust = 93 + +type windowAdjustMsg struct { + PeersID uint32 `sshtype:"93"` + AdditionalBytes uint32 +} + +// See RFC 4252, section 7 +const msgUserAuthPubKeyOk = 60 + +type userAuthPubKeyOkMsg struct { + Algo string `sshtype:"60"` + PubKey []byte +} + +// See RFC 4462, section 3 +const msgUserAuthGSSAPIResponse = 60 + +type userAuthGSSAPIResponse struct { + SupportMech []byte `sshtype:"60"` +} + +const msgUserAuthGSSAPIToken = 61 + +type userAuthGSSAPIToken struct { + Token []byte `sshtype:"61"` +} + +const msgUserAuthGSSAPIMIC = 66 + +type userAuthGSSAPIMIC struct { + MIC []byte `sshtype:"66"` +} + +// See RFC 4462, section 3.9 +const msgUserAuthGSSAPIErrTok = 64 + +type userAuthGSSAPIErrTok struct { + ErrorToken []byte `sshtype:"64"` +} + +// See RFC 4462, section 3.8 +const msgUserAuthGSSAPIError = 65 + +type userAuthGSSAPIError struct { + MajorStatus uint32 `sshtype:"65"` + MinorStatus uint32 + Message string + LanguageTag string +} + +// typeTags returns the possible type bytes for the given reflect.Type, which +// should be a struct. The possible values are separated by a '|' character. +func typeTags(structType reflect.Type) (tags []byte) { + tagStr := structType.Field(0).Tag.Get("sshtype") + + for _, tag := range strings.Split(tagStr, "|") { + i, err := strconv.Atoi(tag) + if err == nil { + tags = append(tags, byte(i)) + } + } + + return tags +} + +func fieldError(t reflect.Type, field int, problem string) error { + if problem != "" { + problem = ": " + problem + } + return fmt.Errorf("ssh: unmarshal error for field %s of type %s%s", t.Field(field).Name, t.Name(), problem) +} + +var errShortRead = errors.New("ssh: short read") + +// Unmarshal parses data in SSH wire format into a structure. The out +// argument should be a pointer to struct. If the first member of the +// struct has the "sshtype" tag set to a '|'-separated set of numbers +// in decimal, the packet must start with one of those numbers. In +// case of error, Unmarshal returns a ParseError or +// UnexpectedMessageError. +func Unmarshal(data []byte, out interface{}) error { + v := reflect.ValueOf(out).Elem() + structType := v.Type() + expectedTypes := typeTags(structType) + + var expectedType byte + if len(expectedTypes) > 0 { + expectedType = expectedTypes[0] + } + + if len(data) == 0 { + return parseError(expectedType) + } + + if len(expectedTypes) > 0 { + goodType := false + for _, e := range expectedTypes { + if e > 0 && data[0] == e { + goodType = true + break + } + } + if !goodType { + return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes) + } + data = data[1:] + } + + var ok bool + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + t := field.Type() + switch t.Kind() { + case reflect.Bool: + if len(data) < 1 { + return errShortRead + } + field.SetBool(data[0] != 0) + data = data[1:] + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + return fieldError(structType, i, "array of unsupported type") + } + if len(data) < t.Len() { + return errShortRead + } + for j, n := 0, t.Len(); j < n; j++ { + field.Index(j).Set(reflect.ValueOf(data[j])) + } + data = data[t.Len():] + case reflect.Uint64: + var u64 uint64 + if u64, data, ok = parseUint64(data); !ok { + return errShortRead + } + field.SetUint(u64) + case reflect.Uint32: + var u32 uint32 + if u32, data, ok = parseUint32(data); !ok { + return errShortRead + } + field.SetUint(uint64(u32)) + case reflect.Uint8: + if len(data) < 1 { + return errShortRead + } + field.SetUint(uint64(data[0])) + data = data[1:] + case reflect.String: + var s []byte + if s, data, ok = parseString(data); !ok { + return fieldError(structType, i, "") + } + field.SetString(string(s)) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if structType.Field(i).Tag.Get("ssh") == "rest" { + field.Set(reflect.ValueOf(data)) + data = nil + } else { + var s []byte + if s, data, ok = parseString(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(s)) + } + case reflect.String: + var nl []string + if nl, data, ok = parseNameList(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(nl)) + default: + return fieldError(structType, i, "slice of unsupported type") + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + if n, data, ok = parseInt(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(n)) + } else { + return fieldError(structType, i, "pointer to unsupported type") + } + default: + return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t)) + } + } + + if len(data) != 0 { + return parseError(expectedType) + } + + return nil +} + +// Marshal serializes the message in msg to SSH wire format. The msg +// argument should be a struct or pointer to struct. If the first +// member has the "sshtype" tag set to a number in decimal, that +// number is prepended to the result. If the last of member has the +// "ssh" tag set to "rest", its contents are appended to the output. +func Marshal(msg interface{}) []byte { + out := make([]byte, 0, 64) + return marshalStruct(out, msg) +} + +func marshalStruct(out []byte, msg interface{}) []byte { + v := reflect.Indirect(reflect.ValueOf(msg)) + msgTypes := typeTags(v.Type()) + if len(msgTypes) > 0 { + out = append(out, msgTypes[0]) + } + + for i, n := 0, v.NumField(); i < n; i++ { + field := v.Field(i) + switch t := field.Type(); t.Kind() { + case reflect.Bool: + var v uint8 + if field.Bool() { + v = 1 + } + out = append(out, v) + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + panic(fmt.Sprintf("array of non-uint8 in field %d: %T", i, field.Interface())) + } + for j, l := 0, t.Len(); j < l; j++ { + out = append(out, uint8(field.Index(j).Uint())) + } + case reflect.Uint32: + out = appendU32(out, uint32(field.Uint())) + case reflect.Uint64: + out = appendU64(out, uint64(field.Uint())) + case reflect.Uint8: + out = append(out, uint8(field.Uint())) + case reflect.String: + s := field.String() + out = appendInt(out, len(s)) + out = append(out, s...) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if v.Type().Field(i).Tag.Get("ssh") != "rest" { + out = appendInt(out, field.Len()) + } + out = append(out, field.Bytes()...) + case reflect.String: + offset := len(out) + out = appendU32(out, 0) + if n := field.Len(); n > 0 { + for j := 0; j < n; j++ { + f := field.Index(j) + if j != 0 { + out = append(out, ',') + } + out = append(out, f.String()...) + } + // overwrite length value + binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4)) + } + default: + panic(fmt.Sprintf("slice of unknown type in field %d: %T", i, field.Interface())) + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + nValue := reflect.ValueOf(&n) + nValue.Elem().Set(field) + needed := intLength(n) + oldLength := len(out) + + if cap(out)-len(out) < needed { + newOut := make([]byte, len(out), 2*(len(out)+needed)) + copy(newOut, out) + out = newOut + } + out = out[:oldLength+needed] + marshalInt(out[oldLength:], n) + } else { + panic(fmt.Sprintf("pointer to unknown type in field %d: %T", i, field.Interface())) + } + } + } + + return out +} + +var bigOne = big.NewInt(1) + +func parseString(in []byte) (out, rest []byte, ok bool) { + if len(in) < 4 { + return + } + length := binary.BigEndian.Uint32(in) + in = in[4:] + if uint32(len(in)) < length { + return + } + out = in[:length] + rest = in[length:] + ok = true + return +} + +var ( + comma = []byte{','} + emptyNameList = []string{} +) + +func parseNameList(in []byte) (out []string, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + if len(contents) == 0 { + out = emptyNameList + return + } + parts := bytes.Split(contents, comma) + out = make([]string, len(parts)) + for i, part := range parts { + out[i] = string(part) + } + return +} + +func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + out = new(big.Int) + + if len(contents) > 0 && contents[0]&0x80 == 0x80 { + // This is a negative number + notBytes := make([]byte, len(contents)) + for i := range notBytes { + notBytes[i] = ^contents[i] + } + out.SetBytes(notBytes) + out.Add(out, bigOne) + out.Neg(out) + } else { + // Positive number + out.SetBytes(contents) + } + ok = true + return +} + +func parseUint32(in []byte) (uint32, []byte, bool) { + if len(in) < 4 { + return 0, nil, false + } + return binary.BigEndian.Uint32(in), in[4:], true +} + +func parseUint64(in []byte) (uint64, []byte, bool) { + if len(in) < 8 { + return 0, nil, false + } + return binary.BigEndian.Uint64(in), in[8:], true +} + +func intLength(n *big.Int) int { + length := 4 /* length bytes */ + if n.Sign() < 0 { + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bitLen := nMinus1.BitLen() + if bitLen%8 == 0 { + // The number will need 0xff padding + length++ + } + length += (bitLen + 7) / 8 + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bitLen := n.BitLen() + if bitLen%8 == 0 { + // The number will need 0x00 padding + length++ + } + length += (bitLen + 7) / 8 + } + + return length +} + +func marshalUint32(to []byte, n uint32) []byte { + binary.BigEndian.PutUint32(to, n) + return to[4:] +} + +func marshalUint64(to []byte, n uint64) []byte { + binary.BigEndian.PutUint64(to, n) + return to[8:] +} + +func marshalInt(to []byte, n *big.Int) []byte { + lengthBytes := to + to = to[4:] + length := 0 + + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll subtract 1 and invert. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + to[0] = 0xff + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with a 0x00 in order to + // stop it looking like a negative number. + to[0] = 0 + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } + + lengthBytes[0] = byte(length >> 24) + lengthBytes[1] = byte(length >> 16) + lengthBytes[2] = byte(length >> 8) + lengthBytes[3] = byte(length) + return to +} + +func writeInt(w io.Writer, n *big.Int) { + length := intLength(n) + buf := make([]byte, length) + marshalInt(buf, n) + w.Write(buf) +} + +func writeString(w io.Writer, s []byte) { + var lengthBytes [4]byte + lengthBytes[0] = byte(len(s) >> 24) + lengthBytes[1] = byte(len(s) >> 16) + lengthBytes[2] = byte(len(s) >> 8) + lengthBytes[3] = byte(len(s)) + w.Write(lengthBytes[:]) + w.Write(s) +} + +func stringLength(n int) int { + return 4 + n +} + +func marshalString(to []byte, s []byte) []byte { + to[0] = byte(len(s) >> 24) + to[1] = byte(len(s) >> 16) + to[2] = byte(len(s) >> 8) + to[3] = byte(len(s)) + to = to[4:] + copy(to, s) + return to[len(s):] +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)) + +// Decode a packet into its corresponding message. +func decode(packet []byte) (interface{}, error) { + var msg interface{} + switch packet[0] { + case msgDisconnect: + msg = new(disconnectMsg) + case msgServiceRequest: + msg = new(serviceRequestMsg) + case msgServiceAccept: + msg = new(serviceAcceptMsg) + case msgKexInit: + msg = new(kexInitMsg) + case msgKexDHInit: + msg = new(kexDHInitMsg) + case msgKexDHReply: + msg = new(kexDHReplyMsg) + case msgUserAuthRequest: + msg = new(userAuthRequestMsg) + case msgUserAuthSuccess: + return new(userAuthSuccessMsg), nil + case msgUserAuthFailure: + msg = new(userAuthFailureMsg) + case msgUserAuthPubKeyOk: + msg = new(userAuthPubKeyOkMsg) + case msgGlobalRequest: + msg = new(globalRequestMsg) + case msgRequestSuccess: + msg = new(globalRequestSuccessMsg) + case msgRequestFailure: + msg = new(globalRequestFailureMsg) + case msgChannelOpen: + msg = new(channelOpenMsg) + case msgChannelData: + msg = new(channelDataMsg) + case msgChannelOpenConfirm: + msg = new(channelOpenConfirmMsg) + case msgChannelOpenFailure: + msg = new(channelOpenFailureMsg) + case msgChannelWindowAdjust: + msg = new(windowAdjustMsg) + case msgChannelEOF: + msg = new(channelEOFMsg) + case msgChannelClose: + msg = new(channelCloseMsg) + case msgChannelRequest: + msg = new(channelRequestMsg) + case msgChannelSuccess: + msg = new(channelRequestSuccessMsg) + case msgChannelFailure: + msg = new(channelRequestFailureMsg) + case msgUserAuthGSSAPIToken: + msg = new(userAuthGSSAPIToken) + case msgUserAuthGSSAPIMIC: + msg = new(userAuthGSSAPIMIC) + case msgUserAuthGSSAPIErrTok: + msg = new(userAuthGSSAPIErrTok) + case msgUserAuthGSSAPIError: + msg = new(userAuthGSSAPIError) + default: + return nil, unexpectedMessageError(0, packet[0]) + } + if err := Unmarshal(packet, msg); err != nil { + return nil, err + } + return msg, nil +} + +var packetTypeNames = map[byte]string{ + msgDisconnect: "disconnectMsg", + msgServiceRequest: "serviceRequestMsg", + msgServiceAccept: "serviceAcceptMsg", + msgKexInit: "kexInitMsg", + msgKexDHInit: "kexDHInitMsg", + msgKexDHReply: "kexDHReplyMsg", + msgUserAuthRequest: "userAuthRequestMsg", + msgUserAuthSuccess: "userAuthSuccessMsg", + msgUserAuthFailure: "userAuthFailureMsg", + msgUserAuthPubKeyOk: "userAuthPubKeyOkMsg", + msgGlobalRequest: "globalRequestMsg", + msgRequestSuccess: "globalRequestSuccessMsg", + msgRequestFailure: "globalRequestFailureMsg", + msgChannelOpen: "channelOpenMsg", + msgChannelData: "channelDataMsg", + msgChannelOpenConfirm: "channelOpenConfirmMsg", + msgChannelOpenFailure: "channelOpenFailureMsg", + msgChannelWindowAdjust: "windowAdjustMsg", + msgChannelEOF: "channelEOFMsg", + msgChannelClose: "channelCloseMsg", + msgChannelRequest: "channelRequestMsg", + msgChannelSuccess: "channelRequestSuccessMsg", + msgChannelFailure: "channelRequestFailureMsg", +} diff --git a/vendor/golang.org/x/crypto/ssh/mux.go b/vendor/golang.org/x/crypto/ssh/mux.go new file mode 100644 index 00000000000..f19016270e8 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mux.go @@ -0,0 +1,330 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/binary" + "fmt" + "io" + "log" + "sync" + "sync/atomic" +) + +// debugMux, if set, causes messages in the connection protocol to be +// logged. +const debugMux = false + +// chanList is a thread safe channel list. +type chanList struct { + // protects concurrent access to chans + sync.Mutex + + // chans are indexed by the local id of the channel, which the + // other side should send in the PeersId field. + chans []*channel + + // This is a debugging aid: it offsets all IDs by this + // amount. This helps distinguish otherwise identical + // server/client muxes + offset uint32 +} + +// Assigns a channel ID to the given channel. +func (c *chanList) add(ch *channel) uint32 { + c.Lock() + defer c.Unlock() + for i := range c.chans { + if c.chans[i] == nil { + c.chans[i] = ch + return uint32(i) + c.offset + } + } + c.chans = append(c.chans, ch) + return uint32(len(c.chans)-1) + c.offset +} + +// getChan returns the channel for the given ID. +func (c *chanList) getChan(id uint32) *channel { + id -= c.offset + + c.Lock() + defer c.Unlock() + if id < uint32(len(c.chans)) { + return c.chans[id] + } + return nil +} + +func (c *chanList) remove(id uint32) { + id -= c.offset + c.Lock() + if id < uint32(len(c.chans)) { + c.chans[id] = nil + } + c.Unlock() +} + +// dropAll forgets all channels it knows, returning them in a slice. +func (c *chanList) dropAll() []*channel { + c.Lock() + defer c.Unlock() + var r []*channel + + for _, ch := range c.chans { + if ch == nil { + continue + } + r = append(r, ch) + } + c.chans = nil + return r +} + +// mux represents the state for the SSH connection protocol, which +// multiplexes many channels onto a single packet transport. +type mux struct { + conn packetConn + chanList chanList + + incomingChannels chan NewChannel + + globalSentMu sync.Mutex + globalResponses chan interface{} + incomingRequests chan *Request + + errCond *sync.Cond + err error +} + +// When debugging, each new chanList instantiation has a different +// offset. +var globalOff uint32 + +func (m *mux) Wait() error { + m.errCond.L.Lock() + defer m.errCond.L.Unlock() + for m.err == nil { + m.errCond.Wait() + } + return m.err +} + +// newMux returns a mux that runs over the given connection. +func newMux(p packetConn) *mux { + m := &mux{ + conn: p, + incomingChannels: make(chan NewChannel, chanSize), + globalResponses: make(chan interface{}, 1), + incomingRequests: make(chan *Request, chanSize), + errCond: newCond(), + } + if debugMux { + m.chanList.offset = atomic.AddUint32(&globalOff, 1) + } + + go m.loop() + return m +} + +func (m *mux) sendMessage(msg interface{}) error { + p := Marshal(msg) + if debugMux { + log.Printf("send global(%d): %#v", m.chanList.offset, msg) + } + return m.conn.writePacket(p) +} + +func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) { + if wantReply { + m.globalSentMu.Lock() + defer m.globalSentMu.Unlock() + } + + if err := m.sendMessage(globalRequestMsg{ + Type: name, + WantReply: wantReply, + Data: payload, + }); err != nil { + return false, nil, err + } + + if !wantReply { + return false, nil, nil + } + + msg, ok := <-m.globalResponses + if !ok { + return false, nil, io.EOF + } + switch msg := msg.(type) { + case *globalRequestFailureMsg: + return false, msg.Data, nil + case *globalRequestSuccessMsg: + return true, msg.Data, nil + default: + return false, nil, fmt.Errorf("ssh: unexpected response to request: %#v", msg) + } +} + +// ackRequest must be called after processing a global request that +// has WantReply set. +func (m *mux) ackRequest(ok bool, data []byte) error { + if ok { + return m.sendMessage(globalRequestSuccessMsg{Data: data}) + } + return m.sendMessage(globalRequestFailureMsg{Data: data}) +} + +func (m *mux) Close() error { + return m.conn.Close() +} + +// loop runs the connection machine. It will process packets until an +// error is encountered. To synchronize on loop exit, use mux.Wait. +func (m *mux) loop() { + var err error + for err == nil { + err = m.onePacket() + } + + for _, ch := range m.chanList.dropAll() { + ch.close() + } + + close(m.incomingChannels) + close(m.incomingRequests) + close(m.globalResponses) + + m.conn.Close() + + m.errCond.L.Lock() + m.err = err + m.errCond.Broadcast() + m.errCond.L.Unlock() + + if debugMux { + log.Println("loop exit", err) + } +} + +// onePacket reads and processes one packet. +func (m *mux) onePacket() error { + packet, err := m.conn.readPacket() + if err != nil { + return err + } + + if debugMux { + if packet[0] == msgChannelData || packet[0] == msgChannelExtendedData { + log.Printf("decoding(%d): data packet - %d bytes", m.chanList.offset, len(packet)) + } else { + p, _ := decode(packet) + log.Printf("decoding(%d): %d %#v - %d bytes", m.chanList.offset, packet[0], p, len(packet)) + } + } + + switch packet[0] { + case msgChannelOpen: + return m.handleChannelOpen(packet) + case msgGlobalRequest, msgRequestSuccess, msgRequestFailure: + return m.handleGlobalPacket(packet) + } + + // assume a channel packet. + if len(packet) < 5 { + return parseError(packet[0]) + } + id := binary.BigEndian.Uint32(packet[1:]) + ch := m.chanList.getChan(id) + if ch == nil { + return fmt.Errorf("ssh: invalid channel %d", id) + } + + return ch.handlePacket(packet) +} + +func (m *mux) handleGlobalPacket(packet []byte) error { + msg, err := decode(packet) + if err != nil { + return err + } + + switch msg := msg.(type) { + case *globalRequestMsg: + m.incomingRequests <- &Request{ + Type: msg.Type, + WantReply: msg.WantReply, + Payload: msg.Data, + mux: m, + } + case *globalRequestSuccessMsg, *globalRequestFailureMsg: + m.globalResponses <- msg + default: + panic(fmt.Sprintf("not a global message %#v", msg)) + } + + return nil +} + +// handleChannelOpen schedules a channel to be Accept()ed. +func (m *mux) handleChannelOpen(packet []byte) error { + var msg channelOpenMsg + if err := Unmarshal(packet, &msg); err != nil { + return err + } + + if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { + failMsg := channelOpenFailureMsg{ + PeersID: msg.PeersID, + Reason: ConnectionFailed, + Message: "invalid request", + Language: "en_US.UTF-8", + } + return m.sendMessage(failMsg) + } + + c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData) + c.remoteId = msg.PeersID + c.maxRemotePayload = msg.MaxPacketSize + c.remoteWin.add(msg.PeersWindow) + m.incomingChannels <- c + return nil +} + +func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Request, error) { + ch, err := m.openChannel(chanType, extra) + if err != nil { + return nil, nil, err + } + + return ch, ch.incomingRequests, nil +} + +func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) { + ch := m.newChannel(chanType, channelOutbound, extra) + + ch.maxIncomingPayload = channelMaxPacket + + open := channelOpenMsg{ + ChanType: chanType, + PeersWindow: ch.myWindow, + MaxPacketSize: ch.maxIncomingPayload, + TypeSpecificData: extra, + PeersID: ch.localId, + } + if err := m.sendMessage(open); err != nil { + return nil, err + } + + switch msg := (<-ch.msg).(type) { + case *channelOpenConfirmMsg: + return ch, nil + case *channelOpenFailureMsg: + return nil, &OpenChannelError{msg.Reason, msg.Message} + default: + return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go new file mode 100644 index 00000000000..7d42a8c88d2 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -0,0 +1,716 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + "strings" +) + +// The Permissions type holds fine-grained permissions that are +// specific to a user or a specific authentication method for a user. +// The Permissions value for a successful authentication attempt is +// available in ServerConn, so it can be used to pass information from +// the user-authentication phase to the application layer. +type Permissions struct { + // CriticalOptions indicate restrictions to the default + // permissions, and are typically used in conjunction with + // user certificates. The standard for SSH certificates + // defines "force-command" (only allow the given command to + // execute) and "source-address" (only allow connections from + // the given address). The SSH package currently only enforces + // the "source-address" critical option. It is up to server + // implementations to enforce other critical options, such as + // "force-command", by checking them after the SSH handshake + // is successful. In general, SSH servers should reject + // connections that specify critical options that are unknown + // or not supported. + CriticalOptions map[string]string + + // Extensions are extra functionality that the server may + // offer on authenticated connections. Lack of support for an + // extension does not preclude authenticating a user. Common + // extensions are "permit-agent-forwarding", + // "permit-X11-forwarding". The Go SSH library currently does + // not act on any extension, and it is up to server + // implementations to honor them. Extensions can be used to + // pass data from the authentication callbacks to the server + // application layer. + Extensions map[string]string +} + +type GSSAPIWithMICConfig struct { + // AllowLogin, must be set, is called when gssapi-with-mic + // authentication is selected (RFC 4462 section 3). The srcName is from the + // results of the GSS-API authentication. The format is username@DOMAIN. + // GSSAPI just guarantees to the server who the user is, but not if they can log in, and with what permissions. + // This callback is called after the user identity is established with GSSAPI to decide if the user can login with + // which permissions. If the user is allowed to login, it should return a nil error. + AllowLogin func(conn ConnMetadata, srcName string) (*Permissions, error) + + // Server must be set. It's the implementation + // of the GSSAPIServer interface. See GSSAPIServer interface for details. + Server GSSAPIServer +} + +// ServerConfig holds server specific configuration data. +type ServerConfig struct { + // Config contains configuration shared between client and server. + Config + + hostKeys []Signer + + // NoClientAuth is true if clients are allowed to connect without + // authenticating. + NoClientAuth bool + + // MaxAuthTries specifies the maximum number of authentication attempts + // permitted per connection. If set to a negative number, the number of + // attempts are unlimited. If set to zero, the number of attempts are limited + // to 6. + MaxAuthTries int + + // PasswordCallback, if non-nil, is called when a user + // attempts to authenticate using a password. + PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) + + // PublicKeyCallback, if non-nil, is called when a client + // offers a public key for authentication. It must return a nil error + // if the given public key can be used to authenticate the + // given user. For example, see CertChecker.Authenticate. A + // call to this function does not guarantee that the key + // offered is in fact used to authenticate. To record any data + // depending on the public key, store it inside a + // Permissions.Extensions entry. + PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) + + // KeyboardInteractiveCallback, if non-nil, is called when + // keyboard-interactive authentication is selected (RFC + // 4256). The client object's Challenge function should be + // used to query the user. The callback may offer multiple + // Challenge rounds. To avoid information leaks, the client + // should be presented a challenge even if the user is + // unknown. + KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error) + + // AuthLogCallback, if non-nil, is called to log all authentication + // attempts. + AuthLogCallback func(conn ConnMetadata, method string, err error) + + // ServerVersion is the version identification string to announce in + // the public handshake. + // If empty, a reasonable default is used. + // Note that RFC 4253 section 4.2 requires that this string start with + // "SSH-2.0-". + ServerVersion string + + // BannerCallback, if present, is called and the return string is sent to + // the client after key exchange completed but before authentication. + BannerCallback func(conn ConnMetadata) string + + // GSSAPIWithMICConfig includes gssapi server and callback, which if both non-nil, is used + // when gssapi-with-mic authentication is selected (RFC 4462 section 3). + GSSAPIWithMICConfig *GSSAPIWithMICConfig +} + +// AddHostKey adds a private key as a host key. If an existing host +// key exists with the same algorithm, it is overwritten. Each server +// config must have at least one host key. +func (s *ServerConfig) AddHostKey(key Signer) { + for i, k := range s.hostKeys { + if k.PublicKey().Type() == key.PublicKey().Type() { + s.hostKeys[i] = key + return + } + } + + s.hostKeys = append(s.hostKeys, key) +} + +// cachedPubKey contains the results of querying whether a public key is +// acceptable for a user. +type cachedPubKey struct { + user string + pubKeyData []byte + result error + perms *Permissions +} + +const maxCachedPubKeys = 16 + +// pubKeyCache caches tests for public keys. Since SSH clients +// will query whether a public key is acceptable before attempting to +// authenticate with it, we end up with duplicate queries for public +// key validity. The cache only applies to a single ServerConn. +type pubKeyCache struct { + keys []cachedPubKey +} + +// get returns the result for a given user/algo/key tuple. +func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) { + for _, k := range c.keys { + if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) { + return k, true + } + } + return cachedPubKey{}, false +} + +// add adds the given tuple to the cache. +func (c *pubKeyCache) add(candidate cachedPubKey) { + if len(c.keys) < maxCachedPubKeys { + c.keys = append(c.keys, candidate) + } +} + +// ServerConn is an authenticated SSH connection, as seen from the +// server +type ServerConn struct { + Conn + + // If the succeeding authentication callback returned a + // non-nil Permissions pointer, it is stored here. + Permissions *Permissions +} + +// NewServerConn starts a new SSH server with c as the underlying +// transport. It starts with a handshake and, if the handshake is +// unsuccessful, it closes the connection and returns an error. The +// Request and NewChannel channels must be serviced, or the connection +// will hang. +// +// The returned error may be of type *ServerAuthError for +// authentication errors. +func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { + fullConf := *config + fullConf.SetDefaults() + if fullConf.MaxAuthTries == 0 { + fullConf.MaxAuthTries = 6 + } + // Check if the config contains any unsupported key exchanges + for _, kex := range fullConf.KeyExchanges { + if _, ok := serverForbiddenKexAlgos[kex]; ok { + return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex) + } + } + + s := &connection{ + sshConn: sshConn{conn: c}, + } + perms, err := s.serverHandshake(&fullConf) + if err != nil { + c.Close() + return nil, nil, nil, err + } + return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil +} + +// signAndMarshal signs the data with the appropriate algorithm, +// and serializes the result in SSH wire format. +func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { + sig, err := k.Sign(rand, data) + if err != nil { + return nil, err + } + + return Marshal(sig), nil +} + +// handshake performs key exchange and user authentication. +func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) { + if len(config.hostKeys) == 0 { + return nil, errors.New("ssh: server has no host keys") + } + + if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && + config.KeyboardInteractiveCallback == nil && (config.GSSAPIWithMICConfig == nil || + config.GSSAPIWithMICConfig.AllowLogin == nil || config.GSSAPIWithMICConfig.Server == nil) { + return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if config.ServerVersion != "" { + s.serverVersion = []byte(config.ServerVersion) + } else { + s.serverVersion = []byte(packageVersion) + } + var err error + s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion) + if err != nil { + return nil, err + } + + tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) + s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) + + if err := s.transport.waitSession(); err != nil { + return nil, err + } + + // We just did the key change, so the session ID is established. + s.sessionID = s.transport.getSessionID() + + var packet []byte + if packet, err = s.transport.readPacket(); err != nil { + return nil, err + } + + var serviceRequest serviceRequestMsg + if err = Unmarshal(packet, &serviceRequest); err != nil { + return nil, err + } + if serviceRequest.Service != serviceUserAuth { + return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating") + } + serviceAccept := serviceAcceptMsg{ + Service: serviceUserAuth, + } + if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil { + return nil, err + } + + perms, err := s.serverAuthenticate(config) + if err != nil { + return nil, err + } + s.mux = newMux(s.transport) + return perms, err +} + +func isAcceptableAlgo(algo string) bool { + switch algo { + case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519, + CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01: + return true + } + return false +} + +func checkSourceAddress(addr net.Addr, sourceAddrs string) error { + if addr == nil { + return errors.New("ssh: no address known for client, but source-address match required") + } + + tcpAddr, ok := addr.(*net.TCPAddr) + if !ok { + return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr) + } + + for _, sourceAddr := range strings.Split(sourceAddrs, ",") { + if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { + if allowedIP.Equal(tcpAddr.IP) { + return nil + } + } else { + _, ipNet, err := net.ParseCIDR(sourceAddr) + if err != nil { + return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err) + } + + if ipNet.Contains(tcpAddr.IP) { + return nil + } + } + } + + return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) +} + +func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *connection, + sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) { + gssAPIServer := gssapiConfig.Server + defer gssAPIServer.DeleteSecContext() + var srcName string + for { + var ( + outToken []byte + needContinue bool + ) + outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(firstToken) + if err != nil { + return err, nil, nil + } + if len(outToken) != 0 { + if err := s.transport.writePacket(Marshal(&userAuthGSSAPIToken{ + Token: outToken, + })); err != nil { + return nil, nil, err + } + } + if !needContinue { + break + } + packet, err := s.transport.readPacket() + if err != nil { + return nil, nil, err + } + userAuthGSSAPITokenReq := &userAuthGSSAPIToken{} + if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil { + return nil, nil, err + } + } + packet, err := s.transport.readPacket() + if err != nil { + return nil, nil, err + } + userAuthGSSAPIMICReq := &userAuthGSSAPIMIC{} + if err := Unmarshal(packet, userAuthGSSAPIMICReq); err != nil { + return nil, nil, err + } + mic := buildMIC(string(sessionID), userAuthReq.User, userAuthReq.Service, userAuthReq.Method) + if err := gssAPIServer.VerifyMIC(mic, userAuthGSSAPIMICReq.MIC); err != nil { + return err, nil, nil + } + perms, authErr = gssapiConfig.AllowLogin(s, srcName) + return authErr, perms, nil +} + +// ServerAuthError represents server authentication errors and is +// sometimes returned by NewServerConn. It appends any authentication +// errors that may occur, and is returned if all of the authentication +// methods provided by the user failed to authenticate. +type ServerAuthError struct { + // Errors contains authentication errors returned by the authentication + // callback methods. The first entry is typically ErrNoAuth. + Errors []error +} + +func (l ServerAuthError) Error() string { + var errs []string + for _, err := range l.Errors { + errs = append(errs, err.Error()) + } + return "[" + strings.Join(errs, ", ") + "]" +} + +// ErrNoAuth is the error value returned if no +// authentication method has been passed yet. This happens as a normal +// part of the authentication loop, since the client first tries +// 'none' authentication to discover available methods. +// It is returned in ServerAuthError.Errors from NewServerConn. +var ErrNoAuth = errors.New("ssh: no auth passed yet") + +func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { + sessionID := s.transport.getSessionID() + var cache pubKeyCache + var perms *Permissions + + authFailures := 0 + var authErrs []error + var displayedBanner bool + +userAuthLoop: + for { + if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 { + discMsg := &disconnectMsg{ + Reason: 2, + Message: "too many authentication failures", + } + + if err := s.transport.writePacket(Marshal(discMsg)); err != nil { + return nil, err + } + + return nil, discMsg + } + + var userAuthReq userAuthRequestMsg + if packet, err := s.transport.readPacket(); err != nil { + if err == io.EOF { + return nil, &ServerAuthError{Errors: authErrs} + } + return nil, err + } else if err = Unmarshal(packet, &userAuthReq); err != nil { + return nil, err + } + + if userAuthReq.Service != serviceSSH { + return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) + } + + s.user = userAuthReq.User + + if !displayedBanner && config.BannerCallback != nil { + displayedBanner = true + msg := config.BannerCallback(s) + if msg != "" { + bannerMsg := &userAuthBannerMsg{ + Message: msg, + } + if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil { + return nil, err + } + } + } + + perms = nil + authErr := ErrNoAuth + + switch userAuthReq.Method { + case "none": + if config.NoClientAuth { + authErr = nil + } + + // allow initial attempt of 'none' without penalty + if authFailures == 0 { + authFailures-- + } + case "password": + if config.PasswordCallback == nil { + authErr = errors.New("ssh: password auth not configured") + break + } + payload := userAuthReq.Payload + if len(payload) < 1 || payload[0] != 0 { + return nil, parseError(msgUserAuthRequest) + } + payload = payload[1:] + password, payload, ok := parseString(payload) + if !ok || len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + + perms, authErr = config.PasswordCallback(s, password) + case "keyboard-interactive": + if config.KeyboardInteractiveCallback == nil { + authErr = errors.New("ssh: keyboard-interactive auth not configured") + break + } + + prompter := &sshClientKeyboardInteractive{s} + perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge) + case "publickey": + if config.PublicKeyCallback == nil { + authErr = errors.New("ssh: publickey auth not configured") + break + } + payload := userAuthReq.Payload + if len(payload) < 1 { + return nil, parseError(msgUserAuthRequest) + } + isQuery := payload[0] == 0 + payload = payload[1:] + algoBytes, payload, ok := parseString(payload) + if !ok { + return nil, parseError(msgUserAuthRequest) + } + algo := string(algoBytes) + if !isAcceptableAlgo(algo) { + authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo) + break + } + + pubKeyData, payload, ok := parseString(payload) + if !ok { + return nil, parseError(msgUserAuthRequest) + } + + pubKey, err := ParsePublicKey(pubKeyData) + if err != nil { + return nil, err + } + + candidate, ok := cache.get(s.user, pubKeyData) + if !ok { + candidate.user = s.user + candidate.pubKeyData = pubKeyData + candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey) + if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" { + candidate.result = checkSourceAddress( + s.RemoteAddr(), + candidate.perms.CriticalOptions[sourceAddressCriticalOption]) + } + cache.add(candidate) + } + + if isQuery { + // The client can query if the given public key + // would be okay. + + if len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + + if candidate.result == nil { + okMsg := userAuthPubKeyOkMsg{ + Algo: algo, + PubKey: pubKeyData, + } + if err = s.transport.writePacket(Marshal(&okMsg)); err != nil { + return nil, err + } + continue userAuthLoop + } + authErr = candidate.result + } else { + sig, payload, ok := parseSignature(payload) + if !ok || len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + // Ensure the public key algo and signature algo + // are supported. Compare the private key + // algorithm name that corresponds to algo with + // sig.Format. This is usually the same, but + // for certs, the names differ. + if !isAcceptableAlgo(sig.Format) { + authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format) + break + } + signedData := buildDataSignedForAuth(sessionID, userAuthReq, algoBytes, pubKeyData) + + if err := pubKey.Verify(signedData, sig); err != nil { + return nil, err + } + + authErr = candidate.result + perms = candidate.perms + } + case "gssapi-with-mic": + gssapiConfig := config.GSSAPIWithMICConfig + userAuthRequestGSSAPI, err := parseGSSAPIPayload(userAuthReq.Payload) + if err != nil { + return nil, parseError(msgUserAuthRequest) + } + // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication. + if userAuthRequestGSSAPI.N == 0 { + authErr = fmt.Errorf("ssh: Mechanism negotiation is not supported") + break + } + var i uint32 + present := false + for i = 0; i < userAuthRequestGSSAPI.N; i++ { + if userAuthRequestGSSAPI.OIDS[i].Equal(krb5Mesh) { + present = true + break + } + } + if !present { + authErr = fmt.Errorf("ssh: GSSAPI authentication must use the Kerberos V5 mechanism") + break + } + // Initial server response, see RFC 4462 section 3.3. + if err := s.transport.writePacket(Marshal(&userAuthGSSAPIResponse{ + SupportMech: krb5OID, + })); err != nil { + return nil, err + } + // Exchange token, see RFC 4462 section 3.4. + packet, err := s.transport.readPacket() + if err != nil { + return nil, err + } + userAuthGSSAPITokenReq := &userAuthGSSAPIToken{} + if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil { + return nil, err + } + authErr, perms, err = gssExchangeToken(gssapiConfig, userAuthGSSAPITokenReq.Token, s, sessionID, + userAuthReq) + if err != nil { + return nil, err + } + default: + authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) + } + + authErrs = append(authErrs, authErr) + + if config.AuthLogCallback != nil { + config.AuthLogCallback(s, userAuthReq.Method, authErr) + } + + if authErr == nil { + break userAuthLoop + } + + authFailures++ + + var failureMsg userAuthFailureMsg + if config.PasswordCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "password") + } + if config.PublicKeyCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "publickey") + } + if config.KeyboardInteractiveCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive") + } + if config.GSSAPIWithMICConfig != nil && config.GSSAPIWithMICConfig.Server != nil && + config.GSSAPIWithMICConfig.AllowLogin != nil { + failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic") + } + + if len(failureMsg.Methods) == 0 { + return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if err := s.transport.writePacket(Marshal(&failureMsg)); err != nil { + return nil, err + } + } + + if err := s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { + return nil, err + } + return perms, nil +} + +// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by +// asking the client on the other side of a ServerConn. +type sshClientKeyboardInteractive struct { + *connection +} + +func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { + if len(questions) != len(echos) { + return nil, errors.New("ssh: echos and questions must have equal length") + } + + var prompts []byte + for i := range questions { + prompts = appendString(prompts, questions[i]) + prompts = appendBool(prompts, echos[i]) + } + + if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{ + Instruction: instruction, + NumPrompts: uint32(len(questions)), + Prompts: prompts, + })); err != nil { + return nil, err + } + + packet, err := c.transport.readPacket() + if err != nil { + return nil, err + } + if packet[0] != msgUserAuthInfoResponse { + return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0]) + } + packet = packet[1:] + + n, packet, ok := parseUint32(packet) + if !ok || int(n) != len(questions) { + return nil, parseError(msgUserAuthInfoResponse) + } + + for i := uint32(0); i < n; i++ { + ans, rest, ok := parseString(packet) + if !ok { + return nil, parseError(msgUserAuthInfoResponse) + } + + answers = append(answers, string(ans)) + packet = rest + } + if len(packet) != 0 { + return nil, errors.New("ssh: junk at end of message") + } + + return answers, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go new file mode 100644 index 00000000000..d3321f6b784 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/session.go @@ -0,0 +1,647 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Session implements an interactive session described in +// "RFC 4254, section 6". + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "sync" +) + +type Signal string + +// POSIX signals as listed in RFC 4254 Section 6.10. +const ( + SIGABRT Signal = "ABRT" + SIGALRM Signal = "ALRM" + SIGFPE Signal = "FPE" + SIGHUP Signal = "HUP" + SIGILL Signal = "ILL" + SIGINT Signal = "INT" + SIGKILL Signal = "KILL" + SIGPIPE Signal = "PIPE" + SIGQUIT Signal = "QUIT" + SIGSEGV Signal = "SEGV" + SIGTERM Signal = "TERM" + SIGUSR1 Signal = "USR1" + SIGUSR2 Signal = "USR2" +) + +var signals = map[Signal]int{ + SIGABRT: 6, + SIGALRM: 14, + SIGFPE: 8, + SIGHUP: 1, + SIGILL: 4, + SIGINT: 2, + SIGKILL: 9, + SIGPIPE: 13, + SIGQUIT: 3, + SIGSEGV: 11, + SIGTERM: 15, +} + +type TerminalModes map[uint8]uint32 + +// POSIX terminal mode flags as listed in RFC 4254 Section 8. +const ( + tty_OP_END = 0 + VINTR = 1 + VQUIT = 2 + VERASE = 3 + VKILL = 4 + VEOF = 5 + VEOL = 6 + VEOL2 = 7 + VSTART = 8 + VSTOP = 9 + VSUSP = 10 + VDSUSP = 11 + VREPRINT = 12 + VWERASE = 13 + VLNEXT = 14 + VFLUSH = 15 + VSWTCH = 16 + VSTATUS = 17 + VDISCARD = 18 + IGNPAR = 30 + PARMRK = 31 + INPCK = 32 + ISTRIP = 33 + INLCR = 34 + IGNCR = 35 + ICRNL = 36 + IUCLC = 37 + IXON = 38 + IXANY = 39 + IXOFF = 40 + IMAXBEL = 41 + ISIG = 50 + ICANON = 51 + XCASE = 52 + ECHO = 53 + ECHOE = 54 + ECHOK = 55 + ECHONL = 56 + NOFLSH = 57 + TOSTOP = 58 + IEXTEN = 59 + ECHOCTL = 60 + ECHOKE = 61 + PENDIN = 62 + OPOST = 70 + OLCUC = 71 + ONLCR = 72 + OCRNL = 73 + ONOCR = 74 + ONLRET = 75 + CS7 = 90 + CS8 = 91 + PARENB = 92 + PARODD = 93 + TTY_OP_ISPEED = 128 + TTY_OP_OSPEED = 129 +) + +// A Session represents a connection to a remote command or shell. +type Session struct { + // Stdin specifies the remote process's standard input. + // If Stdin is nil, the remote process reads from an empty + // bytes.Buffer. + Stdin io.Reader + + // Stdout and Stderr specify the remote process's standard + // output and error. + // + // If either is nil, Run connects the corresponding file + // descriptor to an instance of ioutil.Discard. There is a + // fixed amount of buffering that is shared for the two streams. + // If either blocks it may eventually cause the remote + // command to block. + Stdout io.Writer + Stderr io.Writer + + ch Channel // the channel backing this session + started bool // true once Start, Run or Shell is invoked. + copyFuncs []func() error + errors chan error // one send per copyFunc + + // true if pipe method is active + stdinpipe, stdoutpipe, stderrpipe bool + + // stdinPipeWriter is non-nil if StdinPipe has not been called + // and Stdin was specified by the user; it is the write end of + // a pipe connecting Session.Stdin to the stdin channel. + stdinPipeWriter io.WriteCloser + + exitStatus chan error +} + +// SendRequest sends an out-of-band channel request on the SSH channel +// underlying the session. +func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { + return s.ch.SendRequest(name, wantReply, payload) +} + +func (s *Session) Close() error { + return s.ch.Close() +} + +// RFC 4254 Section 6.4. +type setenvRequest struct { + Name string + Value string +} + +// Setenv sets an environment variable that will be applied to any +// command executed by Shell or Run. +func (s *Session) Setenv(name, value string) error { + msg := setenvRequest{ + Name: name, + Value: value, + } + ok, err := s.ch.SendRequest("env", true, Marshal(&msg)) + if err == nil && !ok { + err = errors.New("ssh: setenv failed") + } + return err +} + +// RFC 4254 Section 6.2. +type ptyRequestMsg struct { + Term string + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 + Modelist string +} + +// RequestPty requests the association of a pty with the session on the remote host. +func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error { + var tm []byte + for k, v := range termmodes { + kv := struct { + Key byte + Val uint32 + }{k, v} + + tm = append(tm, Marshal(&kv)...) + } + tm = append(tm, tty_OP_END) + req := ptyRequestMsg{ + Term: term, + Columns: uint32(w), + Rows: uint32(h), + Width: uint32(w * 8), + Height: uint32(h * 8), + Modelist: string(tm), + } + ok, err := s.ch.SendRequest("pty-req", true, Marshal(&req)) + if err == nil && !ok { + err = errors.New("ssh: pty-req failed") + } + return err +} + +// RFC 4254 Section 6.5. +type subsystemRequestMsg struct { + Subsystem string +} + +// RequestSubsystem requests the association of a subsystem with the session on the remote host. +// A subsystem is a predefined command that runs in the background when the ssh session is initiated +func (s *Session) RequestSubsystem(subsystem string) error { + msg := subsystemRequestMsg{ + Subsystem: subsystem, + } + ok, err := s.ch.SendRequest("subsystem", true, Marshal(&msg)) + if err == nil && !ok { + err = errors.New("ssh: subsystem request failed") + } + return err +} + +// RFC 4254 Section 6.7. +type ptyWindowChangeMsg struct { + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 +} + +// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns. +func (s *Session) WindowChange(h, w int) error { + req := ptyWindowChangeMsg{ + Columns: uint32(w), + Rows: uint32(h), + Width: uint32(w * 8), + Height: uint32(h * 8), + } + _, err := s.ch.SendRequest("window-change", false, Marshal(&req)) + return err +} + +// RFC 4254 Section 6.9. +type signalMsg struct { + Signal string +} + +// Signal sends the given signal to the remote process. +// sig is one of the SIG* constants. +func (s *Session) Signal(sig Signal) error { + msg := signalMsg{ + Signal: string(sig), + } + + _, err := s.ch.SendRequest("signal", false, Marshal(&msg)) + return err +} + +// RFC 4254 Section 6.5. +type execMsg struct { + Command string +} + +// Start runs cmd on the remote host. Typically, the remote +// server passes cmd to the shell for interpretation. +// A Session only accepts one call to Run, Start or Shell. +func (s *Session) Start(cmd string) error { + if s.started { + return errors.New("ssh: session already started") + } + req := execMsg{ + Command: cmd, + } + + ok, err := s.ch.SendRequest("exec", true, Marshal(&req)) + if err == nil && !ok { + err = fmt.Errorf("ssh: command %v failed", cmd) + } + if err != nil { + return err + } + return s.start() +} + +// Run runs cmd on the remote host. Typically, the remote +// server passes cmd to the shell for interpretation. +// A Session only accepts one call to Run, Start, Shell, Output, +// or CombinedOutput. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the remote server does not send an exit status, an error of type +// *ExitMissingError is returned. If the command completes +// unsuccessfully or is interrupted by a signal, the error is of type +// *ExitError. Other error types may be returned for I/O problems. +func (s *Session) Run(cmd string) error { + err := s.Start(cmd) + if err != nil { + return err + } + return s.Wait() +} + +// Output runs cmd on the remote host and returns its standard output. +func (s *Session) Output(cmd string) ([]byte, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + var b bytes.Buffer + s.Stdout = &b + err := s.Run(cmd) + return b.Bytes(), err +} + +type singleWriter struct { + b bytes.Buffer + mu sync.Mutex +} + +func (w *singleWriter) Write(p []byte) (int, error) { + w.mu.Lock() + defer w.mu.Unlock() + return w.b.Write(p) +} + +// CombinedOutput runs cmd on the remote host and returns its combined +// standard output and standard error. +func (s *Session) CombinedOutput(cmd string) ([]byte, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + if s.Stderr != nil { + return nil, errors.New("ssh: Stderr already set") + } + var b singleWriter + s.Stdout = &b + s.Stderr = &b + err := s.Run(cmd) + return b.b.Bytes(), err +} + +// Shell starts a login shell on the remote host. A Session only +// accepts one call to Run, Start, Shell, Output, or CombinedOutput. +func (s *Session) Shell() error { + if s.started { + return errors.New("ssh: session already started") + } + + ok, err := s.ch.SendRequest("shell", true, nil) + if err == nil && !ok { + return errors.New("ssh: could not start shell") + } + if err != nil { + return err + } + return s.start() +} + +func (s *Session) start() error { + s.started = true + + type F func(*Session) + for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} { + setupFd(s) + } + + s.errors = make(chan error, len(s.copyFuncs)) + for _, fn := range s.copyFuncs { + go func(fn func() error) { + s.errors <- fn() + }(fn) + } + return nil +} + +// Wait waits for the remote command to exit. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the remote server does not send an exit status, an error of type +// *ExitMissingError is returned. If the command completes +// unsuccessfully or is interrupted by a signal, the error is of type +// *ExitError. Other error types may be returned for I/O problems. +func (s *Session) Wait() error { + if !s.started { + return errors.New("ssh: session not started") + } + waitErr := <-s.exitStatus + + if s.stdinPipeWriter != nil { + s.stdinPipeWriter.Close() + } + var copyError error + for range s.copyFuncs { + if err := <-s.errors; err != nil && copyError == nil { + copyError = err + } + } + if waitErr != nil { + return waitErr + } + return copyError +} + +func (s *Session) wait(reqs <-chan *Request) error { + wm := Waitmsg{status: -1} + // Wait for msg channel to be closed before returning. + for msg := range reqs { + switch msg.Type { + case "exit-status": + wm.status = int(binary.BigEndian.Uint32(msg.Payload)) + case "exit-signal": + var sigval struct { + Signal string + CoreDumped bool + Error string + Lang string + } + if err := Unmarshal(msg.Payload, &sigval); err != nil { + return err + } + + // Must sanitize strings? + wm.signal = sigval.Signal + wm.msg = sigval.Error + wm.lang = sigval.Lang + default: + // This handles keepalives and matches + // OpenSSH's behaviour. + if msg.WantReply { + msg.Reply(false, nil) + } + } + } + if wm.status == 0 { + return nil + } + if wm.status == -1 { + // exit-status was never sent from server + if wm.signal == "" { + // signal was not sent either. RFC 4254 + // section 6.10 recommends against this + // behavior, but it is allowed, so we let + // clients handle it. + return &ExitMissingError{} + } + wm.status = 128 + if _, ok := signals[Signal(wm.signal)]; ok { + wm.status += signals[Signal(wm.signal)] + } + } + + return &ExitError{wm} +} + +// ExitMissingError is returned if a session is torn down cleanly, but +// the server sends no confirmation of the exit status. +type ExitMissingError struct{} + +func (e *ExitMissingError) Error() string { + return "wait: remote command exited without exit status or exit signal" +} + +func (s *Session) stdin() { + if s.stdinpipe { + return + } + var stdin io.Reader + if s.Stdin == nil { + stdin = new(bytes.Buffer) + } else { + r, w := io.Pipe() + go func() { + _, err := io.Copy(w, s.Stdin) + w.CloseWithError(err) + }() + stdin, s.stdinPipeWriter = r, w + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.ch, stdin) + if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF { + err = err1 + } + return err + }) +} + +func (s *Session) stdout() { + if s.stdoutpipe { + return + } + if s.Stdout == nil { + s.Stdout = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.Stdout, s.ch) + return err + }) +} + +func (s *Session) stderr() { + if s.stderrpipe { + return + } + if s.Stderr == nil { + s.Stderr = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.Stderr, s.ch.Stderr()) + return err + }) +} + +// sessionStdin reroutes Close to CloseWrite. +type sessionStdin struct { + io.Writer + ch Channel +} + +func (s *sessionStdin) Close() error { + return s.ch.CloseWrite() +} + +// StdinPipe returns a pipe that will be connected to the +// remote command's standard input when the command starts. +func (s *Session) StdinPipe() (io.WriteCloser, error) { + if s.Stdin != nil { + return nil, errors.New("ssh: Stdin already set") + } + if s.started { + return nil, errors.New("ssh: StdinPipe after process started") + } + s.stdinpipe = true + return &sessionStdin{s.ch, s.ch}, nil +} + +// StdoutPipe returns a pipe that will be connected to the +// remote command's standard output when the command starts. +// There is a fixed amount of buffering that is shared between +// stdout and stderr streams. If the StdoutPipe reader is +// not serviced fast enough it may eventually cause the +// remote command to block. +func (s *Session) StdoutPipe() (io.Reader, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + if s.started { + return nil, errors.New("ssh: StdoutPipe after process started") + } + s.stdoutpipe = true + return s.ch, nil +} + +// StderrPipe returns a pipe that will be connected to the +// remote command's standard error when the command starts. +// There is a fixed amount of buffering that is shared between +// stdout and stderr streams. If the StderrPipe reader is +// not serviced fast enough it may eventually cause the +// remote command to block. +func (s *Session) StderrPipe() (io.Reader, error) { + if s.Stderr != nil { + return nil, errors.New("ssh: Stderr already set") + } + if s.started { + return nil, errors.New("ssh: StderrPipe after process started") + } + s.stderrpipe = true + return s.ch.Stderr(), nil +} + +// newSession returns a new interactive session on the remote host. +func newSession(ch Channel, reqs <-chan *Request) (*Session, error) { + s := &Session{ + ch: ch, + } + s.exitStatus = make(chan error, 1) + go func() { + s.exitStatus <- s.wait(reqs) + }() + + return s, nil +} + +// An ExitError reports unsuccessful completion of a remote command. +type ExitError struct { + Waitmsg +} + +func (e *ExitError) Error() string { + return e.Waitmsg.String() +} + +// Waitmsg stores the information about an exited remote command +// as reported by Wait. +type Waitmsg struct { + status int + signal string + msg string + lang string +} + +// ExitStatus returns the exit status of the remote command. +func (w Waitmsg) ExitStatus() int { + return w.status +} + +// Signal returns the exit signal of the remote command if +// it was terminated violently. +func (w Waitmsg) Signal() string { + return w.signal +} + +// Msg returns the exit message given by the remote command +func (w Waitmsg) Msg() string { + return w.msg +} + +// Lang returns the language tag. See RFC 3066 +func (w Waitmsg) Lang() string { + return w.lang +} + +func (w Waitmsg) String() string { + str := fmt.Sprintf("Process exited with status %v", w.status) + if w.signal != "" { + str += fmt.Sprintf(" from signal %v", w.signal) + } + if w.msg != "" { + str += fmt.Sprintf(". Reason was: %v", w.msg) + } + return str +} diff --git a/vendor/golang.org/x/crypto/ssh/ssh_gss.go b/vendor/golang.org/x/crypto/ssh/ssh_gss.go new file mode 100644 index 00000000000..24bd7c8e830 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/ssh_gss.go @@ -0,0 +1,139 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/asn1" + "errors" +) + +var krb5OID []byte + +func init() { + krb5OID, _ = asn1.Marshal(krb5Mesh) +} + +// GSSAPIClient provides the API to plug-in GSSAPI authentication for client logins. +type GSSAPIClient interface { + // InitSecContext initiates the establishment of a security context for GSS-API between the + // ssh client and ssh server. Initially the token parameter should be specified as nil. + // The routine may return a outputToken which should be transferred to + // the ssh server, where the ssh server will present it to + // AcceptSecContext. If no token need be sent, InitSecContext will indicate this by setting + // needContinue to false. To complete the context + // establishment, one or more reply tokens may be required from the ssh + // server;if so, InitSecContext will return a needContinue which is true. + // In this case, InitSecContext should be called again when the + // reply token is received from the ssh server, passing the reply + // token to InitSecContext via the token parameters. + // See RFC 2743 section 2.2.1 and RFC 4462 section 3.4. + InitSecContext(target string, token []byte, isGSSDelegCreds bool) (outputToken []byte, needContinue bool, err error) + // GetMIC generates a cryptographic MIC for the SSH2 message, and places + // the MIC in a token for transfer to the ssh server. + // The contents of the MIC field are obtained by calling GSS_GetMIC() + // over the following, using the GSS-API context that was just + // established: + // string session identifier + // byte SSH_MSG_USERAUTH_REQUEST + // string user name + // string service + // string "gssapi-with-mic" + // See RFC 2743 section 2.3.1 and RFC 4462 3.5. + GetMIC(micFiled []byte) ([]byte, error) + // Whenever possible, it should be possible for + // DeleteSecContext() calls to be successfully processed even + // if other calls cannot succeed, thereby enabling context-related + // resources to be released. + // In addition to deleting established security contexts, + // gss_delete_sec_context must also be able to delete "half-built" + // security contexts resulting from an incomplete sequence of + // InitSecContext()/AcceptSecContext() calls. + // See RFC 2743 section 2.2.3. + DeleteSecContext() error +} + +// GSSAPIServer provides the API to plug in GSSAPI authentication for server logins. +type GSSAPIServer interface { + // AcceptSecContext allows a remotely initiated security context between the application + // and a remote peer to be established by the ssh client. The routine may return a + // outputToken which should be transferred to the ssh client, + // where the ssh client will present it to InitSecContext. + // If no token need be sent, AcceptSecContext will indicate this + // by setting the needContinue to false. To + // complete the context establishment, one or more reply tokens may be + // required from the ssh client. if so, AcceptSecContext + // will return a needContinue which is true, in which case it + // should be called again when the reply token is received from the ssh + // client, passing the token to AcceptSecContext via the + // token parameters. + // The srcName return value is the authenticated username. + // See RFC 2743 section 2.2.2 and RFC 4462 section 3.4. + AcceptSecContext(token []byte) (outputToken []byte, srcName string, needContinue bool, err error) + // VerifyMIC verifies that a cryptographic MIC, contained in the token parameter, + // fits the supplied message is received from the ssh client. + // See RFC 2743 section 2.3.2. + VerifyMIC(micField []byte, micToken []byte) error + // Whenever possible, it should be possible for + // DeleteSecContext() calls to be successfully processed even + // if other calls cannot succeed, thereby enabling context-related + // resources to be released. + // In addition to deleting established security contexts, + // gss_delete_sec_context must also be able to delete "half-built" + // security contexts resulting from an incomplete sequence of + // InitSecContext()/AcceptSecContext() calls. + // See RFC 2743 section 2.2.3. + DeleteSecContext() error +} + +var ( + // OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication, + // so we also support the krb5 mechanism only. + // See RFC 1964 section 1. + krb5Mesh = asn1.ObjectIdentifier{1, 2, 840, 113554, 1, 2, 2} +) + +// The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST +// See RFC 4462 section 3.2. +type userAuthRequestGSSAPI struct { + N uint32 + OIDS []asn1.ObjectIdentifier +} + +func parseGSSAPIPayload(payload []byte) (*userAuthRequestGSSAPI, error) { + n, rest, ok := parseUint32(payload) + if !ok { + return nil, errors.New("parse uint32 failed") + } + s := &userAuthRequestGSSAPI{ + N: n, + OIDS: make([]asn1.ObjectIdentifier, n), + } + for i := 0; i < int(n); i++ { + var ( + desiredMech []byte + err error + ) + desiredMech, rest, ok = parseString(rest) + if !ok { + return nil, errors.New("parse string failed") + } + if rest, err = asn1.Unmarshal(desiredMech, &s.OIDS[i]); err != nil { + return nil, err + } + + } + return s, nil +} + +// See RFC 4462 section 3.6. +func buildMIC(sessionID string, username string, service string, authMethod string) []byte { + out := make([]byte, 0, 0) + out = appendString(out, sessionID) + out = append(out, msgUserAuthRequest) + out = appendString(out, username) + out = appendString(out, service) + out = appendString(out, authMethod) + return out +} diff --git a/vendor/golang.org/x/crypto/ssh/streamlocal.go b/vendor/golang.org/x/crypto/ssh/streamlocal.go new file mode 100644 index 00000000000..b171b330bc3 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/streamlocal.go @@ -0,0 +1,116 @@ +package ssh + +import ( + "errors" + "io" + "net" +) + +// streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message +// with "direct-streamlocal@openssh.com" string. +// +// See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding +// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235 +type streamLocalChannelOpenDirectMsg struct { + socketPath string + reserved0 string + reserved1 uint32 +} + +// forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message +// with "forwarded-streamlocal@openssh.com" string. +type forwardedStreamLocalPayload struct { + SocketPath string + Reserved0 string +} + +// streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message +// with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string. +type streamLocalChannelForwardMsg struct { + socketPath string +} + +// ListenUnix is similar to ListenTCP but uses a Unix domain socket. +func (c *Client) ListenUnix(socketPath string) (net.Listener, error) { + c.handleForwardsOnce.Do(c.handleForwards) + m := streamLocalChannelForwardMsg{ + socketPath, + } + // send message + ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m)) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer") + } + ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"}) + + return &unixListener{socketPath, c, ch}, nil +} + +func (c *Client) dialStreamLocal(socketPath string) (Channel, error) { + msg := streamLocalChannelOpenDirectMsg{ + socketPath: socketPath, + } + ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg)) + if err != nil { + return nil, err + } + go DiscardRequests(in) + return ch, err +} + +type unixListener struct { + socketPath string + + conn *Client + in <-chan forward +} + +// Accept waits for and returns the next connection to the listener. +func (l *unixListener) Accept() (net.Conn, error) { + s, ok := <-l.in + if !ok { + return nil, io.EOF + } + ch, incoming, err := s.newCh.Accept() + if err != nil { + return nil, err + } + go DiscardRequests(incoming) + + return &chanConn{ + Channel: ch, + laddr: &net.UnixAddr{ + Name: l.socketPath, + Net: "unix", + }, + raddr: &net.UnixAddr{ + Name: "@", + Net: "unix", + }, + }, nil +} + +// Close closes the listener. +func (l *unixListener) Close() error { + // this also closes the listener. + l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"}) + m := streamLocalChannelForwardMsg{ + l.socketPath, + } + ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m)) + if err == nil && !ok { + err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed") + } + return err +} + +// Addr returns the listener's network address. +func (l *unixListener) Addr() net.Addr { + return &net.UnixAddr{ + Name: l.socketPath, + Net: "unix", + } +} diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go new file mode 100644 index 00000000000..80d35f5ec18 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/tcpip.go @@ -0,0 +1,474 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "errors" + "fmt" + "io" + "math/rand" + "net" + "strconv" + "strings" + "sync" + "time" +) + +// Listen requests the remote peer open a listening socket on +// addr. Incoming connections will be available by calling Accept on +// the returned net.Listener. The listener must be serviced, or the +// SSH connection may hang. +// N must be "tcp", "tcp4", "tcp6", or "unix". +func (c *Client) Listen(n, addr string) (net.Listener, error) { + switch n { + case "tcp", "tcp4", "tcp6": + laddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.ListenTCP(laddr) + case "unix": + return c.ListenUnix(addr) + default: + return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) + } +} + +// Automatic port allocation is broken with OpenSSH before 6.0. See +// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In +// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0, +// rather than the actual port number. This means you can never open +// two different listeners with auto allocated ports. We work around +// this by trying explicit ports until we succeed. + +const openSSHPrefix = "OpenSSH_" + +var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano())) + +// isBrokenOpenSSHVersion returns true if the given version string +// specifies a version of OpenSSH that is known to have a bug in port +// forwarding. +func isBrokenOpenSSHVersion(versionStr string) bool { + i := strings.Index(versionStr, openSSHPrefix) + if i < 0 { + return false + } + i += len(openSSHPrefix) + j := i + for ; j < len(versionStr); j++ { + if versionStr[j] < '0' || versionStr[j] > '9' { + break + } + } + version, _ := strconv.Atoi(versionStr[i:j]) + return version < 6 +} + +// autoPortListenWorkaround simulates automatic port allocation by +// trying random ports repeatedly. +func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) { + var sshListener net.Listener + var err error + const tries = 10 + for i := 0; i < tries; i++ { + addr := *laddr + addr.Port = 1024 + portRandomizer.Intn(60000) + sshListener, err = c.ListenTCP(&addr) + if err == nil { + laddr.Port = addr.Port + return sshListener, err + } + } + return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err) +} + +// RFC 4254 7.1 +type channelForwardMsg struct { + addr string + rport uint32 +} + +// handleForwards starts goroutines handling forwarded connections. +// It's called on first use by (*Client).ListenTCP to not launch +// goroutines until needed. +func (c *Client) handleForwards() { + go c.forwards.handleChannels(c.HandleChannelOpen("forwarded-tcpip")) + go c.forwards.handleChannels(c.HandleChannelOpen("forwarded-streamlocal@openssh.com")) +} + +// ListenTCP requests the remote peer open a listening socket +// on laddr. Incoming connections will be available by calling +// Accept on the returned net.Listener. +func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { + c.handleForwardsOnce.Do(c.handleForwards) + if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) { + return c.autoPortListenWorkaround(laddr) + } + + m := channelForwardMsg{ + laddr.IP.String(), + uint32(laddr.Port), + } + // send message + ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m)) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("ssh: tcpip-forward request denied by peer") + } + + // If the original port was 0, then the remote side will + // supply a real port number in the response. + if laddr.Port == 0 { + var p struct { + Port uint32 + } + if err := Unmarshal(resp, &p); err != nil { + return nil, err + } + laddr.Port = int(p.Port) + } + + // Register this forward, using the port number we obtained. + ch := c.forwards.add(laddr) + + return &tcpListener{laddr, c, ch}, nil +} + +// forwardList stores a mapping between remote +// forward requests and the tcpListeners. +type forwardList struct { + sync.Mutex + entries []forwardEntry +} + +// forwardEntry represents an established mapping of a laddr on a +// remote ssh server to a channel connected to a tcpListener. +type forwardEntry struct { + laddr net.Addr + c chan forward +} + +// forward represents an incoming forwarded tcpip connection. The +// arguments to add/remove/lookup should be address as specified in +// the original forward-request. +type forward struct { + newCh NewChannel // the ssh client channel underlying this forward + raddr net.Addr // the raddr of the incoming connection +} + +func (l *forwardList) add(addr net.Addr) chan forward { + l.Lock() + defer l.Unlock() + f := forwardEntry{ + laddr: addr, + c: make(chan forward, 1), + } + l.entries = append(l.entries, f) + return f.c +} + +// See RFC 4254, section 7.2 +type forwardedTCPPayload struct { + Addr string + Port uint32 + OriginAddr string + OriginPort uint32 +} + +// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr. +func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) { + if port == 0 || port > 65535 { + return nil, fmt.Errorf("ssh: port number out of range: %d", port) + } + ip := net.ParseIP(string(addr)) + if ip == nil { + return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr) + } + return &net.TCPAddr{IP: ip, Port: int(port)}, nil +} + +func (l *forwardList) handleChannels(in <-chan NewChannel) { + for ch := range in { + var ( + laddr net.Addr + raddr net.Addr + err error + ) + switch channelType := ch.ChannelType(); channelType { + case "forwarded-tcpip": + var payload forwardedTCPPayload + if err = Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) + continue + } + + // RFC 4254 section 7.2 specifies that incoming + // addresses should list the address, in string + // format. It is implied that this should be an IP + // address, as it would be impossible to connect to it + // otherwise. + laddr, err = parseTCPAddr(payload.Addr, payload.Port) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + + case "forwarded-streamlocal@openssh.com": + var payload forwardedStreamLocalPayload + if err = Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error()) + continue + } + laddr = &net.UnixAddr{ + Name: payload.SocketPath, + Net: "unix", + } + raddr = &net.UnixAddr{ + Name: "@", + Net: "unix", + } + default: + panic(fmt.Errorf("ssh: unknown channel type %s", channelType)) + } + if ok := l.forward(laddr, raddr, ch); !ok { + // Section 7.2, implementations MUST reject spurious incoming + // connections. + ch.Reject(Prohibited, "no forward for address") + continue + } + + } +} + +// remove removes the forward entry, and the channel feeding its +// listener. +func (l *forwardList) remove(addr net.Addr) { + l.Lock() + defer l.Unlock() + for i, f := range l.entries { + if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() { + l.entries = append(l.entries[:i], l.entries[i+1:]...) + close(f.c) + return + } + } +} + +// closeAll closes and clears all forwards. +func (l *forwardList) closeAll() { + l.Lock() + defer l.Unlock() + for _, f := range l.entries { + close(f.c) + } + l.entries = nil +} + +func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool { + l.Lock() + defer l.Unlock() + for _, f := range l.entries { + if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() { + f.c <- forward{newCh: ch, raddr: raddr} + return true + } + } + return false +} + +type tcpListener struct { + laddr *net.TCPAddr + + conn *Client + in <-chan forward +} + +// Accept waits for and returns the next connection to the listener. +func (l *tcpListener) Accept() (net.Conn, error) { + s, ok := <-l.in + if !ok { + return nil, io.EOF + } + ch, incoming, err := s.newCh.Accept() + if err != nil { + return nil, err + } + go DiscardRequests(incoming) + + return &chanConn{ + Channel: ch, + laddr: l.laddr, + raddr: s.raddr, + }, nil +} + +// Close closes the listener. +func (l *tcpListener) Close() error { + m := channelForwardMsg{ + l.laddr.IP.String(), + uint32(l.laddr.Port), + } + + // this also closes the listener. + l.conn.forwards.remove(l.laddr) + ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) + if err == nil && !ok { + err = errors.New("ssh: cancel-tcpip-forward failed") + } + return err +} + +// Addr returns the listener's network address. +func (l *tcpListener) Addr() net.Addr { + return l.laddr +} + +// Dial initiates a connection to the addr from the remote host. +// The resulting connection has a zero LocalAddr() and RemoteAddr(). +func (c *Client) Dial(n, addr string) (net.Conn, error) { + var ch Channel + switch n { + case "tcp", "tcp4", "tcp6": + // Parse the address into host and numeric port. + host, portString, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + port, err := strconv.ParseUint(portString, 10, 16) + if err != nil { + return nil, err + } + ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port)) + if err != nil { + return nil, err + } + // Use a zero address for local and remote address. + zeroAddr := &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + return &chanConn{ + Channel: ch, + laddr: zeroAddr, + raddr: zeroAddr, + }, nil + case "unix": + var err error + ch, err = c.dialStreamLocal(addr) + if err != nil { + return nil, err + } + return &chanConn{ + Channel: ch, + laddr: &net.UnixAddr{ + Name: "@", + Net: "unix", + }, + raddr: &net.UnixAddr{ + Name: addr, + Net: "unix", + }, + }, nil + default: + return nil, fmt.Errorf("ssh: unsupported protocol: %s", n) + } +} + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { + if laddr == nil { + laddr = &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + } + ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) + if err != nil { + return nil, err + } + return &chanConn{ + Channel: ch, + laddr: laddr, + raddr: raddr, + }, nil +} + +// RFC 4254 7.2 +type channelOpenDirectMsg struct { + raddr string + rport uint32 + laddr string + lport uint32 +} + +func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) { + msg := channelOpenDirectMsg{ + raddr: raddr, + rport: uint32(rport), + laddr: laddr, + lport: uint32(lport), + } + ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg)) + if err != nil { + return nil, err + } + go DiscardRequests(in) + return ch, err +} + +type tcpChan struct { + Channel // the backing channel +} + +// chanConn fulfills the net.Conn interface without +// the tcpChan having to hold laddr or raddr directly. +type chanConn struct { + Channel + laddr, raddr net.Addr +} + +// LocalAddr returns the local network address. +func (t *chanConn) LocalAddr() net.Addr { + return t.laddr +} + +// RemoteAddr returns the remote network address. +func (t *chanConn) RemoteAddr() net.Addr { + return t.raddr +} + +// SetDeadline sets the read and write deadlines associated +// with the connection. +func (t *chanConn) SetDeadline(deadline time.Time) error { + if err := t.SetReadDeadline(deadline); err != nil { + return err + } + return t.SetWriteDeadline(deadline) +} + +// SetReadDeadline sets the read deadline. +// A zero value for t means Read will not time out. +// After the deadline, the error from Read will implement net.Error +// with Timeout() == true. +func (t *chanConn) SetReadDeadline(deadline time.Time) error { + // for compatibility with previous version, + // the error message contains "tcpChan" + return errors.New("ssh: tcpChan: deadline not supported") +} + +// SetWriteDeadline exists to satisfy the net.Conn interface +// but is not implemented by this type. It always returns an error. +func (t *chanConn) SetWriteDeadline(deadline time.Time) error { + return errors.New("ssh: tcpChan: deadline not supported") +} diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go new file mode 100644 index 00000000000..49ddc2e7de4 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -0,0 +1,353 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bufio" + "bytes" + "errors" + "io" + "log" +) + +// debugTransport if set, will print packet types as they go over the +// wire. No message decoding is done, to minimize the impact on timing. +const debugTransport = false + +const ( + gcmCipherID = "aes128-gcm@openssh.com" + aes128cbcID = "aes128-cbc" + tripledescbcID = "3des-cbc" +) + +// packetConn represents a transport that implements packet based +// operations. +type packetConn interface { + // Encrypt and send a packet of data to the remote peer. + writePacket(packet []byte) error + + // Read a packet from the connection. The read is blocking, + // i.e. if error is nil, then the returned byte slice is + // always non-empty. + readPacket() ([]byte, error) + + // Close closes the write-side of the connection. + Close() error +} + +// transport is the keyingTransport that implements the SSH packet +// protocol. +type transport struct { + reader connectionState + writer connectionState + + bufReader *bufio.Reader + bufWriter *bufio.Writer + rand io.Reader + isClient bool + io.Closer +} + +// packetCipher represents a combination of SSH encryption/MAC +// protocol. A single instance should be used for one direction only. +type packetCipher interface { + // writeCipherPacket encrypts the packet and writes it to w. The + // contents of the packet are generally scrambled. + writeCipherPacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error + + // readCipherPacket reads and decrypts a packet of data. The + // returned packet may be overwritten by future calls of + // readPacket. + readCipherPacket(seqnum uint32, r io.Reader) ([]byte, error) +} + +// connectionState represents one side (read or write) of the +// connection. This is necessary because each direction has its own +// keys, and can even have its own algorithms +type connectionState struct { + packetCipher + seqNum uint32 + dir direction + pendingKeyChange chan packetCipher +} + +// prepareKeyChange sets up key material for a keychange. The key changes in +// both directions are triggered by reading and writing a msgNewKey packet +// respectively. +func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { + ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult) + if err != nil { + return err + } + t.reader.pendingKeyChange <- ciph + + ciph, err = newPacketCipher(t.writer.dir, algs.w, kexResult) + if err != nil { + return err + } + t.writer.pendingKeyChange <- ciph + + return nil +} + +func (t *transport) printPacket(p []byte, write bool) { + if len(p) == 0 { + return + } + who := "server" + if t.isClient { + who = "client" + } + what := "read" + if write { + what = "write" + } + + log.Println(what, who, p[0]) +} + +// Read and decrypt next packet. +func (t *transport) readPacket() (p []byte, err error) { + for { + p, err = t.reader.readPacket(t.bufReader) + if err != nil { + break + } + if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { + break + } + } + if debugTransport { + t.printPacket(p, false) + } + + return p, err +} + +func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { + packet, err := s.packetCipher.readCipherPacket(s.seqNum, r) + s.seqNum++ + if err == nil && len(packet) == 0 { + err = errors.New("ssh: zero length packet") + } + + if len(packet) > 0 { + switch packet[0] { + case msgNewKeys: + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher + default: + return nil, errors.New("ssh: got bogus newkeys message") + } + + case msgDisconnect: + // Transform a disconnect message into an + // error. Since this is lowest level at which + // we interpret message types, doing it here + // ensures that we don't have to handle it + // elsewhere. + var msg disconnectMsg + if err := Unmarshal(packet, &msg); err != nil { + return nil, err + } + return nil, &msg + } + } + + // The packet may point to an internal buffer, so copy the + // packet out here. + fresh := make([]byte, len(packet)) + copy(fresh, packet) + + return fresh, err +} + +func (t *transport) writePacket(packet []byte) error { + if debugTransport { + t.printPacket(packet, true) + } + return t.writer.writePacket(t.bufWriter, t.rand, packet) +} + +func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { + changeKeys := len(packet) > 0 && packet[0] == msgNewKeys + + err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet) + if err != nil { + return err + } + if err = w.Flush(); err != nil { + return err + } + s.seqNum++ + if changeKeys { + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher + default: + panic("ssh: no key material for msgNewKeys") + } + } + return err +} + +func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport { + t := &transport{ + bufReader: bufio.NewReader(rwc), + bufWriter: bufio.NewWriter(rwc), + rand: rand, + reader: connectionState{ + packetCipher: &streamPacketCipher{cipher: noneCipher{}}, + pendingKeyChange: make(chan packetCipher, 1), + }, + writer: connectionState{ + packetCipher: &streamPacketCipher{cipher: noneCipher{}}, + pendingKeyChange: make(chan packetCipher, 1), + }, + Closer: rwc, + } + t.isClient = isClient + + if isClient { + t.reader.dir = serverKeys + t.writer.dir = clientKeys + } else { + t.reader.dir = clientKeys + t.writer.dir = serverKeys + } + + return t +} + +type direction struct { + ivTag []byte + keyTag []byte + macKeyTag []byte +} + +var ( + serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} + clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} +) + +// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as +// described in RFC 4253, section 6.4. direction should either be serverKeys +// (to setup server->client keys) or clientKeys (for client->server keys). +func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { + cipherMode := cipherModes[algs.Cipher] + macMode := macModes[algs.MAC] + + iv := make([]byte, cipherMode.ivSize) + key := make([]byte, cipherMode.keySize) + macKey := make([]byte, macMode.keySize) + + generateKeyMaterial(iv, d.ivTag, kex) + generateKeyMaterial(key, d.keyTag, kex) + generateKeyMaterial(macKey, d.macKeyTag, kex) + + return cipherModes[algs.Cipher].create(key, iv, macKey, algs) +} + +// generateKeyMaterial fills out with key material generated from tag, K, H +// and sessionId, as specified in RFC 4253, section 7.2. +func generateKeyMaterial(out, tag []byte, r *kexResult) { + var digestsSoFar []byte + + h := r.Hash.New() + for len(out) > 0 { + h.Reset() + h.Write(r.K) + h.Write(r.H) + + if len(digestsSoFar) == 0 { + h.Write(tag) + h.Write(r.SessionID) + } else { + h.Write(digestsSoFar) + } + + digest := h.Sum(nil) + n := copy(out, digest) + out = out[n:] + if len(out) > 0 { + digestsSoFar = append(digestsSoFar, digest...) + } + } +} + +const packageVersion = "SSH-2.0-Go" + +// Sends and receives a version line. The versionLine string should +// be US ASCII, start with "SSH-2.0-", and should not include a +// newline. exchangeVersions returns the other side's version line. +func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { + // Contrary to the RFC, we do not ignore lines that don't + // start with "SSH-2.0-" to make the library usable with + // nonconforming servers. + for _, c := range versionLine { + // The spec disallows non US-ASCII chars, and + // specifically forbids null chars. + if c < 32 { + return nil, errors.New("ssh: junk character in version line") + } + } + if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { + return + } + + them, err = readVersion(rw) + return them, err +} + +// maxVersionStringBytes is the maximum number of bytes that we'll +// accept as a version string. RFC 4253 section 4.2 limits this at 255 +// chars +const maxVersionStringBytes = 255 + +// Read version string as specified by RFC 4253, section 4.2. +func readVersion(r io.Reader) ([]byte, error) { + versionString := make([]byte, 0, 64) + var ok bool + var buf [1]byte + + for length := 0; length < maxVersionStringBytes; length++ { + _, err := io.ReadFull(r, buf[:]) + if err != nil { + return nil, err + } + // The RFC says that the version should be terminated with \r\n + // but several SSH servers actually only send a \n. + if buf[0] == '\n' { + if !bytes.HasPrefix(versionString, []byte("SSH-")) { + // RFC 4253 says we need to ignore all version string lines + // except the one containing the SSH version (provided that + // all the lines do not exceed 255 bytes in total). + versionString = versionString[:0] + continue + } + ok = true + break + } + + // non ASCII chars are disallowed, but we are lenient, + // since Go doesn't use null-terminated strings. + + // The RFC allows a comment after a space, however, + // all of it (version and comments) goes into the + // session hash. + versionString = append(versionString, buf[0]) + } + + if !ok { + return nil, errors.New("ssh: overflow reading version string") + } + + // There might be a '\r' on the end which we should remove. + if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { + versionString = versionString[:len(versionString)-1] + } + return versionString, nil +} diff --git a/vendor/gopkg.in/jcmturner/goidentity.v3/.gitignore b/vendor/gopkg.in/jcmturner/goidentity.v3/.gitignore new file mode 100644 index 00000000000..a1338d68517 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/goidentity.v3/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/vendor/gopkg.in/jcmturner/goidentity.v3/LICENSE b/vendor/gopkg.in/jcmturner/goidentity.v3/LICENSE new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/goidentity.v3/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/gopkg.in/jcmturner/goidentity.v3/README.md b/vendor/gopkg.in/jcmturner/goidentity.v3/README.md new file mode 100644 index 00000000000..89b33ebb70b --- /dev/null +++ b/vendor/gopkg.in/jcmturner/goidentity.v3/README.md @@ -0,0 +1,13 @@ +# goidentity + +Standard interface to holding authenticated identities and their attributes. + +To get the package, execute: +``` +go get gopkg.in/jcmturner/goidentity.v3 +``` +To import this package, add the following line to your code: +```go +import "gopkg.in/jcmturner/goidentity.v3" + +``` \ No newline at end of file diff --git a/vendor/gopkg.in/jcmturner/goidentity.v3/authenticator.go b/vendor/gopkg.in/jcmturner/goidentity.v3/authenticator.go new file mode 100644 index 00000000000..42ec79b0617 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/goidentity.v3/authenticator.go @@ -0,0 +1,6 @@ +package goidentity + +type Authenticator interface { + Authenticate() (Identity, bool, error) + Mechanism() string // gives the name of the type of authentication mechanism +} diff --git a/vendor/gopkg.in/jcmturner/goidentity.v3/identity.go b/vendor/gopkg.in/jcmturner/goidentity.v3/identity.go new file mode 100644 index 00000000000..d36c23fe050 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/goidentity.v3/identity.go @@ -0,0 +1,32 @@ +package goidentity + +import "time" + +const ( + CTXKey = "jcmturner/goidentity" +) + +type Identity interface { + UserName() string + SetUserName(s string) + Domain() string + SetDomain(s string) + DisplayName() string + SetDisplayName(s string) + Human() bool + SetHuman(b bool) + AuthTime() time.Time + SetAuthTime(t time.Time) + AuthzAttributes() []string + AddAuthzAttribute(a string) + RemoveAuthzAttribute(a string) + Authenticated() bool + SetAuthenticated(b bool) + Authorized(a string) bool + SessionID() string + Expired() bool + Attributes() map[string]interface{} + SetAttribute(k string, v interface{}) + SetAttributes(map[string]interface{}) + RemoveAttribute(k string) +} diff --git a/vendor/gopkg.in/jcmturner/goidentity.v3/user.go b/vendor/gopkg.in/jcmturner/goidentity.v3/user.go new file mode 100644 index 00000000000..d79f140c9f4 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/goidentity.v3/user.go @@ -0,0 +1,154 @@ +package goidentity + +import ( + "github.com/hashicorp/go-uuid" + "time" +) + +type User struct { + authenticated bool + domain string + userName string + displayName string + email string + human bool + groupMembership map[string]bool + authTime time.Time + sessionID string + expiry time.Time + attributes map[string]interface{} +} + +func NewUser(username string) User { + uuid, err := uuid.GenerateUUID() + if err != nil { + uuid = "00unique-sess-ions-uuid-unavailable0" + } + return User{ + userName: username, + groupMembership: make(map[string]bool), + sessionID: uuid, + } +} + +func (u *User) UserName() string { + return u.userName +} + +func (u *User) SetUserName(s string) { + u.userName = s +} + +func (u *User) Domain() string { + return u.domain +} + +func (u *User) SetDomain(s string) { + u.domain = s +} + +func (u *User) DisplayName() string { + if u.displayName == "" { + return u.userName + } + return u.displayName +} + +func (u *User) SetDisplayName(s string) { + u.displayName = s +} + +func (u *User) Human() bool { + return u.human +} + +func (u *User) SetHuman(b bool) { + u.human = b +} + +func (u *User) AuthTime() time.Time { + return u.authTime +} + +func (u *User) SetAuthTime(t time.Time) { + u.authTime = t +} + +func (u *User) AuthzAttributes() []string { + s := make([]string, len(u.groupMembership)) + i := 0 + for a := range u.groupMembership { + s[i] = a + i++ + } + return s +} + +func (u *User) Authenticated() bool { + return u.authenticated +} + +func (u *User) SetAuthenticated(b bool) { + u.authenticated = b +} + +func (u *User) AddAuthzAttribute(a string) { + u.groupMembership[a] = true +} + +func (u *User) RemoveAuthzAttribute(a string) { + if _, ok := u.groupMembership[a]; !ok { + return + } + delete(u.groupMembership, a) +} + +func (u *User) EnableAuthzAttribute(a string) { + if enabled, ok := u.groupMembership[a]; ok && !enabled { + u.groupMembership[a] = true + } +} + +func (u *User) DisableAuthzAttribute(a string) { + if enabled, ok := u.groupMembership[a]; ok && enabled { + u.groupMembership[a] = false + } +} + +func (u *User) Authorized(a string) bool { + if enabled, ok := u.groupMembership[a]; ok && enabled { + return true + } + return false +} + +func (u *User) SessionID() string { + return u.sessionID +} + +func (u *User) SetExpiry(t time.Time) { + u.expiry = t +} + +func (u *User) Expired() bool { + if !u.expiry.IsZero() && time.Now().UTC().After(u.expiry) { + return true + } + return false +} + +func (u *User) Attributes() map[string]interface{} { + return u.attributes +} + +func (u *User) SetAttribute(k string, v interface{}) { + u.attributes[k] = v +} + +func (u *User) SetAttributes(a map[string]interface{}) { + u.attributes = a +} + +func (u *User) RemoveAttribute(k string) { + delete(u.attributes, k) +} diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/service/APExchange.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/APExchange.go new file mode 100644 index 00000000000..4126cfa1582 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/APExchange.go @@ -0,0 +1,62 @@ +package service + +import ( + "time" + + "gopkg.in/jcmturner/gokrb5.v7/credentials" + "gopkg.in/jcmturner/gokrb5.v7/iana/errorcode" + "gopkg.in/jcmturner/gokrb5.v7/messages" +) + +// VerifyAPREQ verifies an AP_REQ sent to the service. Returns a boolean for if the AP_REQ is valid and the client's principal name and realm. +func VerifyAPREQ(APReq messages.APReq, s *Settings) (bool, *credentials.Credentials, error) { + var creds *credentials.Credentials + + ok, err := APReq.Verify(s.Keytab, s.MaxClockSkew(), s.ClientAddress()) + if err != nil || !ok { + return false, creds, err + } + + if s.RequireHostAddr() && len(APReq.Ticket.DecryptedEncPart.CAddr) < 1 { + return false, creds, + messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "ticket does not contain HostAddress values required") + } + + // Check for replay + rc := GetReplayCache(s.MaxClockSkew()) + if rc.IsReplay(APReq.Ticket.SName, APReq.Authenticator) { + return false, creds, + messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_REPEAT, "replay detected") + } + + c := credentials.NewFromPrincipalName(APReq.Authenticator.CName, APReq.Authenticator.CRealm) + creds = c + creds.SetAuthTime(time.Now().UTC()) + creds.SetAuthenticated(true) + creds.SetValidUntil(APReq.Ticket.DecryptedEncPart.EndTime) + + //PAC decoding + if !s.disablePACDecoding { + isPAC, pac, err := APReq.Ticket.GetPACType(s.Keytab, s.KeytabPrincipal(), s.Logger()) + if isPAC && err != nil { + return false, creds, err + } + if isPAC { + // There is a valid PAC. Adding attributes to creds + creds.SetADCredentials(credentials.ADCredentials{ + GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(), + LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(), + LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(), + PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(), + EffectiveName: pac.KerbValidationInfo.EffectiveName.Value, + FullName: pac.KerbValidationInfo.FullName.Value, + UserID: int(pac.KerbValidationInfo.UserID), + PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID), + LogonServer: pac.KerbValidationInfo.LogonServer.Value, + LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value, + LogonDomainID: pac.KerbValidationInfo.LogonDomainID.String(), + }) + } + } + return true, creds, nil +} diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/service/authenticator.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/authenticator.go new file mode 100644 index 00000000000..d60d259ad36 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/authenticator.go @@ -0,0 +1,118 @@ +package service + +import ( + "encoding/base64" + "fmt" + "strings" + "time" + + goidentity "gopkg.in/jcmturner/goidentity.v3" + "gopkg.in/jcmturner/gokrb5.v7/client" + "gopkg.in/jcmturner/gokrb5.v7/config" + "gopkg.in/jcmturner/gokrb5.v7/credentials" +) + +// NewKRB5BasicAuthenticator creates a new NewKRB5BasicAuthenticator +func NewKRB5BasicAuthenticator(headerVal string, krb5conf *config.Config, serviceSettings *Settings, clientSettings *client.Settings) KRB5BasicAuthenticator { + return KRB5BasicAuthenticator{ + BasicHeaderValue: headerVal, + clientConfig: krb5conf, + serviceSettings: serviceSettings, + clientSettings: clientSettings, + } +} + +// KRB5BasicAuthenticator implements gopkg.in/jcmturner/goidentity.v3.Authenticator interface. +// It takes username and password so can be used for basic authentication. +type KRB5BasicAuthenticator struct { + BasicHeaderValue string + serviceSettings *Settings + clientSettings *client.Settings + clientConfig *config.Config + realm string + username string + password string +} + +// Authenticate and return the identity. The boolean indicates if the authentication was successful. +func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) { + a.realm, a.username, a.password, err = parseBasicHeaderValue(a.BasicHeaderValue) + if err != nil { + err = fmt.Errorf("could not parse basic authentication header: %v", err) + return + } + cl := client.NewClientWithPassword(a.username, a.realm, a.password, a.clientConfig) + err = cl.Login() + if err != nil { + // Username and/or password could be wrong + err = fmt.Errorf("error with user credentials during login: %v", err) + return + } + tkt, _, err := cl.GetServiceTicket(a.serviceSettings.SName()) + if err != nil { + err = fmt.Errorf("could not get service ticket: %v", err) + return + } + err = tkt.DecryptEncPart(a.serviceSettings.Keytab, a.serviceSettings.KeytabPrincipal()) + if err != nil { + err = fmt.Errorf("could not decrypt service ticket: %v", err) + return + } + cl.Credentials.SetAuthTime(time.Now().UTC()) + cl.Credentials.SetAuthenticated(true) + isPAC, pac, err := tkt.GetPACType(a.serviceSettings.Keytab, a.serviceSettings.KeytabPrincipal(), a.serviceSettings.Logger()) + if isPAC && err != nil { + err = fmt.Errorf("error processing PAC: %v", err) + return + } + if isPAC { + // There is a valid PAC. Adding attributes to creds + cl.Credentials.SetADCredentials(credentials.ADCredentials{ + GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(), + LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(), + LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(), + PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(), + EffectiveName: pac.KerbValidationInfo.EffectiveName.Value, + FullName: pac.KerbValidationInfo.FullName.Value, + UserID: int(pac.KerbValidationInfo.UserID), + PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID), + LogonServer: pac.KerbValidationInfo.LogonServer.Value, + LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value, + LogonDomainID: pac.KerbValidationInfo.LogonDomainID.String(), + }) + } + ok = true + i = cl.Credentials + return +} + +// Mechanism returns the authentication mechanism. +func (a KRB5BasicAuthenticator) Mechanism() string { + return "Kerberos Basic" +} + +func parseBasicHeaderValue(s string) (domain, username, password string, err error) { + b, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return + } + v := string(b) + vc := strings.SplitN(v, ":", 2) + password = vc[1] + // Domain and username can be specified in 2 formats: + // - no domain specified + // \ + // @ + if strings.Contains(vc[0], `\`) { + u := strings.SplitN(vc[0], `\`, 2) + domain = u[0] + username = u[1] + } else if strings.Contains(vc[0], `@`) { + u := strings.SplitN(vc[0], `@`, 2) + domain = u[1] + username = u[0] + } else { + username = vc[0] + } + return +} diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/service/cache.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/cache.go new file mode 100644 index 00000000000..c844749362d --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/cache.go @@ -0,0 +1,148 @@ +// Package service provides server side integrations for Kerberos authentication. +package service + +import ( + "gopkg.in/jcmturner/gokrb5.v7/types" + "sync" + "time" +) + +/*The server MUST utilize a replay cache to remember any authenticator +presented within the allowable clock skew. +The replay cache will store at least the server name, along with the +client name, time, and microsecond fields from the recently-seen +authenticators, and if a matching tuple is found, the +KRB_AP_ERR_REPEAT error is returned. Note that the rejection here is +restricted to authenticators from the same principal to the same +server. Other client principals communicating with the same server +principal should not have their authenticators rejected if the time +and microsecond fields happen to match some other client's +authenticator. + +If a server loses track of authenticators presented within the +allowable clock skew, it MUST reject all requests until the clock +skew interval has passed, providing assurance that any lost or +replayed authenticators will fall outside the allowable clock skew +and can no longer be successfully replayed. If this were not done, +an attacker could subvert the authentication by recording the ticket +and authenticator sent over the network to a server and replaying +them following an event that caused the server to lose track of +recently seen authenticators.*/ + +// Cache for tickets received from clients keyed by fully qualified client name. Used to track replay of tickets. +type Cache struct { + entries map[string]clientEntries + mux sync.RWMutex +} + +// clientEntries holds entries of client details sent to the service. +type clientEntries struct { + replayMap map[time.Time]replayCacheEntry + seqNumber int64 + subKey types.EncryptionKey +} + +// Cache entry tracking client time values of tickets sent to the service. +type replayCacheEntry struct { + presentedTime time.Time + sName types.PrincipalName + cTime time.Time // This combines the ticket's CTime and Cusec +} + +func (c *Cache) getClientEntries(cname types.PrincipalName) (clientEntries, bool) { + c.mux.RLock() + defer c.mux.RUnlock() + ce, ok := c.entries[cname.PrincipalNameString()] + return ce, ok +} + +func (c *Cache) getClientEntry(cname types.PrincipalName, t time.Time) (replayCacheEntry, bool) { + if ce, ok := c.getClientEntries(cname); ok { + c.mux.RLock() + defer c.mux.RUnlock() + if e, ok := ce.replayMap[t]; ok { + return e, true + } + } + return replayCacheEntry{}, false +} + +// Instance of the ServiceCache. This needs to be a singleton. +var replayCache Cache +var once sync.Once + +// GetReplayCache returns a pointer to the Cache singleton. +func GetReplayCache(d time.Duration) *Cache { + // Create a singleton of the ReplayCache and start a background thread to regularly clean out old entries + once.Do(func() { + replayCache = Cache{ + entries: make(map[string]clientEntries), + } + go func() { + for { + // TODO consider using a context here. + time.Sleep(d) + replayCache.ClearOldEntries(d) + } + }() + }) + return &replayCache +} + +// AddEntry adds an entry to the Cache. +func (c *Cache) AddEntry(sname types.PrincipalName, a types.Authenticator) { + ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond) + if ce, ok := c.getClientEntries(a.CName); ok { + c.mux.Lock() + defer c.mux.Unlock() + ce.replayMap[ct] = replayCacheEntry{ + presentedTime: time.Now().UTC(), + sName: sname, + cTime: ct, + } + ce.seqNumber = a.SeqNumber + ce.subKey = a.SubKey + } else { + c.mux.Lock() + defer c.mux.Unlock() + c.entries[a.CName.PrincipalNameString()] = clientEntries{ + replayMap: map[time.Time]replayCacheEntry{ + ct: { + presentedTime: time.Now().UTC(), + sName: sname, + cTime: ct, + }, + }, + seqNumber: a.SeqNumber, + subKey: a.SubKey, + } + } +} + +// ClearOldEntries clears entries from the Cache that are older than the duration provided. +func (c *Cache) ClearOldEntries(d time.Duration) { + c.mux.Lock() + defer c.mux.Unlock() + for ke, ce := range c.entries { + for k, e := range ce.replayMap { + if time.Now().UTC().Sub(e.presentedTime) > d { + delete(ce.replayMap, k) + } + } + if len(ce.replayMap) == 0 { + delete(c.entries, ke) + } + } +} + +// IsReplay tests if the Authenticator provided is a replay within the duration defined. If this is not a replay add the entry to the cache for tracking. +func (c *Cache) IsReplay(sname types.PrincipalName, a types.Authenticator) bool { + ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond) + if e, ok := c.getClientEntry(a.CName, ct); ok { + if e.sName.Equal(sname) { + return true + } + } + c.AddEntry(sname, a) + return false +} diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/service/settings.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/settings.go new file mode 100644 index 00000000000..6e373ced6ba --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/service/settings.go @@ -0,0 +1,136 @@ +package service + +import ( + "log" + "time" + + "gopkg.in/jcmturner/gokrb5.v7/keytab" + "gopkg.in/jcmturner/gokrb5.v7/types" +) + +// Settings defines service side configuration settings. +type Settings struct { + Keytab *keytab.Keytab + ktprinc *types.PrincipalName + sname string + requireHostAddr bool + disablePACDecoding bool + cAddr types.HostAddress + maxClockSkew time.Duration + logger *log.Logger +} + +// NewSettings creates a new service Settings. +func NewSettings(kt *keytab.Keytab, settings ...func(*Settings)) *Settings { + s := new(Settings) + s.Keytab = kt + for _, set := range settings { + set(s) + } + return s +} + +// RequireHostAddr used to configure service side to required host addresses to be specified in Kerberos tickets. +// +// s := NewSettings(kt, RequireHostAddr(true)) +func RequireHostAddr(b bool) func(*Settings) { + return func(s *Settings) { + s.requireHostAddr = b + } +} + +// RequireHostAddr indicates if the service should require the host address to be included in the ticket. +func (s *Settings) RequireHostAddr() bool { + return s.requireHostAddr +} + +// DecodePAC used to configure service side to enable/disable PAC decoding if the PAC is present. +// Defaults to enabled if not specified. +// +// s := NewSettings(kt, DecodePAC(false)) +func DecodePAC(b bool) func(*Settings) { + return func(s *Settings) { + s.disablePACDecoding = !b + } +} + +// DecodePAC indicates whether the service should decode any PAC information present in the ticket. +func (s *Settings) DecodePAC() bool { + return !s.disablePACDecoding +} + +// ClientAddress used to configure service side with the clients host address to be used during validation. +// +// s := NewSettings(kt, ClientAddress(h)) +func ClientAddress(h types.HostAddress) func(*Settings) { + return func(s *Settings) { + s.cAddr = h + } +} + +// ClientAddress returns the client host address which has been provided to the service. +func (s *Settings) ClientAddress() types.HostAddress { + return s.cAddr +} + +// Logger used to configure service side with a logger. +// +// s := NewSettings(kt, Logger(l)) +func Logger(l *log.Logger) func(*Settings) { + return func(s *Settings) { + s.logger = l + } +} + +// Logger returns the logger instances configured for the service. If none is configured nill will be returned. +func (s *Settings) Logger() *log.Logger { + return s.logger +} + +// KeytabPrincipal used to override the principal name used to find the key in the keytab. +// +// s := NewSettings(kt, KeytabPrincipal("someaccount")) +func KeytabPrincipal(p string) func(*Settings) { + return func(s *Settings) { + pn, _ := types.ParseSPNString(p) + s.ktprinc = &pn + } +} + +// KeytabPrincipal returns the principal name used to find the key in the keytab if it has been overridden. +func (s *Settings) KeytabPrincipal() *types.PrincipalName { + return s.ktprinc +} + +// MaxClockSkew used to configure service side with the maximum acceptable clock skew +// between the service and the issue time of kerberos tickets +// +// s := NewSettings(kt, MaxClockSkew(d)) +func MaxClockSkew(d time.Duration) func(*Settings) { + return func(s *Settings) { + s.maxClockSkew = d + } +} + +// MaxClockSkew returns the maximum acceptable clock skew between the service and the issue time of kerberos tickets. +// If none is defined a duration of 5 minutes is returned. +func (s *Settings) MaxClockSkew() time.Duration { + if s.maxClockSkew.Nanoseconds() == 0 { + return time.Duration(5) * time.Minute + } + return s.maxClockSkew +} + +// SName used provide a specific service name to the service settings. +// +// s := NewSettings(kt, SName("HTTP/some.service.com")) +func SName(sname string) func(*Settings) { + return func(s *Settings) { + s.sname = sname + } +} + +// SName returns the specific service name to the service. +func (s *Settings) SName() string { + return s.sname +} diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/http.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/http.go new file mode 100644 index 00000000000..0cb28449c5d --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/http.go @@ -0,0 +1,293 @@ +package spnego + +import ( + "bytes" + "context" + "encoding/base64" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/cookiejar" + "net/url" + "strings" + + "gopkg.in/jcmturner/goidentity.v3" + "gopkg.in/jcmturner/gokrb5.v7/client" + "gopkg.in/jcmturner/gokrb5.v7/gssapi" + "gopkg.in/jcmturner/gokrb5.v7/keytab" + "gopkg.in/jcmturner/gokrb5.v7/krberror" + "gopkg.in/jcmturner/gokrb5.v7/service" + "gopkg.in/jcmturner/gokrb5.v7/types" +) + +// Client side functionality // + +// Client will negotiate authentication with a server using SPNEGO. +type Client struct { + *http.Client + krb5Client *client.Client + spn string + reqs []*http.Request +} + +type redirectErr struct { + reqTarget *http.Request +} + +func (e redirectErr) Error() string { + return fmt.Sprintf("redirect to %v", e.reqTarget.URL) +} + +type teeReadCloser struct { + io.Reader + io.Closer +} + +// NewClient returns an SPNEGO enabled HTTP client. +func NewClient(krb5Cl *client.Client, httpCl *http.Client, spn string) *Client { + if httpCl == nil { + httpCl = http.DefaultClient + } + // Add a cookie jar if there isn't one + if httpCl.Jar == nil { + httpCl.Jar, _ = cookiejar.New(nil) + } + // Add a CheckRedirect function that will execute any functional already defined and then error with a redirectErr + f := httpCl.CheckRedirect + httpCl.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if f != nil { + err := f(req, via) + if err != nil { + return err + } + } + return redirectErr{reqTarget: req} + } + return &Client{ + Client: httpCl, + krb5Client: krb5Cl, + spn: spn, + } +} + +// Do is the SPNEGO enabled HTTP client's equivalent of the http.Client's Do method. +func (c *Client) Do(req *http.Request) (resp *http.Response, err error) { + var body bytes.Buffer + if req.Body != nil { + // Use a tee reader to capture any body sent in case we have to replay it again + teeR := io.TeeReader(req.Body, &body) + teeRC := teeReadCloser{teeR, req.Body} + req.Body = teeRC + } + resp, err = c.Client.Do(req) + if err != nil { + if ue, ok := err.(*url.Error); ok { + if e, ok := ue.Err.(redirectErr); ok { + // Picked up a redirect + e.reqTarget.Header.Del(HTTPHeaderAuthRequest) + c.reqs = append(c.reqs, e.reqTarget) + if len(c.reqs) >= 10 { + return resp, errors.New("stopped after 10 redirects") + } + if req.Body != nil { + // Refresh the body reader so the body can be sent again + e.reqTarget.Body = ioutil.NopCloser(&body) + } + return c.Do(e.reqTarget) + } + } + return resp, err + } + if respUnauthorizedNegotiate(resp) { + err := SetSPNEGOHeader(c.krb5Client, req, c.spn) + if err != nil { + return resp, err + } + if req.Body != nil { + // Refresh the body reader so the body can be sent again + req.Body = ioutil.NopCloser(&body) + } + return c.Do(req) + } + return resp, err +} + +// Get is the SPNEGO enabled HTTP client's equivalent of the http.Client's Get method. +func (c *Client) Get(url string) (resp *http.Response, err error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +// Post is the SPNEGO enabled HTTP client's equivalent of the http.Client's Post method. +func (c *Client) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) { + req, err := http.NewRequest("POST", url, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", contentType) + return c.Do(req) +} + +// PostForm is the SPNEGO enabled HTTP client's equivalent of the http.Client's PostForm method. +func (c *Client) PostForm(url string, data url.Values) (resp *http.Response, err error) { + return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) +} + +// Head is the SPNEGO enabled HTTP client's equivalent of the http.Client's Head method. +func (c *Client) Head(url string) (resp *http.Response, err error) { + req, err := http.NewRequest("HEAD", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +func respUnauthorizedNegotiate(resp *http.Response) bool { + if resp.StatusCode == http.StatusUnauthorized { + if resp.Header.Get(HTTPHeaderAuthResponse) == HTTPHeaderAuthResponseValueKey { + return true + } + } + return false +} + +// SetSPNEGOHeader gets the service ticket and sets it as the SPNEGO authorization header on HTTP request object. +// To auto generate the SPN from the request object pass a null string "". +func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error { + if spn == "" { + h := strings.TrimSuffix(strings.SplitN(r.URL.Host, ":", 2)[0], ".") + name, err := net.LookupCNAME(h) + if err == nil { + // Underlyng canonical name should be used for SPN + h = strings.TrimSuffix(name, ".") + } + spn = "HTTP/" + h + r.Host = h + } + cl.Log("using SPN %s", spn) + s := SPNEGOClient(cl, spn) + err := s.AcquireCred() + if err != nil { + return fmt.Errorf("could not acquire client credential: %v", err) + } + st, err := s.InitSecContext() + if err != nil { + return fmt.Errorf("could not initialize context: %v", err) + } + nb, err := st.Marshal() + if err != nil { + return krberror.Errorf(err, krberror.EncodingError, "could not marshal SPNEGO") + } + hs := "Negotiate " + base64.StdEncoding.EncodeToString(nb) + r.Header.Set(HTTPHeaderAuthRequest, hs) + return nil +} + +// Service side functionality // + +type ctxKey string + +const ( + // spnegoNegTokenRespKRBAcceptCompleted - The response on successful authentication always has this header. Capturing as const so we don't have marshaling and encoding overhead. + spnegoNegTokenRespKRBAcceptCompleted = "Negotiate oRQwEqADCgEAoQsGCSqGSIb3EgECAg==" + // spnegoNegTokenRespReject - The response on a failed authentication always has this rejection header. Capturing as const so we don't have marshaling and encoding overhead. + spnegoNegTokenRespReject = "Negotiate oQcwBaADCgEC" + // spnegoNegTokenRespIncompleteKRB5 - Response token specifying incomplete context and KRB5 as the supported mechtype. + spnegoNegTokenRespIncompleteKRB5 = "Negotiate oRQwEqADCgEBoQsGCSqGSIb3EgECAg==" + // CTXKeyAuthenticated is the request context key holding a boolean indicating if the request has been authenticated. + CTXKeyAuthenticated ctxKey = "github.com/jcmturner/gokrb5/CTXKeyAuthenticated" + // CTXKeyCredentials is the request context key holding the credentials gopkg.in/jcmturner/goidentity.v2/Identity object. + CTXKeyCredentials ctxKey = "github.com/jcmturner/gokrb5/CTXKeyCredentials" + // HTTPHeaderAuthRequest is the header that will hold authn/z information. + HTTPHeaderAuthRequest = "Authorization" + // HTTPHeaderAuthResponse is the header that will hold SPNEGO data from the server. + HTTPHeaderAuthResponse = "WWW-Authenticate" + // HTTPHeaderAuthResponseValueKey is the key in the auth header for SPNEGO. + HTTPHeaderAuthResponseValueKey = "Negotiate" + // UnauthorizedMsg is the message returned in the body when authentication fails. + UnauthorizedMsg = "Unauthorised.\n" +) + +// SPNEGOKRB5Authenticate is a Kerberos SPNEGO authentication HTTP handler wrapper. +func SPNEGOKRB5Authenticate(inner http.Handler, kt *keytab.Keytab, settings ...func(*service.Settings)) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Get the auth header + s := strings.SplitN(r.Header.Get(HTTPHeaderAuthRequest), " ", 2) + if len(s) != 2 || s[0] != HTTPHeaderAuthResponseValueKey { + // No Authorization header set so return 401 with WWW-Authenticate Negotiate header + w.Header().Set(HTTPHeaderAuthResponse, HTTPHeaderAuthResponseValueKey) + http.Error(w, UnauthorizedMsg, http.StatusUnauthorized) + return + } + + // Set up the SPNEGO GSS-API mechanism + var spnego *SPNEGO + h, err := types.GetHostAddress(r.RemoteAddr) + if err == nil { + // put in this order so that if the user provides a ClientAddress it will override the one here. + o := append([]func(*service.Settings){service.ClientAddress(h)}, settings...) + spnego = SPNEGOService(kt, o...) + } else { + spnego = SPNEGOService(kt, settings...) + spnego.Log("%s - SPNEGO could not parse client address: %v", r.RemoteAddr, err) + } + + // Decode the header into an SPNEGO context token + b, err := base64.StdEncoding.DecodeString(s[1]) + if err != nil { + spnegoNegotiateKRB5MechType(spnego, w, "%s - SPNEGO error in base64 decoding negotiation header: %v", r.RemoteAddr, err) + return + } + var st SPNEGOToken + err = st.Unmarshal(b) + if err != nil { + spnegoNegotiateKRB5MechType(spnego, w, "%s - SPNEGO error in unmarshaling SPNEGO token: %v", r.RemoteAddr, err) + return + } + + // Validate the context token + authed, ctx, status := spnego.AcceptSecContext(&st) + if status.Code != gssapi.StatusComplete && status.Code != gssapi.StatusContinueNeeded { + spnegoResponseReject(spnego, w, "%s - SPNEGO validation error: %v", r.RemoteAddr, status) + return + } + if status.Code == gssapi.StatusContinueNeeded { + spnegoNegotiateKRB5MechType(spnego, w, "%s - SPNEGO GSS-API continue needed", r.RemoteAddr) + return + } + if authed { + id := ctx.Value(CTXKeyCredentials).(goidentity.Identity) + requestCtx := r.Context() + requestCtx = context.WithValue(requestCtx, CTXKeyCredentials, id) + requestCtx = context.WithValue(requestCtx, CTXKeyAuthenticated, ctx.Value(CTXKeyAuthenticated)) + spnegoResponseAcceptCompleted(spnego, w, "%s %s@%s - SPNEGO authentication succeeded", r.RemoteAddr, id.UserName(), id.Domain()) + inner.ServeHTTP(w, r.WithContext(requestCtx)) + } else { + spnegoResponseReject(spnego, w, "%s - SPNEGO Kerberos authentication failed", r.RemoteAddr) + return + } + }) +} + +func spnegoNegotiateKRB5MechType(s *SPNEGO, w http.ResponseWriter, format string, v ...interface{}) { + s.Log(format, v...) + w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespIncompleteKRB5) + http.Error(w, UnauthorizedMsg, http.StatusUnauthorized) +} + +func spnegoResponseReject(s *SPNEGO, w http.ResponseWriter, format string, v ...interface{}) { + s.Log(format, v...) + w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespReject) + http.Error(w, UnauthorizedMsg, http.StatusUnauthorized) +} + +func spnegoResponseAcceptCompleted(s *SPNEGO, w http.ResponseWriter, format string, v ...interface{}) { + s.Log(format, v...) + w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespKRBAcceptCompleted) +} diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/krb5Token.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/krb5Token.go new file mode 100644 index 00000000000..8d82df2af39 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/krb5Token.go @@ -0,0 +1,236 @@ +package spnego + +import ( + "context" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + + "github.com/jcmturner/gofork/encoding/asn1" + "gopkg.in/jcmturner/gokrb5.v7/asn1tools" + "gopkg.in/jcmturner/gokrb5.v7/client" + "gopkg.in/jcmturner/gokrb5.v7/credentials" + "gopkg.in/jcmturner/gokrb5.v7/gssapi" + "gopkg.in/jcmturner/gokrb5.v7/iana/chksumtype" + "gopkg.in/jcmturner/gokrb5.v7/iana/msgtype" + "gopkg.in/jcmturner/gokrb5.v7/krberror" + "gopkg.in/jcmturner/gokrb5.v7/messages" + "gopkg.in/jcmturner/gokrb5.v7/service" + "gopkg.in/jcmturner/gokrb5.v7/types" +) + +// GSSAPI KRB5 MechToken IDs. +const ( + TOK_ID_KRB_AP_REQ = "0100" + TOK_ID_KRB_AP_REP = "0200" + TOK_ID_KRB_ERROR = "0300" +) + +// KRB5Token context token implementation for GSSAPI. +type KRB5Token struct { + OID asn1.ObjectIdentifier + tokID []byte + APReq messages.APReq + APRep messages.APRep + KRBError messages.KRBError + settings *service.Settings + context context.Context +} + +// Marshal a KRB5Token into a slice of bytes. +func (m *KRB5Token) Marshal() ([]byte, error) { + // Create the header + b, _ := asn1.Marshal(m.OID) + b = append(b, m.tokID...) + var tb []byte + var err error + switch hex.EncodeToString(m.tokID) { + case TOK_ID_KRB_AP_REQ: + tb, err = m.APReq.Marshal() + if err != nil { + return []byte{}, fmt.Errorf("error marshalling AP_REQ for MechToken: %v", err) + } + case TOK_ID_KRB_AP_REP: + return []byte{}, errors.New("marshal of AP_REP GSSAPI MechToken not supported by gokrb5") + case TOK_ID_KRB_ERROR: + return []byte{}, errors.New("marshal of KRB_ERROR GSSAPI MechToken not supported by gokrb5") + } + if err != nil { + return []byte{}, fmt.Errorf("error mashalling kerberos message within mech token: %v", err) + } + b = append(b, tb...) + return asn1tools.AddASNAppTag(b, 0), nil +} + +// Unmarshal a KRB5Token. +func (m *KRB5Token) Unmarshal(b []byte) error { + var oid asn1.ObjectIdentifier + r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0)) + if err != nil { + return fmt.Errorf("error unmarshalling KRB5Token OID: %v", err) + } + m.OID = oid + m.tokID = r[0:2] + switch hex.EncodeToString(m.tokID) { + case TOK_ID_KRB_AP_REQ: + var a messages.APReq + err = a.Unmarshal(r[2:]) + if err != nil { + return fmt.Errorf("error unmarshalling KRB5Token AP_REQ: %v", err) + } + m.APReq = a + case TOK_ID_KRB_AP_REP: + var a messages.APRep + err = a.Unmarshal(r[2:]) + if err != nil { + return fmt.Errorf("error unmarshalling KRB5Token AP_REP: %v", err) + } + m.APRep = a + case TOK_ID_KRB_ERROR: + var a messages.KRBError + err = a.Unmarshal(r[2:]) + if err != nil { + return fmt.Errorf("error unmarshalling KRB5Token KRBError: %v", err) + } + m.KRBError = a + } + return nil +} + +// Verify a KRB5Token. +func (m *KRB5Token) Verify() (bool, gssapi.Status) { + switch hex.EncodeToString(m.tokID) { + case TOK_ID_KRB_AP_REQ: + ok, creds, err := service.VerifyAPREQ(m.APReq, m.settings) + if err != nil { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()} + } + if !ok { + return false, gssapi.Status{Code: gssapi.StatusDefectiveCredential, Message: "KRB5_AP_REQ token not valid"} + } + m.context = context.Background() + m.context = context.WithValue(m.context, CTXKeyCredentials, creds) + m.context = context.WithValue(m.context, CTXKeyAuthenticated, ok) + return true, gssapi.Status{Code: gssapi.StatusComplete} + case TOK_ID_KRB_AP_REP: + // Client side + // TODO how to verify the AP_REP - not yet implemented + return false, gssapi.Status{Code: gssapi.StatusFailure, Message: "verifying an AP_REP is not currently supported by gokrb5"} + case TOK_ID_KRB_ERROR: + if m.KRBError.MsgType != msgtype.KRB_ERROR { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "KRB5_Error token not valid"} + } + return true, gssapi.Status{Code: gssapi.StatusUnavailable} + } + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "unknown TOK_ID in KRB5 token"} +} + +// IsAPReq tests if the MechToken contains an AP_REQ. +func (m *KRB5Token) IsAPReq() bool { + if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REQ { + return true + } + return false +} + +// IsAPRep tests if the MechToken contains an AP_REP. +func (m *KRB5Token) IsAPRep() bool { + if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REP { + return true + } + return false +} + +// IsKRBError tests if the MechToken contains an KRB_ERROR. +func (m *KRB5Token) IsKRBError() bool { + if hex.EncodeToString(m.tokID) == TOK_ID_KRB_ERROR { + return true + } + return false +} + +// Context returns the KRB5 token's context which will contain any verify user identity information. +func (m *KRB5Token) Context() context.Context { + return m.context +} + +// NewKRB5TokenAPREQ creates a new KRB5 token with AP_REQ +func NewKRB5TokenAPREQ(cl *client.Client, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (KRB5Token, error) { + // TODO consider providing the SPN rather than the specific tkt and key and get these from the krb client. + var m KRB5Token + m.OID = gssapi.OID(gssapi.OIDKRB5) + tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ) + m.tokID = tb + + auth, err := krb5TokenAuthenticator(cl.Credentials, GSSAPIFlags) + if err != nil { + return m, err + } + APReq, err := messages.NewAPReq( + tkt, + sessionKey, + auth, + ) + if err != nil { + return m, err + } + for _, o := range APOptions { + types.SetFlag(&APReq.APOptions, o) + } + m.APReq = APReq + return m, nil +} + +// krb5TokenAuthenticator creates a new kerberos authenticator for kerberos MechToken +func krb5TokenAuthenticator(creds *credentials.Credentials, flags []int) (types.Authenticator, error) { + //RFC 4121 Section 4.1.1 + auth, err := types.NewAuthenticator(creds.Domain(), creds.CName()) + if err != nil { + return auth, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator") + } + auth.Cksum = types.Checksum{ + CksumType: chksumtype.GSSAPI, + Checksum: newAuthenticatorChksum(flags), + } + return auth, nil +} + +// Create new authenticator checksum for kerberos MechToken +func newAuthenticatorChksum(flags []int) []byte { + a := make([]byte, 24) + binary.LittleEndian.PutUint32(a[:4], 16) + for _, i := range flags { + if i == gssapi.ContextFlagDeleg { + x := make([]byte, 28-len(a)) + a = append(a, x...) + } + f := binary.LittleEndian.Uint32(a[20:24]) + f |= uint32(i) + binary.LittleEndian.PutUint32(a[20:24], f) + } + return a +} + +/* +The authenticator checksum field SHALL have the following format: + +Octet Name Description +----------------------------------------------------------------- +0..3 Lgth Number of octets in Bnd field; Represented + in little-endian order; Currently contains + hex value 10 00 00 00 (16). +4..19 Bnd Channel binding information, as described in + section 4.1.1.2. +20..23 Flags Four-octet context-establishment flags in + little-endian order as described in section + 4.1.1.1. +24..25 DlgOpt The delegation option identifier (=1) in + little-endian order [optional]. This field + and the next two fields are present if and + only if GSS_C_DELEG_FLAG is set as described + in section 4.1.1.1. +26..27 Dlgth The length of the Deleg field in little-endian order [optional]. +28..(n-1) Deleg A KRB_CRED message (n = Dlgth + 28) [optional]. +n..last Exts Extensions [optional]. +*/ diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/negotiationToken.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/negotiationToken.go new file mode 100644 index 00000000000..4a80f3595e0 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/negotiationToken.go @@ -0,0 +1,338 @@ +package spnego + +import ( + "context" + "errors" + "fmt" + + "github.com/jcmturner/gofork/encoding/asn1" + "gopkg.in/jcmturner/gokrb5.v7/client" + "gopkg.in/jcmturner/gokrb5.v7/gssapi" + "gopkg.in/jcmturner/gokrb5.v7/messages" + "gopkg.in/jcmturner/gokrb5.v7/service" + "gopkg.in/jcmturner/gokrb5.v7/types" +) + +/* +https://msdn.microsoft.com/en-us/library/ms995330.aspx + +NegotiationToken ::= CHOICE { + negTokenInit [0] NegTokenInit, This is the Negotiation token sent from the client to the server. + negTokenResp [1] NegTokenResp +} + +NegTokenInit ::= SEQUENCE { + mechTypes [0] MechTypeList, + reqFlags [1] ContextFlags OPTIONAL, + -- inherited from RFC 2478 for backward compatibility, + -- RECOMMENDED to be left out + mechToken [2] OCTET STRING OPTIONAL, + mechListMIC [3] OCTET STRING OPTIONAL, + ... +} + +NegTokenResp ::= SEQUENCE { + negState [0] ENUMERATED { + accept-completed (0), + accept-incomplete (1), + reject (2), + request-mic (3) + } OPTIONAL, + -- REQUIRED in the first reply from the target + supportedMech [1] MechType OPTIONAL, + -- present only in the first reply from the target + responseToken [2] OCTET STRING OPTIONAL, + mechListMIC [3] OCTET STRING OPTIONAL, + ... +} +*/ + +// Negotiation state values. +const ( + NegStateAcceptCompleted NegState = 0 + NegStateAcceptIncomplete NegState = 1 + NegStateReject NegState = 2 + NegStateRequestMIC NegState = 3 +) + +// NegState is a type to indicate the SPNEGO negotiation state. +type NegState int + +// NegTokenInit implements Negotiation Token of type Init. +type NegTokenInit struct { + MechTypes []asn1.ObjectIdentifier + ReqFlags gssapi.ContextFlags + MechTokenBytes []byte + MechListMIC []byte + mechToken gssapi.ContextToken + settings *service.Settings +} + +type marshalNegTokenInit struct { + MechTypes []asn1.ObjectIdentifier `asn1:"explicit,tag:0"` + ReqFlags gssapi.ContextFlags `asn1:"explicit,optional,tag:1"` + MechTokenBytes []byte `asn1:"explicit,optional,omitempty,tag:2"` + MechListMIC []byte `asn1:"explicit,optional,omitempty,tag:3"` // This field is not used when negotiating Kerberos tokens +} + +// NegTokenResp implements Negotiation Token of type Resp/Targ +type NegTokenResp struct { + NegState asn1.Enumerated + SupportedMech asn1.ObjectIdentifier + ResponseToken []byte + MechListMIC []byte + mechToken gssapi.ContextToken + settings *service.Settings +} + +type marshalNegTokenResp struct { + NegState asn1.Enumerated `asn1:"explicit,tag:0"` + SupportedMech asn1.ObjectIdentifier `asn1:"explicit,optional,tag:1"` + ResponseToken []byte `asn1:"explicit,optional,omitempty,tag:2"` + MechListMIC []byte `asn1:"explicit,optional,omitempty,tag:3"` // This field is not used when negotiating Kerberos tokens +} + +// NegTokenTarg implements Negotiation Token of type Resp/Targ +type NegTokenTarg NegTokenResp + +// Marshal an Init negotiation token +func (n *NegTokenInit) Marshal() ([]byte, error) { + m := marshalNegTokenInit{ + MechTypes: n.MechTypes, + ReqFlags: n.ReqFlags, + MechTokenBytes: n.MechTokenBytes, + MechListMIC: n.MechListMIC, + } + b, err := asn1.Marshal(m) + if err != nil { + return nil, err + } + nt := asn1.RawValue{ + Tag: 0, + Class: 2, + IsCompound: true, + Bytes: b, + } + nb, err := asn1.Marshal(nt) + if err != nil { + return nil, err + } + return nb, nil +} + +// Unmarshal an Init negotiation token +func (n *NegTokenInit) Unmarshal(b []byte) error { + init, nt, err := UnmarshalNegToken(b) + if err != nil { + return err + } + if !init { + return errors.New("bytes were not that of a NegTokenInit") + } + nInit := nt.(NegTokenInit) + n.MechTokenBytes = nInit.MechTokenBytes + n.MechListMIC = nInit.MechListMIC + n.MechTypes = nInit.MechTypes + n.ReqFlags = nInit.ReqFlags + return nil +} + +// Verify an Init negotiation token +func (n *NegTokenInit) Verify() (bool, gssapi.Status) { + // Check if supported mechanisms are in the MechTypeList + var mtSupported bool + for _, m := range n.MechTypes { + if m.Equal(gssapi.OID(gssapi.OIDKRB5)) || m.Equal(gssapi.OID(gssapi.OIDMSLegacyKRB5)) { + if n.mechToken == nil && n.MechTokenBytes == nil { + return false, gssapi.Status{Code: gssapi.StatusContinueNeeded} + } + mtSupported = true + break + } + } + if !mtSupported { + return false, gssapi.Status{Code: gssapi.StatusBadMech, Message: "no supported mechanism specified in negotiation"} + } + // There should be some mechtoken bytes for a KRB5Token (other mech types are not supported) + mt := new(KRB5Token) + mt.settings = n.settings + if n.mechToken == nil { + err := mt.Unmarshal(n.MechTokenBytes) + if err != nil { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()} + } + n.mechToken = mt + } else { + var ok bool + mt, ok = n.mechToken.(*KRB5Token) + if !ok { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "MechToken is not a KRB5 token as expected"} + } + } + // RFC4178 states that the initial negotiation message can optionally contain the initial mechanism token for the preferred mechanism of the client. + if !mt.OID.Equal(n.MechTypes[0]) { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "OID of MechToken does not match the first in the MechTypeList"} + } + // Verify the mechtoken + return n.mechToken.Verify() +} + +// Context returns the SPNEGO context which will contain any verify user identity information. +func (n *NegTokenInit) Context() context.Context { + if n.mechToken != nil { + mt, ok := n.mechToken.(*KRB5Token) + if !ok { + return nil + } + return mt.Context() + } + return nil +} + +// Marshal a Resp/Targ negotiation token +func (n *NegTokenResp) Marshal() ([]byte, error) { + m := marshalNegTokenResp{ + NegState: n.NegState, + SupportedMech: n.SupportedMech, + ResponseToken: n.ResponseToken, + MechListMIC: n.MechListMIC, + } + b, err := asn1.Marshal(m) + if err != nil { + return nil, err + } + nt := asn1.RawValue{ + Tag: 1, + Class: 2, + IsCompound: true, + Bytes: b, + } + nb, err := asn1.Marshal(nt) + if err != nil { + return nil, err + } + return nb, nil +} + +// Unmarshal a Resp/Targ negotiation token +func (n *NegTokenResp) Unmarshal(b []byte) error { + init, nt, err := UnmarshalNegToken(b) + if err != nil { + return err + } + if init { + return errors.New("bytes were not that of a NegTokenResp") + } + nResp := nt.(NegTokenResp) + n.MechListMIC = nResp.MechListMIC + n.NegState = nResp.NegState + n.ResponseToken = nResp.ResponseToken + n.SupportedMech = nResp.SupportedMech + return nil +} + +// Verify a Resp/Targ negotiation token +func (n *NegTokenResp) Verify() (bool, gssapi.Status) { + if n.SupportedMech.Equal(gssapi.OID(gssapi.OIDKRB5)) || n.SupportedMech.Equal(gssapi.OID(gssapi.OIDMSLegacyKRB5)) { + if n.mechToken == nil && n.ResponseToken == nil { + return false, gssapi.Status{Code: gssapi.StatusContinueNeeded} + } + mt := new(KRB5Token) + mt.settings = n.settings + if n.mechToken == nil { + err := mt.Unmarshal(n.ResponseToken) + if err != nil { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()} + } + n.mechToken = mt + } else { + var ok bool + mt, ok = n.mechToken.(*KRB5Token) + if !ok { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "MechToken is not a KRB5 token as expected"} + } + } + if mt == nil { + return false, gssapi.Status{Code: gssapi.StatusContinueNeeded} + } + // Verify the mechtoken + return mt.Verify() + } + return false, gssapi.Status{Code: gssapi.StatusBadMech, Message: "no supported mechanism specified in negotiation"} +} + +// State returns the negotiation state of the negotiation response. +func (n *NegTokenResp) State() NegState { + return NegState(n.NegState) +} + +// Context returns the SPNEGO context which will contain any verify user identity information. +func (n *NegTokenResp) Context() context.Context { + if n.mechToken != nil { + mt, ok := n.mechToken.(*KRB5Token) + if !ok { + return nil + } + return mt.Context() + } + return nil +} + +// UnmarshalNegToken umarshals and returns either a NegTokenInit or a NegTokenResp. +// +// The boolean indicates if the response is a NegTokenInit. +// If error is nil and the boolean is false the response is a NegTokenResp. +func UnmarshalNegToken(b []byte) (bool, interface{}, error) { + var a asn1.RawValue + _, err := asn1.Unmarshal(b, &a) + if err != nil { + return false, nil, fmt.Errorf("error unmarshalling NegotiationToken: %v", err) + } + switch a.Tag { + case 0: + var n marshalNegTokenInit + _, err = asn1.Unmarshal(a.Bytes, &n) + if err != nil { + return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Init): %v", a.Tag, err) + } + nt := NegTokenInit{ + MechTypes: n.MechTypes, + ReqFlags: n.ReqFlags, + MechTokenBytes: n.MechTokenBytes, + MechListMIC: n.MechListMIC, + } + return true, nt, nil + case 1: + var n marshalNegTokenResp + _, err = asn1.Unmarshal(a.Bytes, &n) + if err != nil { + return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Resp/Targ): %v", a.Tag, err) + } + nt := NegTokenResp{ + NegState: n.NegState, + SupportedMech: n.SupportedMech, + ResponseToken: n.ResponseToken, + MechListMIC: n.MechListMIC, + } + return false, nt, nil + default: + return false, nil, errors.New("unknown choice type for NegotiationToken") + } + +} + +// NewNegTokenInitKRB5 creates new Init negotiation token for Kerberos 5 +func NewNegTokenInitKRB5(cl *client.Client, tkt messages.Ticket, sessionKey types.EncryptionKey) (NegTokenInit, error) { + mt, err := NewKRB5TokenAPREQ(cl, tkt, sessionKey, []int{gssapi.ContextFlagInteg, gssapi.ContextFlagConf}, []int{}) + if err != nil { + return NegTokenInit{}, fmt.Errorf("error getting KRB5 token; %v", err) + } + mtb, err := mt.Marshal() + if err != nil { + return NegTokenInit{}, fmt.Errorf("error marshalling KRB5 token; %v", err) + } + return NegTokenInit{ + MechTypes: []asn1.ObjectIdentifier{gssapi.OID(gssapi.OIDKRB5)}, + MechTokenBytes: mtb, + }, nil +} diff --git a/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/spnego.go b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/spnego.go new file mode 100644 index 00000000000..f82947c7e13 --- /dev/null +++ b/vendor/gopkg.in/jcmturner/gokrb5.v7/spnego/spnego.go @@ -0,0 +1,199 @@ +// Package spnego implements the Simple and Protected GSSAPI Negotiation Mechanism for Kerberos authentication. +package spnego + +import ( + "context" + "errors" + "fmt" + + "github.com/jcmturner/gofork/encoding/asn1" + "gopkg.in/jcmturner/gokrb5.v7/asn1tools" + "gopkg.in/jcmturner/gokrb5.v7/client" + "gopkg.in/jcmturner/gokrb5.v7/gssapi" + "gopkg.in/jcmturner/gokrb5.v7/keytab" + "gopkg.in/jcmturner/gokrb5.v7/service" +) + +// SPNEGO implements the GSS-API mechanism for RFC 4178 +type SPNEGO struct { + serviceSettings *service.Settings + client *client.Client + spn string +} + +// SPNEGOClient configures the SPNEGO mechanism suitable for client side use. +func SPNEGOClient(cl *client.Client, spn string) *SPNEGO { + s := new(SPNEGO) + s.client = cl + s.spn = spn + s.serviceSettings = service.NewSettings(nil, service.SName(spn)) + return s +} + +// SPNEGOService configures the SPNEGO mechanism suitable for service side use. +func SPNEGOService(kt *keytab.Keytab, options ...func(*service.Settings)) *SPNEGO { + s := new(SPNEGO) + s.serviceSettings = service.NewSettings(kt, options...) + return s +} + +// OID returns the GSS-API assigned OID for SPNEGO. +func (s *SPNEGO) OID() asn1.ObjectIdentifier { + return gssapi.OID(gssapi.OIDSPNEGO) +} + +// AcquireCred is the GSS-API method to acquire a client credential via Kerberos for SPNEGO. +func (s *SPNEGO) AcquireCred() error { + return s.client.Login() +} + +// InitSecContext is the GSS-API method for the client to a generate a context token to the service via Kerberos. +func (s *SPNEGO) InitSecContext() (gssapi.ContextToken, error) { + tkt, key, err := s.client.GetServiceTicket(s.spn) + if err != nil { + return &SPNEGOToken{}, err + } + negTokenInit, err := NewNegTokenInitKRB5(s.client, tkt, key) + if err != nil { + return &SPNEGOToken{}, fmt.Errorf("could not create NegTokenInit: %v", err) + } + return &SPNEGOToken{ + Init: true, + NegTokenInit: negTokenInit, + settings: s.serviceSettings, + }, nil +} + +// AcceptSecContext is the GSS-API method for the service to verify the context token provided by the client and +// establish a context. +func (s *SPNEGO) AcceptSecContext(ct gssapi.ContextToken) (bool, context.Context, gssapi.Status) { + var ctx context.Context + t, ok := ct.(*SPNEGOToken) + if !ok { + return false, ctx, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "context token provided was not an SPNEGO token"} + } + t.settings = s.serviceSettings + var oid asn1.ObjectIdentifier + if t.Init { + oid = t.NegTokenInit.MechTypes[0] + } + if t.Resp { + oid = t.NegTokenResp.SupportedMech + } + if !(oid.Equal(gssapi.OID(gssapi.OIDKRB5)) || oid.Equal(gssapi.OID(gssapi.OIDMSLegacyKRB5))) { + return false, ctx, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "SPNEGO OID of MechToken is not of type KRB5"} + } + // Flags in the NegInit must be used t.NegTokenInit.ReqFlags + ok, status := t.Verify() + ctx = t.Context() + return ok, ctx, status +} + +// Log will write to the service's logger if it is configured. +func (s *SPNEGO) Log(format string, v ...interface{}) { + if s.serviceSettings.Logger() != nil { + s.serviceSettings.Logger().Printf(format, v...) + } +} + +// SPNEGOToken is a GSS-API context token +type SPNEGOToken struct { + Init bool + Resp bool + NegTokenInit NegTokenInit + NegTokenResp NegTokenResp + settings *service.Settings + context context.Context +} + +// Marshal SPNEGO context token +func (s *SPNEGOToken) Marshal() ([]byte, error) { + var b []byte + if s.Init { + hb, _ := asn1.Marshal(gssapi.OID(gssapi.OIDSPNEGO)) + tb, err := s.NegTokenInit.Marshal() + if err != nil { + return b, fmt.Errorf("could not marshal NegTokenInit: %v", err) + } + b = append(hb, tb...) + return asn1tools.AddASNAppTag(b, 0), nil + } + if s.Resp { + b, err := s.NegTokenResp.Marshal() + if err != nil { + return b, fmt.Errorf("could not marshal NegTokenResp: %v", err) + } + return b, nil + } + return b, errors.New("SPNEGO cannot be marshalled. It contains neither a NegTokenInit or NegTokenResp") +} + +// Unmarshal SPNEGO context token +func (s *SPNEGOToken) Unmarshal(b []byte) error { + var r []byte + var err error + if b[0] != byte(161) { + // Not a NegTokenResp/Targ could be a NegTokenInit + var oid asn1.ObjectIdentifier + r, err = asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0)) + if err != nil { + return fmt.Errorf("not a valid SPNEGO token: %v", err) + } + // Check the OID is the SPNEGO OID value + SPNEGOOID := gssapi.OID(gssapi.OIDSPNEGO) + if !oid.Equal(SPNEGOOID) { + return fmt.Errorf("OID %s does not match SPNEGO OID %s", oid.String(), SPNEGOOID.String()) + } + } else { + // Could be a NegTokenResp/Targ + r = b + } + + _, nt, err := UnmarshalNegToken(r) + if err != nil { + return err + } + switch v := nt.(type) { + case NegTokenInit: + s.Init = true + s.NegTokenInit = v + s.NegTokenInit.settings = s.settings + case NegTokenResp: + s.Resp = true + s.NegTokenResp = v + s.NegTokenResp.settings = s.settings + default: + return errors.New("unknown choice type for NegotiationToken") + } + return nil +} + +// Verify the SPNEGOToken +func (s *SPNEGOToken) Verify() (bool, gssapi.Status) { + if (!s.Init && !s.Resp) || (s.Init && s.Resp) { + return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "invalid SPNEGO token, unclear if NegTokenInit or NegTokenResp"} + } + if s.Init { + s.NegTokenInit.settings = s.settings + ok, status := s.NegTokenInit.Verify() + if ok { + s.context = s.NegTokenInit.Context() + } + return ok, status + } + if s.Resp { + s.NegTokenResp.settings = s.settings + ok, status := s.NegTokenResp.Verify() + if ok { + s.context = s.NegTokenResp.Context() + } + return ok, status + } + // should not be possible to get here + return false, gssapi.Status{Code: gssapi.StatusFailure, Message: "unable to verify SPNEGO token"} +} + +// Context returns the SPNEGO context which will contain any verify user identity information. +func (s *SPNEGOToken) Context() context.Context { + return s.context +} diff --git a/vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go b/vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go new file mode 100644 index 00000000000..5893df5bd26 --- /dev/null +++ b/vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package httpstream adds multiplexed streaming support to HTTP requests and +// responses via connection upgrades. +package httpstream // import "k8s.io/apimachinery/pkg/util/httpstream" diff --git a/vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go b/vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go new file mode 100644 index 00000000000..50d9a366f36 --- /dev/null +++ b/vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go @@ -0,0 +1,149 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package httpstream + +import ( + "fmt" + "io" + "net/http" + "strings" + "time" +) + +const ( + HeaderConnection = "Connection" + HeaderUpgrade = "Upgrade" + HeaderProtocolVersion = "X-Stream-Protocol-Version" + HeaderAcceptedProtocolVersions = "X-Accepted-Stream-Protocol-Versions" +) + +// NewStreamHandler defines a function that is called when a new Stream is +// received. If no error is returned, the Stream is accepted; otherwise, +// the stream is rejected. After the reply frame has been sent, replySent is closed. +type NewStreamHandler func(stream Stream, replySent <-chan struct{}) error + +// NoOpNewStreamHandler is a stream handler that accepts a new stream and +// performs no other logic. +func NoOpNewStreamHandler(stream Stream, replySent <-chan struct{}) error { return nil } + +// Dialer knows how to open a streaming connection to a server. +type Dialer interface { + + // Dial opens a streaming connection to a server using one of the protocols + // specified (in order of most preferred to least preferred). + Dial(protocols ...string) (Connection, string, error) +} + +// UpgradeRoundTripper is a type of http.RoundTripper that is able to upgrade +// HTTP requests to support multiplexed bidirectional streams. After RoundTrip() +// is invoked, if the upgrade is successful, clients may retrieve the upgraded +// connection by calling UpgradeRoundTripper.Connection(). +type UpgradeRoundTripper interface { + http.RoundTripper + // NewConnection validates the response and creates a new Connection. + NewConnection(resp *http.Response) (Connection, error) +} + +// ResponseUpgrader knows how to upgrade HTTP requests and responses to +// add streaming support to them. +type ResponseUpgrader interface { + // UpgradeResponse upgrades an HTTP response to one that supports multiplexed + // streams. newStreamHandler will be called asynchronously whenever the + // other end of the upgraded connection creates a new stream. + UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler NewStreamHandler) Connection +} + +// Connection represents an upgraded HTTP connection. +type Connection interface { + // CreateStream creates a new Stream with the supplied headers. + CreateStream(headers http.Header) (Stream, error) + // Close resets all streams and closes the connection. + Close() error + // CloseChan returns a channel that is closed when the underlying connection is closed. + CloseChan() <-chan bool + // SetIdleTimeout sets the amount of time the connection may remain idle before + // it is automatically closed. + SetIdleTimeout(timeout time.Duration) +} + +// Stream represents a bidirectional communications channel that is part of an +// upgraded connection. +type Stream interface { + io.ReadWriteCloser + // Reset closes both directions of the stream, indicating that neither client + // or server can use it any more. + Reset() error + // Headers returns the headers used to create the stream. + Headers() http.Header + // Identifier returns the stream's ID. + Identifier() uint32 +} + +// IsUpgradeRequest returns true if the given request is a connection upgrade request +func IsUpgradeRequest(req *http.Request) bool { + for _, h := range req.Header[http.CanonicalHeaderKey(HeaderConnection)] { + if strings.Contains(strings.ToLower(h), strings.ToLower(HeaderUpgrade)) { + return true + } + } + return false +} + +func negotiateProtocol(clientProtocols, serverProtocols []string) string { + for i := range clientProtocols { + for j := range serverProtocols { + if clientProtocols[i] == serverProtocols[j] { + return clientProtocols[i] + } + } + } + return "" +} + +// Handshake performs a subprotocol negotiation. If the client did request a +// subprotocol, Handshake will select the first common value found in +// serverProtocols. If a match is found, Handshake adds a response header +// indicating the chosen subprotocol. If no match is found, HTTP forbidden is +// returned, along with a response header containing the list of protocols the +// server can accept. +func Handshake(req *http.Request, w http.ResponseWriter, serverProtocols []string) (string, error) { + clientProtocols := req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)] + if len(clientProtocols) == 0 { + // Kube 1.0 clients didn't support subprotocol negotiation. + // TODO require clientProtocols once Kube 1.0 is no longer supported + return "", nil + } + + if len(serverProtocols) == 0 { + // Kube 1.0 servers didn't support subprotocol negotiation. This is mainly for testing. + // TODO require serverProtocols once Kube 1.0 is no longer supported + return "", nil + } + + negotiatedProtocol := negotiateProtocol(clientProtocols, serverProtocols) + if len(negotiatedProtocol) == 0 { + for i := range serverProtocols { + w.Header().Add(HeaderAcceptedProtocolVersions, serverProtocols[i]) + } + err := fmt.Errorf("unable to upgrade: unable to negotiate protocol: client supports %v, server accepts %v", clientProtocols, serverProtocols) + http.Error(w, err.Error(), http.StatusForbidden) + return "", err + } + + w.Header().Add(HeaderProtocolVersion, negotiatedProtocol) + return negotiatedProtocol, nil +} diff --git a/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go new file mode 100644 index 00000000000..9d222faa898 --- /dev/null +++ b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go @@ -0,0 +1,145 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package spdy + +import ( + "net" + "net/http" + "sync" + "time" + + "github.com/docker/spdystream" + "k8s.io/apimachinery/pkg/util/httpstream" + "k8s.io/klog" +) + +// connection maintains state about a spdystream.Connection and its associated +// streams. +type connection struct { + conn *spdystream.Connection + streams []httpstream.Stream + streamLock sync.Mutex + newStreamHandler httpstream.NewStreamHandler +} + +// NewClientConnection creates a new SPDY client connection. +func NewClientConnection(conn net.Conn) (httpstream.Connection, error) { + spdyConn, err := spdystream.NewConnection(conn, false) + if err != nil { + defer conn.Close() + return nil, err + } + + return newConnection(spdyConn, httpstream.NoOpNewStreamHandler), nil +} + +// NewServerConnection creates a new SPDY server connection. newStreamHandler +// will be invoked when the server receives a newly created stream from the +// client. +func NewServerConnection(conn net.Conn, newStreamHandler httpstream.NewStreamHandler) (httpstream.Connection, error) { + spdyConn, err := spdystream.NewConnection(conn, true) + if err != nil { + defer conn.Close() + return nil, err + } + + return newConnection(spdyConn, newStreamHandler), nil +} + +// newConnection returns a new connection wrapping conn. newStreamHandler +// will be invoked when the server receives a newly created stream from the +// client. +func newConnection(conn *spdystream.Connection, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection { + c := &connection{conn: conn, newStreamHandler: newStreamHandler} + go conn.Serve(c.newSpdyStream) + return c +} + +// createStreamResponseTimeout indicates how long to wait for the other side to +// acknowledge the new stream before timing out. +const createStreamResponseTimeout = 30 * time.Second + +// Close first sends a reset for all of the connection's streams, and then +// closes the underlying spdystream.Connection. +func (c *connection) Close() error { + c.streamLock.Lock() + for _, s := range c.streams { + // calling Reset instead of Close ensures that all streams are fully torn down + s.Reset() + } + c.streams = make([]httpstream.Stream, 0) + c.streamLock.Unlock() + + // now that all streams are fully torn down, it's safe to call close on the underlying connection, + // which should be able to terminate immediately at this point, instead of waiting for any + // remaining graceful stream termination. + return c.conn.Close() +} + +// CreateStream creates a new stream with the specified headers and registers +// it with the connection. +func (c *connection) CreateStream(headers http.Header) (httpstream.Stream, error) { + stream, err := c.conn.CreateStream(headers, nil, false) + if err != nil { + return nil, err + } + if err = stream.WaitTimeout(createStreamResponseTimeout); err != nil { + return nil, err + } + + c.registerStream(stream) + return stream, nil +} + +// registerStream adds the stream s to the connection's list of streams that +// it owns. +func (c *connection) registerStream(s httpstream.Stream) { + c.streamLock.Lock() + c.streams = append(c.streams, s) + c.streamLock.Unlock() +} + +// CloseChan returns a channel that, when closed, indicates that the underlying +// spdystream.Connection has been closed. +func (c *connection) CloseChan() <-chan bool { + return c.conn.CloseChan() +} + +// newSpdyStream is the internal new stream handler used by spdystream.Connection.Serve. +// It calls connection's newStreamHandler, giving it the opportunity to accept or reject +// the stream. If newStreamHandler returns an error, the stream is rejected. If not, the +// stream is accepted and registered with the connection. +func (c *connection) newSpdyStream(stream *spdystream.Stream) { + replySent := make(chan struct{}) + err := c.newStreamHandler(stream, replySent) + rejectStream := (err != nil) + if rejectStream { + klog.Warningf("Stream rejected: %v", err) + stream.Reset() + return + } + + c.registerStream(stream) + stream.SendReply(http.Header{}, rejectStream) + close(replySent) +} + +// SetIdleTimeout sets the amount of time the connection may remain idle before +// it is automatically closed. +func (c *connection) SetIdleTimeout(timeout time.Duration) { + c.conn.SetIdleTimeout(timeout) +} diff --git a/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go new file mode 100644 index 00000000000..2699597e7a5 --- /dev/null +++ b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go @@ -0,0 +1,335 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package spdy + +import ( + "bufio" + "bytes" + "context" + "crypto/tls" + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httputil" + "net/url" + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/util/httpstream" + utilnet "k8s.io/apimachinery/pkg/util/net" + "k8s.io/apimachinery/third_party/forked/golang/netutil" +) + +// SpdyRoundTripper knows how to upgrade an HTTP request to one that supports +// multiplexed streams. After RoundTrip() is invoked, Conn will be set +// and usable. SpdyRoundTripper implements the UpgradeRoundTripper interface. +type SpdyRoundTripper struct { + //tlsConfig holds the TLS configuration settings to use when connecting + //to the remote server. + tlsConfig *tls.Config + + /* TODO according to http://golang.org/pkg/net/http/#RoundTripper, a RoundTripper + must be safe for use by multiple concurrent goroutines. If this is absolutely + necessary, we could keep a map from http.Request to net.Conn. In practice, + a client will create an http.Client, set the transport to a new insteace of + SpdyRoundTripper, and use it a single time, so this hopefully won't be an issue. + */ + // conn is the underlying network connection to the remote server. + conn net.Conn + + // Dialer is the dialer used to connect. Used if non-nil. + Dialer *net.Dialer + + // proxier knows which proxy to use given a request, defaults to http.ProxyFromEnvironment + // Used primarily for mocking the proxy discovery in tests. + proxier func(req *http.Request) (*url.URL, error) + + // followRedirects indicates if the round tripper should examine responses for redirects and + // follow them. + followRedirects bool + // requireSameHostRedirects restricts redirect following to only follow redirects to the same host + // as the original request. + requireSameHostRedirects bool +} + +var _ utilnet.TLSClientConfigHolder = &SpdyRoundTripper{} +var _ httpstream.UpgradeRoundTripper = &SpdyRoundTripper{} +var _ utilnet.Dialer = &SpdyRoundTripper{} + +// NewRoundTripper creates a new SpdyRoundTripper that will use +// the specified tlsConfig. +func NewRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) httpstream.UpgradeRoundTripper { + return NewSpdyRoundTripper(tlsConfig, followRedirects, requireSameHostRedirects) +} + +// NewSpdyRoundTripper creates a new SpdyRoundTripper that will use +// the specified tlsConfig. This function is mostly meant for unit tests. +func NewSpdyRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) *SpdyRoundTripper { + return &SpdyRoundTripper{ + tlsConfig: tlsConfig, + followRedirects: followRedirects, + requireSameHostRedirects: requireSameHostRedirects, + } +} + +// TLSClientConfig implements pkg/util/net.TLSClientConfigHolder for proper TLS checking during +// proxying with a spdy roundtripper. +func (s *SpdyRoundTripper) TLSClientConfig() *tls.Config { + return s.tlsConfig +} + +// Dial implements k8s.io/apimachinery/pkg/util/net.Dialer. +func (s *SpdyRoundTripper) Dial(req *http.Request) (net.Conn, error) { + conn, err := s.dial(req) + if err != nil { + return nil, err + } + + if err := req.Write(conn); err != nil { + conn.Close() + return nil, err + } + + return conn, nil +} + +// dial dials the host specified by req, using TLS if appropriate, optionally +// using a proxy server if one is configured via environment variables. +func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) { + proxier := s.proxier + if proxier == nil { + proxier = utilnet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment) + } + proxyURL, err := proxier(req) + if err != nil { + return nil, err + } + + if proxyURL == nil { + return s.dialWithoutProxy(req.Context(), req.URL) + } + + // ensure we use a canonical host with proxyReq + targetHost := netutil.CanonicalAddr(req.URL) + + // proxying logic adapted from http://blog.h6t.eu/post/74098062923/golang-websocket-with-http-proxy-support + proxyReq := http.Request{ + Method: "CONNECT", + URL: &url.URL{}, + Host: targetHost, + } + + if pa := s.proxyAuth(proxyURL); pa != "" { + proxyReq.Header = http.Header{} + proxyReq.Header.Set("Proxy-Authorization", pa) + } + + proxyDialConn, err := s.dialWithoutProxy(req.Context(), proxyURL) + if err != nil { + return nil, err + } + + proxyClientConn := httputil.NewProxyClientConn(proxyDialConn, nil) + _, err = proxyClientConn.Do(&proxyReq) + if err != nil && err != httputil.ErrPersistEOF { + return nil, err + } + + rwc, _ := proxyClientConn.Hijack() + + if req.URL.Scheme != "https" { + return rwc, nil + } + + host, _, err := net.SplitHostPort(targetHost) + if err != nil { + return nil, err + } + + tlsConfig := s.tlsConfig + switch { + case tlsConfig == nil: + tlsConfig = &tls.Config{ServerName: host} + case len(tlsConfig.ServerName) == 0: + tlsConfig = tlsConfig.Clone() + tlsConfig.ServerName = host + } + + tlsConn := tls.Client(rwc, tlsConfig) + + // need to manually call Handshake() so we can call VerifyHostname() below + if err := tlsConn.Handshake(); err != nil { + return nil, err + } + + // Return if we were configured to skip validation + if tlsConfig.InsecureSkipVerify { + return tlsConn, nil + } + + if err := tlsConn.VerifyHostname(tlsConfig.ServerName); err != nil { + return nil, err + } + + return tlsConn, nil +} + +// dialWithoutProxy dials the host specified by url, using TLS if appropriate. +func (s *SpdyRoundTripper) dialWithoutProxy(ctx context.Context, url *url.URL) (net.Conn, error) { + dialAddr := netutil.CanonicalAddr(url) + + if url.Scheme == "http" { + if s.Dialer == nil { + var d net.Dialer + return d.DialContext(ctx, "tcp", dialAddr) + } else { + return s.Dialer.DialContext(ctx, "tcp", dialAddr) + } + } + + // TODO validate the TLSClientConfig is set up? + var conn *tls.Conn + var err error + if s.Dialer == nil { + conn, err = tls.Dial("tcp", dialAddr, s.tlsConfig) + } else { + conn, err = tls.DialWithDialer(s.Dialer, "tcp", dialAddr, s.tlsConfig) + } + if err != nil { + return nil, err + } + + // Return if we were configured to skip validation + if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify { + return conn, nil + } + + host, _, err := net.SplitHostPort(dialAddr) + if err != nil { + return nil, err + } + if s.tlsConfig != nil && len(s.tlsConfig.ServerName) > 0 { + host = s.tlsConfig.ServerName + } + err = conn.VerifyHostname(host) + if err != nil { + return nil, err + } + + return conn, nil +} + +// proxyAuth returns, for a given proxy URL, the value to be used for the Proxy-Authorization header +func (s *SpdyRoundTripper) proxyAuth(proxyURL *url.URL) string { + if proxyURL == nil || proxyURL.User == nil { + return "" + } + credentials := proxyURL.User.String() + encodedAuth := base64.StdEncoding.EncodeToString([]byte(credentials)) + return fmt.Sprintf("Basic %s", encodedAuth) +} + +// RoundTrip executes the Request and upgrades it. After a successful upgrade, +// clients may call SpdyRoundTripper.Connection() to retrieve the upgraded +// connection. +func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + header := utilnet.CloneHeader(req.Header) + header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) + header.Add(httpstream.HeaderUpgrade, HeaderSpdy31) + + var ( + conn net.Conn + rawResponse []byte + err error + ) + + if s.followRedirects { + conn, rawResponse, err = utilnet.ConnectWithRedirects(req.Method, req.URL, header, req.Body, s, s.requireSameHostRedirects) + } else { + clone := utilnet.CloneRequest(req) + clone.Header = header + conn, err = s.Dial(clone) + } + if err != nil { + return nil, err + } + + responseReader := bufio.NewReader( + io.MultiReader( + bytes.NewBuffer(rawResponse), + conn, + ), + ) + + resp, err := http.ReadResponse(responseReader, nil) + if err != nil { + if conn != nil { + conn.Close() + } + return nil, err + } + + s.conn = conn + + return resp, nil +} + +// NewConnection validates the upgrade response, creating and returning a new +// httpstream.Connection if there were no errors. +func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) { + connectionHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderConnection)) + upgradeHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderUpgrade)) + if (resp.StatusCode != http.StatusSwitchingProtocols) || !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) { + defer resp.Body.Close() + responseError := "" + responseErrorBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + responseError = "unable to read error from server response" + } else { + // TODO: I don't belong here, I should be abstracted from this class + if obj, _, err := statusCodecs.UniversalDecoder().Decode(responseErrorBytes, nil, &metav1.Status{}); err == nil { + if status, ok := obj.(*metav1.Status); ok { + return nil, &apierrors.StatusError{ErrStatus: *status} + } + } + responseError = string(responseErrorBytes) + responseError = strings.TrimSpace(responseError) + } + + return nil, fmt.Errorf("unable to upgrade connection: %s", responseError) + } + + return NewClientConnection(s.conn) +} + +// statusScheme is private scheme for the decoding here until someone fixes the TODO in NewConnection +var statusScheme = runtime.NewScheme() + +// ParameterCodec knows about query parameters used with the meta v1 API spec. +var statusCodecs = serializer.NewCodecFactory(statusScheme) + +func init() { + statusScheme.AddUnversionedTypes(metav1.SchemeGroupVersion, + &metav1.Status{}, + ) +} diff --git a/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go new file mode 100644 index 00000000000..045d214d2b7 --- /dev/null +++ b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go @@ -0,0 +1,107 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package spdy + +import ( + "bufio" + "fmt" + "io" + "net" + "net/http" + "strings" + "sync/atomic" + + "k8s.io/apimachinery/pkg/util/httpstream" + "k8s.io/apimachinery/pkg/util/runtime" +) + +const HeaderSpdy31 = "SPDY/3.1" + +// responseUpgrader knows how to upgrade HTTP responses. It +// implements the httpstream.ResponseUpgrader interface. +type responseUpgrader struct { +} + +// connWrapper is used to wrap a hijacked connection and its bufio.Reader. All +// calls will be handled directly by the underlying net.Conn with the exception +// of Read and Close calls, which will consider data in the bufio.Reader. This +// ensures that data already inside the used bufio.Reader instance is also +// read. +type connWrapper struct { + net.Conn + closed int32 + bufReader *bufio.Reader +} + +func (w *connWrapper) Read(b []byte) (n int, err error) { + if atomic.LoadInt32(&w.closed) == 1 { + return 0, io.EOF + } + return w.bufReader.Read(b) +} + +func (w *connWrapper) Close() error { + err := w.Conn.Close() + atomic.StoreInt32(&w.closed, 1) + return err +} + +// NewResponseUpgrader returns a new httpstream.ResponseUpgrader that is +// capable of upgrading HTTP responses using SPDY/3.1 via the +// spdystream package. +func NewResponseUpgrader() httpstream.ResponseUpgrader { + return responseUpgrader{} +} + +// UpgradeResponse upgrades an HTTP response to one that supports multiplexed +// streams. newStreamHandler will be called synchronously whenever the +// other end of the upgraded connection creates a new stream. +func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection { + connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection)) + upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade)) + if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) { + errorMsg := fmt.Sprintf("unable to upgrade: missing upgrade headers in request: %#v", req.Header) + http.Error(w, errorMsg, http.StatusBadRequest) + return nil + } + + hijacker, ok := w.(http.Hijacker) + if !ok { + errorMsg := fmt.Sprintf("unable to upgrade: unable to hijack response") + http.Error(w, errorMsg, http.StatusInternalServerError) + return nil + } + + w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade) + w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31) + w.WriteHeader(http.StatusSwitchingProtocols) + + conn, bufrw, err := hijacker.Hijack() + if err != nil { + runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err)) + return nil + } + + connWithBuf := &connWrapper{Conn: conn, bufReader: bufrw.Reader} + spdyConn, err := NewServerConnection(connWithBuf, newStreamHandler) + if err != nil { + runtime.HandleError(fmt.Errorf("unable to upgrade: error creating SPDY server connection: %v", err)) + return nil + } + + return spdyConn +} diff --git a/vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go b/vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go new file mode 100644 index 00000000000..c70f431c272 --- /dev/null +++ b/vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go @@ -0,0 +1,27 @@ +package netutil + +import ( + "net/url" + "strings" +) + +// FROM: http://golang.org/src/net/http/client.go +// Given a string of the form "host", "host:port", or "[ipv6::address]:port", +// return true if the string includes a port. +func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } + +// FROM: http://golang.org/src/net/http/transport.go +var portMap = map[string]string{ + "http": "80", + "https": "443", +} + +// FROM: http://golang.org/src/net/http/transport.go +// canonicalAddr returns url.Host but always with a ":port" suffix +func CanonicalAddr(url *url.URL) string { + addr := url.Host + if !hasPort(addr) { + return addr + ":" + portMap[url.Scheme] + } + return addr +} diff --git a/vendor/k8s.io/client-go/tools/portforward/doc.go b/vendor/k8s.io/client-go/tools/portforward/doc.go new file mode 100644 index 00000000000..2f53406344f --- /dev/null +++ b/vendor/k8s.io/client-go/tools/portforward/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package portforward adds support for SSH-like port forwarding from the client's +// local host to remote containers. +package portforward // import "k8s.io/client-go/tools/portforward" diff --git a/vendor/k8s.io/client-go/tools/portforward/portforward.go b/vendor/k8s.io/client-go/tools/portforward/portforward.go new file mode 100644 index 00000000000..4ab72bb4f3c --- /dev/null +++ b/vendor/k8s.io/client-go/tools/portforward/portforward.go @@ -0,0 +1,429 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package portforward + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "sort" + "strconv" + "strings" + "sync" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/httpstream" + "k8s.io/apimachinery/pkg/util/runtime" +) + +// PortForwardProtocolV1Name is the subprotocol used for port forwarding. +// TODO move to API machinery and re-unify with kubelet/server/portfoward +const PortForwardProtocolV1Name = "portforward.k8s.io" + +// PortForwarder knows how to listen for local connections and forward them to +// a remote pod via an upgraded HTTP request. +type PortForwarder struct { + addresses []listenAddress + ports []ForwardedPort + stopChan <-chan struct{} + + dialer httpstream.Dialer + streamConn httpstream.Connection + listeners []io.Closer + Ready chan struct{} + requestIDLock sync.Mutex + requestID int + out io.Writer + errOut io.Writer +} + +// ForwardedPort contains a Local:Remote port pairing. +type ForwardedPort struct { + Local uint16 + Remote uint16 +} + +/* + valid port specifications: + + 5000 + - forwards from localhost:5000 to pod:5000 + + 8888:5000 + - forwards from localhost:8888 to pod:5000 + + 0:5000 + :5000 + - selects a random available local port, + forwards from localhost: to pod:5000 +*/ +func parsePorts(ports []string) ([]ForwardedPort, error) { + var forwards []ForwardedPort + for _, portString := range ports { + parts := strings.Split(portString, ":") + var localString, remoteString string + if len(parts) == 1 { + localString = parts[0] + remoteString = parts[0] + } else if len(parts) == 2 { + localString = parts[0] + if localString == "" { + // support :5000 + localString = "0" + } + remoteString = parts[1] + } else { + return nil, fmt.Errorf("Invalid port format '%s'", portString) + } + + localPort, err := strconv.ParseUint(localString, 10, 16) + if err != nil { + return nil, fmt.Errorf("Error parsing local port '%s': %s", localString, err) + } + + remotePort, err := strconv.ParseUint(remoteString, 10, 16) + if err != nil { + return nil, fmt.Errorf("Error parsing remote port '%s': %s", remoteString, err) + } + if remotePort == 0 { + return nil, fmt.Errorf("Remote port must be > 0") + } + + forwards = append(forwards, ForwardedPort{uint16(localPort), uint16(remotePort)}) + } + + return forwards, nil +} + +type listenAddress struct { + address string + protocol string + failureMode string +} + +func parseAddresses(addressesToParse []string) ([]listenAddress, error) { + var addresses []listenAddress + parsed := make(map[string]listenAddress) + for _, address := range addressesToParse { + if address == "localhost" { + if _, exists := parsed["127.0.0.1"]; !exists { + ip := listenAddress{address: "127.0.0.1", protocol: "tcp4", failureMode: "all"} + parsed[ip.address] = ip + } + if _, exists := parsed["::1"]; !exists { + ip := listenAddress{address: "::1", protocol: "tcp6", failureMode: "all"} + parsed[ip.address] = ip + } + } else if net.ParseIP(address).To4() != nil { + parsed[address] = listenAddress{address: address, protocol: "tcp4", failureMode: "any"} + } else if net.ParseIP(address) != nil { + parsed[address] = listenAddress{address: address, protocol: "tcp6", failureMode: "any"} + } else { + return nil, fmt.Errorf("%s is not a valid IP", address) + } + } + addresses = make([]listenAddress, len(parsed)) + id := 0 + for _, v := range parsed { + addresses[id] = v + id++ + } + // Sort addresses before returning to get a stable order + sort.Slice(addresses, func(i, j int) bool { return addresses[i].address < addresses[j].address }) + + return addresses, nil +} + +// New creates a new PortForwarder with localhost listen addresses. +func New(dialer httpstream.Dialer, ports []string, stopChan <-chan struct{}, readyChan chan struct{}, out, errOut io.Writer) (*PortForwarder, error) { + return NewOnAddresses(dialer, []string{"localhost"}, ports, stopChan, readyChan, out, errOut) +} + +// NewOnAddresses creates a new PortForwarder with custom listen addresses. +func NewOnAddresses(dialer httpstream.Dialer, addresses []string, ports []string, stopChan <-chan struct{}, readyChan chan struct{}, out, errOut io.Writer) (*PortForwarder, error) { + if len(addresses) == 0 { + return nil, errors.New("You must specify at least 1 address") + } + parsedAddresses, err := parseAddresses(addresses) + if err != nil { + return nil, err + } + if len(ports) == 0 { + return nil, errors.New("You must specify at least 1 port") + } + parsedPorts, err := parsePorts(ports) + if err != nil { + return nil, err + } + return &PortForwarder{ + dialer: dialer, + addresses: parsedAddresses, + ports: parsedPorts, + stopChan: stopChan, + Ready: readyChan, + out: out, + errOut: errOut, + }, nil +} + +// ForwardPorts formats and executes a port forwarding request. The connection will remain +// open until stopChan is closed. +func (pf *PortForwarder) ForwardPorts() error { + defer pf.Close() + + var err error + pf.streamConn, _, err = pf.dialer.Dial(PortForwardProtocolV1Name) + if err != nil { + return fmt.Errorf("error upgrading connection: %s", err) + } + defer pf.streamConn.Close() + + return pf.forward() +} + +// forward dials the remote host specific in req, upgrades the request, starts +// listeners for each port specified in ports, and forwards local connections +// to the remote host via streams. +func (pf *PortForwarder) forward() error { + var err error + + listenSuccess := false + for i := range pf.ports { + port := &pf.ports[i] + err = pf.listenOnPort(port) + switch { + case err == nil: + listenSuccess = true + default: + if pf.errOut != nil { + fmt.Fprintf(pf.errOut, "Unable to listen on port %d: %v\n", port.Local, err) + } + } + } + + if !listenSuccess { + return fmt.Errorf("Unable to listen on any of the requested ports: %v", pf.ports) + } + + if pf.Ready != nil { + close(pf.Ready) + } + + // wait for interrupt or conn closure + select { + case <-pf.stopChan: + case <-pf.streamConn.CloseChan(): + runtime.HandleError(errors.New("lost connection to pod")) + } + + return nil +} + +// listenOnPort delegates listener creation and waits for connections on requested bind addresses. +// An error is raised based on address groups (default and localhost) and their failure modes +func (pf *PortForwarder) listenOnPort(port *ForwardedPort) error { + var errors []error + failCounters := make(map[string]int, 2) + successCounters := make(map[string]int, 2) + for _, addr := range pf.addresses { + err := pf.listenOnPortAndAddress(port, addr.protocol, addr.address) + if err != nil { + errors = append(errors, err) + failCounters[addr.failureMode]++ + } else { + successCounters[addr.failureMode]++ + } + } + if successCounters["all"] == 0 && failCounters["all"] > 0 { + return fmt.Errorf("%s: %v", "Listeners failed to create with the following errors", errors) + } + if failCounters["any"] > 0 { + return fmt.Errorf("%s: %v", "Listeners failed to create with the following errors", errors) + } + return nil +} + +// listenOnPortAndAddress delegates listener creation and waits for new connections +// in the background f +func (pf *PortForwarder) listenOnPortAndAddress(port *ForwardedPort, protocol string, address string) error { + listener, err := pf.getListener(protocol, address, port) + if err != nil { + return err + } + pf.listeners = append(pf.listeners, listener) + go pf.waitForConnection(listener, *port) + return nil +} + +// getListener creates a listener on the interface targeted by the given hostname on the given port with +// the given protocol. protocol is in net.Listen style which basically admits values like tcp, tcp4, tcp6 +func (pf *PortForwarder) getListener(protocol string, hostname string, port *ForwardedPort) (net.Listener, error) { + listener, err := net.Listen(protocol, net.JoinHostPort(hostname, strconv.Itoa(int(port.Local)))) + if err != nil { + return nil, fmt.Errorf("Unable to create listener: Error %s", err) + } + listenerAddress := listener.Addr().String() + host, localPort, _ := net.SplitHostPort(listenerAddress) + localPortUInt, err := strconv.ParseUint(localPort, 10, 16) + + if err != nil { + fmt.Fprintf(pf.out, "Failed to forward from %s:%d -> %d\n", hostname, localPortUInt, port.Remote) + return nil, fmt.Errorf("Error parsing local port: %s from %s (%s)", err, listenerAddress, host) + } + port.Local = uint16(localPortUInt) + if pf.out != nil { + fmt.Fprintf(pf.out, "Forwarding from %s -> %d\n", net.JoinHostPort(hostname, strconv.Itoa(int(localPortUInt))), port.Remote) + } + + return listener, nil +} + +// waitForConnection waits for new connections to listener and handles them in +// the background. +func (pf *PortForwarder) waitForConnection(listener net.Listener, port ForwardedPort) { + for { + conn, err := listener.Accept() + if err != nil { + // TODO consider using something like https://github.com/hydrogen18/stoppableListener? + if !strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") { + runtime.HandleError(fmt.Errorf("Error accepting connection on port %d: %v", port.Local, err)) + } + return + } + go pf.handleConnection(conn, port) + } +} + +func (pf *PortForwarder) nextRequestID() int { + pf.requestIDLock.Lock() + defer pf.requestIDLock.Unlock() + id := pf.requestID + pf.requestID++ + return id +} + +// handleConnection copies data between the local connection and the stream to +// the remote server. +func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) { + defer conn.Close() + + if pf.out != nil { + fmt.Fprintf(pf.out, "Handling connection for %d\n", port.Local) + } + + requestID := pf.nextRequestID() + + // create error stream + headers := http.Header{} + headers.Set(v1.StreamType, v1.StreamTypeError) + headers.Set(v1.PortHeader, fmt.Sprintf("%d", port.Remote)) + headers.Set(v1.PortForwardRequestIDHeader, strconv.Itoa(requestID)) + errorStream, err := pf.streamConn.CreateStream(headers) + if err != nil { + runtime.HandleError(fmt.Errorf("error creating error stream for port %d -> %d: %v", port.Local, port.Remote, err)) + return + } + // we're not writing to this stream + errorStream.Close() + + errorChan := make(chan error) + go func() { + message, err := ioutil.ReadAll(errorStream) + switch { + case err != nil: + errorChan <- fmt.Errorf("error reading from error stream for port %d -> %d: %v", port.Local, port.Remote, err) + case len(message) > 0: + errorChan <- fmt.Errorf("an error occurred forwarding %d -> %d: %v", port.Local, port.Remote, string(message)) + } + close(errorChan) + }() + + // create data stream + headers.Set(v1.StreamType, v1.StreamTypeData) + dataStream, err := pf.streamConn.CreateStream(headers) + if err != nil { + runtime.HandleError(fmt.Errorf("error creating forwarding stream for port %d -> %d: %v", port.Local, port.Remote, err)) + return + } + + localError := make(chan struct{}) + remoteDone := make(chan struct{}) + + go func() { + // Copy from the remote side to the local port. + if _, err := io.Copy(conn, dataStream); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { + runtime.HandleError(fmt.Errorf("error copying from remote stream to local connection: %v", err)) + } + + // inform the select below that the remote copy is done + close(remoteDone) + }() + + go func() { + // inform server we're not sending any more data after copy unblocks + defer dataStream.Close() + + // Copy from the local port to the remote side. + if _, err := io.Copy(dataStream, conn); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { + runtime.HandleError(fmt.Errorf("error copying from local connection to remote stream: %v", err)) + // break out of the select below without waiting for the other copy to finish + close(localError) + } + }() + + // wait for either a local->remote error or for copying from remote->local to finish + select { + case <-remoteDone: + case <-localError: + } + + // always expect something on errorChan (it may be nil) + err = <-errorChan + if err != nil { + runtime.HandleError(err) + } +} + +// Close stops all listeners of PortForwarder. +func (pf *PortForwarder) Close() { + // stop all listeners + for _, l := range pf.listeners { + if err := l.Close(); err != nil { + runtime.HandleError(fmt.Errorf("error closing listener: %v", err)) + } + } +} + +// GetPorts will return the ports that were forwarded; this can be used to +// retrieve the locally-bound port in cases where the input was port 0. This +// function will signal an error if the Ready channel is nil or if the +// listeners are not ready yet; this function will succeed after the Ready +// channel has been closed. +func (pf *PortForwarder) GetPorts() ([]ForwardedPort, error) { + if pf.Ready == nil { + return nil, fmt.Errorf("no Ready channel provided") + } + select { + case <-pf.Ready: + return pf.ports, nil + default: + return nil, fmt.Errorf("listeners not ready") + } +} diff --git a/vendor/k8s.io/client-go/tools/watch/informerwatcher.go b/vendor/k8s.io/client-go/tools/watch/informerwatcher.go new file mode 100644 index 00000000000..4e0a400bb55 --- /dev/null +++ b/vendor/k8s.io/client-go/tools/watch/informerwatcher.go @@ -0,0 +1,150 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package watch + +import ( + "sync" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +func newEventProcessor(out chan<- watch.Event) *eventProcessor { + return &eventProcessor{ + out: out, + cond: sync.NewCond(&sync.Mutex{}), + done: make(chan struct{}), + } +} + +// eventProcessor buffers events and writes them to an out chan when a reader +// is waiting. Because of the requirement to buffer events, it synchronizes +// input with a condition, and synchronizes output with a channels. It needs to +// be able to yield while both waiting on an input condition and while blocked +// on writing to the output channel. +type eventProcessor struct { + out chan<- watch.Event + + cond *sync.Cond + buff []watch.Event + + done chan struct{} +} + +func (e *eventProcessor) run() { + for { + batch := e.takeBatch() + e.writeBatch(batch) + if e.stopped() { + return + } + } +} + +func (e *eventProcessor) takeBatch() []watch.Event { + e.cond.L.Lock() + defer e.cond.L.Unlock() + + for len(e.buff) == 0 && !e.stopped() { + e.cond.Wait() + } + + batch := e.buff + e.buff = nil + return batch +} + +func (e *eventProcessor) writeBatch(events []watch.Event) { + for _, event := range events { + select { + case e.out <- event: + case <-e.done: + return + } + } +} + +func (e *eventProcessor) push(event watch.Event) { + e.cond.L.Lock() + defer e.cond.L.Unlock() + defer e.cond.Signal() + e.buff = append(e.buff, event) +} + +func (e *eventProcessor) stopped() bool { + select { + case <-e.done: + return true + default: + return false + } +} + +func (e *eventProcessor) stop() { + close(e.done) + e.cond.Signal() +} + +// NewIndexerInformerWatcher will create an IndexerInformer and wrap it into watch.Interface +// so you can use it anywhere where you'd have used a regular Watcher returned from Watch method. +// it also returns a channel you can use to wait for the informers to fully shutdown. +func NewIndexerInformerWatcher(lw cache.ListerWatcher, objType runtime.Object) (cache.Indexer, cache.Controller, watch.Interface, <-chan struct{}) { + ch := make(chan watch.Event) + w := watch.NewProxyWatcher(ch) + e := newEventProcessor(ch) + + indexer, informer := cache.NewIndexerInformer(lw, objType, 0, cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + e.push(watch.Event{ + Type: watch.Added, + Object: obj.(runtime.Object), + }) + }, + UpdateFunc: func(old, new interface{}) { + e.push(watch.Event{ + Type: watch.Modified, + Object: new.(runtime.Object), + }) + }, + DeleteFunc: func(obj interface{}) { + staleObj, stale := obj.(cache.DeletedFinalStateUnknown) + if stale { + // We have no means of passing the additional information down using + // watch API based on watch.Event but the caller can filter such + // objects by checking if metadata.deletionTimestamp is set + obj = staleObj + } + + e.push(watch.Event{ + Type: watch.Deleted, + Object: obj.(runtime.Object), + }) + }, + }, cache.Indexers{}) + + go e.run() + + doneCh := make(chan struct{}) + go func() { + defer close(doneCh) + defer e.stop() + informer.Run(w.StopChan()) + }() + + return indexer, informer, w, doneCh +} diff --git a/vendor/k8s.io/client-go/tools/watch/retrywatcher.go b/vendor/k8s.io/client-go/tools/watch/retrywatcher.go new file mode 100644 index 00000000000..47ae9df4afd --- /dev/null +++ b/vendor/k8s.io/client-go/tools/watch/retrywatcher.go @@ -0,0 +1,287 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package watch + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "time" + + "github.com/davecgh/go-spew/spew" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/net" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" + "k8s.io/klog" +) + +// resourceVersionGetter is an interface used to get resource version from events. +// We can't reuse an interface from meta otherwise it would be a cyclic dependency and we need just this one method +type resourceVersionGetter interface { + GetResourceVersion() string +} + +// RetryWatcher will make sure that in case the underlying watcher is closed (e.g. due to API timeout or etcd timeout) +// it will get restarted from the last point without the consumer even knowing about it. +// RetryWatcher does that by inspecting events and keeping track of resourceVersion. +// Especially useful when using watch.UntilWithoutRetry where premature termination is causing issues and flakes. +// Please note that this is not resilient to etcd cache not having the resource version anymore - you would need to +// use Informers for that. +type RetryWatcher struct { + lastResourceVersion string + watcherClient cache.Watcher + resultChan chan watch.Event + stopChan chan struct{} + doneChan chan struct{} + minRestartDelay time.Duration +} + +// NewRetryWatcher creates a new RetryWatcher. +// It will make sure that watches gets restarted in case of recoverable errors. +// The initialResourceVersion will be given to watch method when first called. +func NewRetryWatcher(initialResourceVersion string, watcherClient cache.Watcher) (*RetryWatcher, error) { + return newRetryWatcher(initialResourceVersion, watcherClient, 1*time.Second) +} + +func newRetryWatcher(initialResourceVersion string, watcherClient cache.Watcher, minRestartDelay time.Duration) (*RetryWatcher, error) { + switch initialResourceVersion { + case "", "0": + // TODO: revisit this if we ever get WATCH v2 where it means start "now" + // without doing the synthetic list of objects at the beginning (see #74022) + return nil, fmt.Errorf("initial RV %q is not supported due to issues with underlying WATCH", initialResourceVersion) + default: + break + } + + rw := &RetryWatcher{ + lastResourceVersion: initialResourceVersion, + watcherClient: watcherClient, + stopChan: make(chan struct{}), + doneChan: make(chan struct{}), + resultChan: make(chan watch.Event, 0), + minRestartDelay: minRestartDelay, + } + + go rw.receive() + return rw, nil +} + +func (rw *RetryWatcher) send(event watch.Event) bool { + // Writing to an unbuffered channel is blocking operation + // and we need to check if stop wasn't requested while doing so. + select { + case rw.resultChan <- event: + return true + case <-rw.stopChan: + return false + } +} + +// doReceive returns true when it is done, false otherwise. +// If it is not done the second return value holds the time to wait before calling it again. +func (rw *RetryWatcher) doReceive() (bool, time.Duration) { + watcher, err := rw.watcherClient.Watch(metav1.ListOptions{ + ResourceVersion: rw.lastResourceVersion, + }) + // We are very unlikely to hit EOF here since we are just establishing the call, + // but it may happen that the apiserver is just shutting down (e.g. being restarted) + // This is consistent with how it is handled for informers + switch err { + case nil: + break + + case io.EOF: + // watch closed normally + return false, 0 + + case io.ErrUnexpectedEOF: + klog.V(1).Infof("Watch closed with unexpected EOF: %v", err) + return false, 0 + + default: + msg := "Watch failed: %v" + if net.IsProbableEOF(err) { + klog.V(5).Infof(msg, err) + // Retry + return false, 0 + } + + klog.Errorf(msg, err) + // Retry + return false, 0 + } + + if watcher == nil { + klog.Error("Watch returned nil watcher") + // Retry + return false, 0 + } + + ch := watcher.ResultChan() + defer watcher.Stop() + + for { + select { + case <-rw.stopChan: + klog.V(4).Info("Stopping RetryWatcher.") + return true, 0 + case event, ok := <-ch: + if !ok { + klog.V(4).Infof("Failed to get event! Re-creating the watcher. Last RV: %s", rw.lastResourceVersion) + return false, 0 + } + + // We need to inspect the event and get ResourceVersion out of it + switch event.Type { + case watch.Added, watch.Modified, watch.Deleted, watch.Bookmark: + metaObject, ok := event.Object.(resourceVersionGetter) + if !ok { + _ = rw.send(watch.Event{ + Type: watch.Error, + Object: &apierrors.NewInternalError(errors.New("retryWatcher: doesn't support resourceVersion")).ErrStatus, + }) + // We have to abort here because this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data! + return true, 0 + } + + resourceVersion := metaObject.GetResourceVersion() + if resourceVersion == "" { + _ = rw.send(watch.Event{ + Type: watch.Error, + Object: &apierrors.NewInternalError(fmt.Errorf("retryWatcher: object %#v doesn't support resourceVersion", event.Object)).ErrStatus, + }) + // We have to abort here because this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data! + return true, 0 + } + + // All is fine; send the event and update lastResourceVersion + ok = rw.send(event) + if !ok { + return true, 0 + } + rw.lastResourceVersion = resourceVersion + + continue + + case watch.Error: + // This round trip allows us to handle unstructured status + errObject := apierrors.FromObject(event.Object) + statusErr, ok := errObject.(*apierrors.StatusError) + if !ok { + klog.Error(spew.Sprintf("Received an error which is not *metav1.Status but %#+v", event.Object)) + // Retry unknown errors + return false, 0 + } + + status := statusErr.ErrStatus + + statusDelay := time.Duration(0) + if status.Details != nil { + statusDelay = time.Duration(status.Details.RetryAfterSeconds) * time.Second + } + + switch status.Code { + case http.StatusGone: + // Never retry RV too old errors + _ = rw.send(event) + return true, 0 + + case http.StatusGatewayTimeout, http.StatusInternalServerError: + // Retry + return false, statusDelay + + default: + // We retry by default. RetryWatcher is meant to proceed unless it is certain + // that it can't. If we are not certain, we proceed with retry and leave it + // up to the user to timeout if needed. + + // Log here so we have a record of hitting the unexpected error + // and we can whitelist some error codes if we missed any that are expected. + klog.V(5).Info(spew.Sprintf("Retrying after unexpected error: %#+v", event.Object)) + + // Retry + return false, statusDelay + } + + default: + klog.Errorf("Failed to recognize Event type %q", event.Type) + _ = rw.send(watch.Event{ + Type: watch.Error, + Object: &apierrors.NewInternalError(fmt.Errorf("retryWatcher failed to recognize Event type %q", event.Type)).ErrStatus, + }) + // We are unable to restart the watch and have to stop the loop or this might cause lastResourceVersion inconsistency by skipping a potential RV with valid data! + return true, 0 + } + } + } +} + +// receive reads the result from a watcher, restarting it if necessary. +func (rw *RetryWatcher) receive() { + defer close(rw.doneChan) + defer close(rw.resultChan) + + klog.V(4).Info("Starting RetryWatcher.") + defer klog.V(4).Info("Stopping RetryWatcher.") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { + select { + case <-rw.stopChan: + cancel() + return + case <-ctx.Done(): + return + } + }() + + // We use non sliding until so we don't introduce delays on happy path when WATCH call + // timeouts or gets closed and we need to reestablish it while also avoiding hot loops. + wait.NonSlidingUntilWithContext(ctx, func(ctx context.Context) { + done, retryAfter := rw.doReceive() + if done { + cancel() + return + } + + time.Sleep(retryAfter) + + klog.V(4).Infof("Restarting RetryWatcher at RV=%q", rw.lastResourceVersion) + }, rw.minRestartDelay) +} + +// ResultChan implements Interface. +func (rw *RetryWatcher) ResultChan() <-chan watch.Event { + return rw.resultChan +} + +// Stop implements Interface. +func (rw *RetryWatcher) Stop() { + close(rw.stopChan) +} + +// Done allows the caller to be notified when Retry watcher stops. +func (rw *RetryWatcher) Done() <-chan struct{} { + return rw.doneChan +} diff --git a/vendor/k8s.io/client-go/tools/watch/until.go b/vendor/k8s.io/client-go/tools/watch/until.go new file mode 100644 index 00000000000..e12d82aca48 --- /dev/null +++ b/vendor/k8s.io/client-go/tools/watch/until.go @@ -0,0 +1,236 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package watch + +import ( + "context" + "errors" + "fmt" + "time" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" + "k8s.io/klog" +) + +// PreconditionFunc returns true if the condition has been reached, false if it has not been reached yet, +// or an error if the condition failed or detected an error state. +type PreconditionFunc func(store cache.Store) (bool, error) + +// ConditionFunc returns true if the condition has been reached, false if it has not been reached yet, +// or an error if the condition cannot be checked and should terminate. In general, it is better to define +// level driven conditions over edge driven conditions (pod has ready=true, vs pod modified and ready changed +// from false to true). +type ConditionFunc func(event watch.Event) (bool, error) + +// ErrWatchClosed is returned when the watch channel is closed before timeout in UntilWithoutRetry. +var ErrWatchClosed = errors.New("watch closed before UntilWithoutRetry timeout") + +// UntilWithoutRetry reads items from the watch until each provided condition succeeds, and then returns the last watch +// encountered. The first condition that returns an error terminates the watch (and the event is also returned). +// If no event has been received, the returned event will be nil. +// Conditions are satisfied sequentially so as to provide a useful primitive for higher level composition. +// Waits until context deadline or until context is canceled. +// +// Warning: Unless you have a very specific use case (probably a special Watcher) don't use this function!!! +// Warning: This will fail e.g. on API timeouts and/or 'too old resource version' error. +// Warning: You are most probably looking for a function *Until* or *UntilWithSync* below, +// Warning: solving such issues. +// TODO: Consider making this function private to prevent misuse when the other occurrences in our codebase are gone. +func UntilWithoutRetry(ctx context.Context, watcher watch.Interface, conditions ...ConditionFunc) (*watch.Event, error) { + ch := watcher.ResultChan() + defer watcher.Stop() + var lastEvent *watch.Event + for _, condition := range conditions { + // check the next condition against the previous event and short circuit waiting for the next watch + if lastEvent != nil { + done, err := condition(*lastEvent) + if err != nil { + return lastEvent, err + } + if done { + continue + } + } + ConditionSucceeded: + for { + select { + case event, ok := <-ch: + if !ok { + return lastEvent, ErrWatchClosed + } + lastEvent = &event + + done, err := condition(event) + if err != nil { + return lastEvent, err + } + if done { + break ConditionSucceeded + } + + case <-ctx.Done(): + return lastEvent, wait.ErrWaitTimeout + } + } + } + return lastEvent, nil +} + +// Until wraps the watcherClient's watch function with RetryWatcher making sure that watcher gets restarted in case of errors. +// The initialResourceVersion will be given to watch method when first called. It shall not be "" or "0" +// given the underlying WATCH call issues (#74022). If you want the initial list ("", "0") done for you use ListWatchUntil instead. +// Remaining behaviour is identical to function UntilWithoutRetry. (See above.) +// Until can deal with API timeouts and lost connections. +// It guarantees you to see all events and in the order they happened. +// Due to this guarantee there is no way it can deal with 'Resource version too old error'. It will fail in this case. +// (See `UntilWithSync` if you'd prefer to recover from all the errors including RV too old by re-listing +// those items. In normal code you should care about being level driven so you'd not care about not seeing all the edges.) +// The most frequent usage for Until would be a test where you want to verify exact order of events ("edges"). +func Until(ctx context.Context, initialResourceVersion string, watcherClient cache.Watcher, conditions ...ConditionFunc) (*watch.Event, error) { + w, err := NewRetryWatcher(initialResourceVersion, watcherClient) + if err != nil { + return nil, err + } + + return UntilWithoutRetry(ctx, w, conditions...) +} + +// UntilWithSync creates an informer from lw, optionally checks precondition when the store is synced, +// and watches the output until each provided condition succeeds, in a way that is identical +// to function UntilWithoutRetry. (See above.) +// UntilWithSync can deal with all errors like API timeout, lost connections and 'Resource version too old'. +// It is the only function that can recover from 'Resource version too old', Until and UntilWithoutRetry will +// just fail in that case. On the other hand it can't provide you with guarantees as strong as using simple +// Watch method with Until. It can skip some intermediate events in case of watch function failing but it will +// re-list to recover and you always get an event, if there has been a change, after recovery. +// Also with the current implementation based on DeltaFIFO, order of the events you receive is guaranteed only for +// particular object, not between more of them even it's the same resource. +// The most frequent usage would be a command that needs to watch the "state of the world" and should't fail, like: +// waiting for object reaching a state, "small" controllers, ... +func UntilWithSync(ctx context.Context, lw cache.ListerWatcher, objType runtime.Object, precondition PreconditionFunc, conditions ...ConditionFunc) (*watch.Event, error) { + indexer, informer, watcher, done := NewIndexerInformerWatcher(lw, objType) + // We need to wait for the internal informers to fully stop so it's easier to reason about + // and it works with non-thread safe clients. + defer func() { <-done }() + // Proxy watcher can be stopped multiple times so it's fine to use defer here to cover alternative branches and + // let UntilWithoutRetry to stop it + defer watcher.Stop() + + if precondition != nil { + if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced) { + return nil, fmt.Errorf("UntilWithSync: unable to sync caches: %v", ctx.Err()) + } + + done, err := precondition(indexer) + if err != nil { + return nil, err + } + + if done { + return nil, nil + } + } + + return UntilWithoutRetry(ctx, watcher, conditions...) +} + +// ContextWithOptionalTimeout wraps context.WithTimeout and handles infinite timeouts expressed as 0 duration. +func ContextWithOptionalTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + if timeout < 0 { + // This should be handled in validation + klog.Errorf("Timeout for context shall not be negative!") + timeout = 0 + } + + if timeout == 0 { + return context.WithCancel(parent) + } + + return context.WithTimeout(parent, timeout) +} + +// ListWatchUntil first lists objects, converts them into synthetic ADDED events +// and checks conditions for those synthetic events. If the conditions have not been reached so far +// it continues by calling Until which establishes a watch from resourceVersion of the list call +// to evaluate those conditions based on new events. +// ListWatchUntil provides the same guarantees as Until and replaces the old WATCH from RV "" (or "0") +// which was mixing list and watch calls internally and having severe design issues. (see #74022) +// There is no resourceVersion order guarantee for the initial list and those synthetic events. +func ListWatchUntil(ctx context.Context, lw cache.ListerWatcher, conditions ...ConditionFunc) (*watch.Event, error) { + if len(conditions) == 0 { + return nil, nil + } + + list, err := lw.List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + initialItems, err := meta.ExtractList(list) + if err != nil { + return nil, err + } + + // use the initial items as simulated "adds" + var lastEvent *watch.Event + currIndex := 0 + passedConditions := 0 + for _, condition := range conditions { + // check the next condition against the previous event and short circuit waiting for the next watch + if lastEvent != nil { + done, err := condition(*lastEvent) + if err != nil { + return lastEvent, err + } + if done { + passedConditions = passedConditions + 1 + continue + } + } + + ConditionSucceeded: + for currIndex < len(initialItems) { + lastEvent = &watch.Event{Type: watch.Added, Object: initialItems[currIndex]} + currIndex++ + + done, err := condition(*lastEvent) + if err != nil { + return lastEvent, err + } + if done { + passedConditions = passedConditions + 1 + break ConditionSucceeded + } + } + } + if passedConditions == len(conditions) { + return lastEvent, nil + } + remainingConditions := conditions[passedConditions:] + + metaObj, err := meta.ListAccessor(list) + if err != nil { + return nil, err + } + currResourceVersion := metaObj.GetResourceVersion() + + return Until(ctx, currResourceVersion, lw, remainingConditions...) +} diff --git a/vendor/k8s.io/client-go/transport/spdy/spdy.go b/vendor/k8s.io/client-go/transport/spdy/spdy.go new file mode 100644 index 00000000000..53cc7ee18c5 --- /dev/null +++ b/vendor/k8s.io/client-go/transport/spdy/spdy.go @@ -0,0 +1,94 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package spdy + +import ( + "fmt" + "net/http" + "net/url" + + "k8s.io/apimachinery/pkg/util/httpstream" + "k8s.io/apimachinery/pkg/util/httpstream/spdy" + restclient "k8s.io/client-go/rest" +) + +// Upgrader validates a response from the server after a SPDY upgrade. +type Upgrader interface { + // NewConnection validates the response and creates a new Connection. + NewConnection(resp *http.Response) (httpstream.Connection, error) +} + +// RoundTripperFor returns a round tripper and upgrader to use with SPDY. +func RoundTripperFor(config *restclient.Config) (http.RoundTripper, Upgrader, error) { + tlsConfig, err := restclient.TLSConfigFor(config) + if err != nil { + return nil, nil, err + } + upgradeRoundTripper := spdy.NewRoundTripper(tlsConfig, true, false) + wrapper, err := restclient.HTTPWrappersForConfig(config, upgradeRoundTripper) + if err != nil { + return nil, nil, err + } + return wrapper, upgradeRoundTripper, nil +} + +// dialer implements the httpstream.Dialer interface. +type dialer struct { + client *http.Client + upgrader Upgrader + method string + url *url.URL +} + +var _ httpstream.Dialer = &dialer{} + +// NewDialer will create a dialer that connects to the provided URL and upgrades the connection to SPDY. +func NewDialer(upgrader Upgrader, client *http.Client, method string, url *url.URL) httpstream.Dialer { + return &dialer{ + client: client, + upgrader: upgrader, + method: method, + url: url, + } +} + +func (d *dialer) Dial(protocols ...string) (httpstream.Connection, string, error) { + req, err := http.NewRequest(d.method, d.url.String(), nil) + if err != nil { + return nil, "", fmt.Errorf("error creating request: %v", err) + } + return Negotiate(d.upgrader, d.client, req, protocols...) +} + +// Negotiate opens a connection to a remote server and attempts to negotiate +// a SPDY connection. Upon success, it returns the connection and the protocol selected by +// the server. The client transport must use the upgradeRoundTripper - see RoundTripperFor. +func Negotiate(upgrader Upgrader, client *http.Client, req *http.Request, protocols ...string) (httpstream.Connection, string, error) { + for i := range protocols { + req.Header.Add(httpstream.HeaderProtocolVersion, protocols[i]) + } + resp, err := client.Do(req) + if err != nil { + return nil, "", fmt.Errorf("error sending request: %v", err) + } + defer resp.Body.Close() + conn, err := upgrader.NewConnection(resp) + if err != nil { + return nil, "", err + } + return conn, resp.Header.Get(httpstream.HeaderProtocolVersion), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 8ffeb4c6fee..2c5b533808a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,7 +4,6 @@ 4d63.com/tz # cloud.google.com/go v0.51.0 cloud.google.com/go -cloud.google.com/go/civil cloud.google.com/go/compute/metadata cloud.google.com/go/functions/metadata cloud.google.com/go/iam @@ -118,6 +117,8 @@ github.com/akavel/rsrc/ico github.com/andrewkroh/sys/windows/svc/eventlog # github.com/antlr/antlr4 v0.0.0-20200225173536-225249fdaef5 github.com/antlr/antlr4/runtime/Go/antlr +# github.com/armon/go-radix v1.0.0 +github.com/armon/go-radix # github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 # github.com/aws/aws-lambda-go v1.6.0 @@ -318,9 +319,11 @@ github.com/coreos/pkg/dlopen github.com/davecgh/go-spew/spew # github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 github.com/davecgh/go-xdr/xdr2 -# github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f +# github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e github.com/denisenkom/go-mssqldb github.com/denisenkom/go-mssqldb/internal/cp +github.com/denisenkom/go-mssqldb/internal/decimal +github.com/denisenkom/go-mssqldb/internal/querytext # github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 github.com/devigned/tab # github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible @@ -387,6 +390,9 @@ github.com/docker/go-metrics github.com/docker/go-plugins-helpers/sdk # github.com/docker/go-units v0.4.0 github.com/docker/go-units +# github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 +github.com/docker/spdystream +github.com/docker/spdystream/spdy # github.com/dop251/goja v0.0.0-00010101000000-000000000000 => github.com/andrewkroh/goja v0.0.0-20190128172624-dd2ac4456e20 github.com/dop251/goja github.com/dop251/goja/ast @@ -518,6 +524,8 @@ github.com/gogo/protobuf/proto github.com/gogo/protobuf/protoc-gen-gogo/descriptor github.com/gogo/protobuf/sortkeys github.com/gogo/protobuf/types +# github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe +github.com/golang-sql/civil # github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 github.com/golang/groupcache/lru # github.com/golang/protobuf v1.3.2 @@ -738,6 +746,12 @@ github.com/samuel/go-thrift/parser github.com/sanathkr/go-yaml # github.com/sanathkr/yaml v1.0.1-0.20170819201035-0056894fa522 github.com/sanathkr/yaml +# github.com/santhosh-tekuri/jsonschema v1.2.4 +github.com/santhosh-tekuri/jsonschema +github.com/santhosh-tekuri/jsonschema/decoders +github.com/santhosh-tekuri/jsonschema/formats +github.com/santhosh-tekuri/jsonschema/loader +github.com/santhosh-tekuri/jsonschema/mediatypes # github.com/shirou/gopsutil v2.19.11+incompatible github.com/shirou/gopsutil/disk github.com/shirou/gopsutil/internal/common @@ -754,6 +768,7 @@ github.com/stretchr/objx github.com/stretchr/testify/assert github.com/stretchr/testify/mock github.com/stretchr/testify/require +github.com/stretchr/testify/suite # github.com/tsg/go-daemon v0.0.0-20200207173439-e704b93fd89b github.com/tsg/go-daemon # github.com/tsg/gopacket v0.0.0-20190320122513-dd3d0e41124a @@ -813,6 +828,32 @@ github.com/yuin/gopher-lua github.com/yuin/gopher-lua/ast github.com/yuin/gopher-lua/parse github.com/yuin/gopher-lua/pm +# go.elastic.co/apm v1.7.2 +go.elastic.co/apm +go.elastic.co/apm/apmconfig +go.elastic.co/apm/apmtest +go.elastic.co/apm/internal/apmcontext +go.elastic.co/apm/internal/apmhostutil +go.elastic.co/apm/internal/apmhttputil +go.elastic.co/apm/internal/apmlog +go.elastic.co/apm/internal/apmschema +go.elastic.co/apm/internal/apmstrings +go.elastic.co/apm/internal/apmversion +go.elastic.co/apm/internal/configutil +go.elastic.co/apm/internal/iochan +go.elastic.co/apm/internal/pkgerrorsutil +go.elastic.co/apm/internal/ringbuffer +go.elastic.co/apm/internal/wildcard +go.elastic.co/apm/model +go.elastic.co/apm/stacktrace +go.elastic.co/apm/transport +go.elastic.co/apm/transport/transporttest +# go.elastic.co/apm/module/apmelasticsearch v1.7.2 +go.elastic.co/apm/module/apmelasticsearch +# go.elastic.co/apm/module/apmhttp v1.7.2 +go.elastic.co/apm/module/apmhttp +# go.elastic.co/fastjson v1.0.0 +go.elastic.co/fastjson # go.opencensus.io v0.22.2 go.opencensus.io go.opencensus.io/internal @@ -845,9 +886,13 @@ go.uber.org/zap/zapcore go.uber.org/zap/zaptest/observer # golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 golang.org/x/crypto/blake2b +golang.org/x/crypto/blowfish golang.org/x/crypto/cast5 +golang.org/x/crypto/chacha20 +golang.org/x/crypto/curve25519 golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519/internal/edwards25519 +golang.org/x/crypto/internal/subtle golang.org/x/crypto/md4 golang.org/x/crypto/openpgp golang.org/x/crypto/openpgp/armor @@ -858,7 +903,10 @@ golang.org/x/crypto/openpgp/s2k golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pkcs12 golang.org/x/crypto/pkcs12/internal/rc2 +golang.org/x/crypto/poly1305 golang.org/x/crypto/sha3 +golang.org/x/crypto/ssh +golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/terminal # golang.org/x/exp v0.0.0-20191227195350-da58074b4299 golang.org/x/exp/apidiff @@ -1057,6 +1105,8 @@ gopkg.in/inf.v0 gopkg.in/jcmturner/aescts.v1 # gopkg.in/jcmturner/dnsutils.v1 v1.0.1 gopkg.in/jcmturner/dnsutils.v1 +# gopkg.in/jcmturner/goidentity.v3 v3.0.0 +gopkg.in/jcmturner/goidentity.v3 # gopkg.in/jcmturner/gokrb5.v7 v7.3.0 gopkg.in/jcmturner/gokrb5.v7/asn1tools gopkg.in/jcmturner/gokrb5.v7/client @@ -1087,6 +1137,8 @@ gopkg.in/jcmturner/gokrb5.v7/keytab gopkg.in/jcmturner/gokrb5.v7/krberror gopkg.in/jcmturner/gokrb5.v7/messages gopkg.in/jcmturner/gokrb5.v7/pac +gopkg.in/jcmturner/gokrb5.v7/service +gopkg.in/jcmturner/gokrb5.v7/spnego gopkg.in/jcmturner/gokrb5.v7/types # gopkg.in/jcmturner/rpc.v1 v1.1.0 gopkg.in/jcmturner/rpc.v1/mstypes @@ -1191,6 +1243,8 @@ k8s.io/apimachinery/pkg/util/clock k8s.io/apimachinery/pkg/util/diff k8s.io/apimachinery/pkg/util/errors k8s.io/apimachinery/pkg/util/framer +k8s.io/apimachinery/pkg/util/httpstream +k8s.io/apimachinery/pkg/util/httpstream/spdy k8s.io/apimachinery/pkg/util/intstr k8s.io/apimachinery/pkg/util/json k8s.io/apimachinery/pkg/util/naming @@ -1203,6 +1257,7 @@ k8s.io/apimachinery/pkg/util/wait k8s.io/apimachinery/pkg/util/yaml k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch +k8s.io/apimachinery/third_party/forked/golang/netutil k8s.io/apimachinery/third_party/forked/golang/reflect # k8s.io/client-go v0.0.0-20190620085101-78d2af792bab k8s.io/client-go/discovery @@ -1259,8 +1314,11 @@ k8s.io/client-go/tools/clientcmd/api/latest k8s.io/client-go/tools/clientcmd/api/v1 k8s.io/client-go/tools/metrics k8s.io/client-go/tools/pager +k8s.io/client-go/tools/portforward k8s.io/client-go/tools/reference +k8s.io/client-go/tools/watch k8s.io/client-go/transport +k8s.io/client-go/transport/spdy k8s.io/client-go/util/cert k8s.io/client-go/util/connrotation k8s.io/client-go/util/flowcontrol diff --git a/winlogbeat/_meta/beat.reference.yml b/winlogbeat/_meta/beat.reference.yml deleted file mode 100644 index c3f14bd412c..00000000000 --- a/winlogbeat/_meta/beat.reference.yml +++ /dev/null @@ -1,34 +0,0 @@ -########################## Winlogbeat Configuration ########################### - -# This file is a full configuration example documenting all non-deprecated -# options in comments. For a shorter configuration example, that contains only -# the most common options, please see winlogbeat.yml in the same directory. -# -# You can find the full configuration reference here: -# https://www.elastic.co/guide/en/beats/winlogbeat/index.html - -#======================= Winlogbeat specific options ========================== - -# The registry file is where Winlogbeat persists its state so that the beat -# can resume after shutdown or an outage. The default is .winlogbeat.yml -# in the directory in which it was started. -#winlogbeat.registry_file: .winlogbeat.yml - -# The maximum amount of time Winlogbeat should wait for events to finish -# publishing when shutting down. -#winlogbeat.shutdown_timeout: 0s - -# event_logs specifies a list of event logs to monitor as well as any -# accompanying options. The YAML data type of event_logs is a list of -# dictionaries. -# -# The supported keys are name (required), tags, fields, fields_under_root, -# forwarded, ignore_older, level, no_more_events, event_id, provider, and -# include_xml. Please visit the documentation for the complete details of each -# option. -# https://go.es.io/WinlogbeatConfig -winlogbeat.event_logs: - - name: Application - ignore_older: 72h - - name: Security - - name: System diff --git a/winlogbeat/_meta/beat.yml.tmpl b/winlogbeat/_meta/beat.yml.tmpl deleted file mode 100644 index 24b27d36648..00000000000 --- a/winlogbeat/_meta/beat.yml.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{ template "header" . }} -winlogbeat.event_logs: - - name: Application - ignore_older: 72h -{{if .Reference}} - # Set to true to publish fields with null values in events. - #keep_null: false -{{end}} - - name: System -{{if .Reference}} - # Set to true to publish fields with null values in events. - #keep_null: false -{{end}} - - name: Security -{{if .Reference}} - # Set to true to publish fields with null values in events. - #keep_null: false -{{end}} -{{if not .Reference}}{{ template "elasticsearch_settings" . }}{{end}} diff --git a/winlogbeat/_meta/config/beat.reference.yml.tmpl b/winlogbeat/_meta/config/beat.reference.yml.tmpl new file mode 100644 index 00000000000..e84dd482b55 --- /dev/null +++ b/winlogbeat/_meta/config/beat.reference.yml.tmpl @@ -0,0 +1,2 @@ +{{template "header.yml.tmpl" .}} +{{template "winlogbeat.event_logs.yml.tmpl" .}} diff --git a/winlogbeat/_meta/config/beat.yml.tmpl b/winlogbeat/_meta/config/beat.yml.tmpl new file mode 100644 index 00000000000..39cc766b2b3 --- /dev/null +++ b/winlogbeat/_meta/config/beat.yml.tmpl @@ -0,0 +1,3 @@ +{{template "header.yml.tmpl" .}} +{{template "winlogbeat.event_logs.yml.tmpl" .}} +{{template "setup.template.yml.tmpl" .}} diff --git a/winlogbeat/_meta/common.yml.tmpl b/winlogbeat/_meta/config/header.yml.tmpl similarity index 73% rename from winlogbeat/_meta/common.yml.tmpl rename to winlogbeat/_meta/config/header.yml.tmpl index 7a7feaddf5a..7ace0063a18 100644 --- a/winlogbeat/_meta/common.yml.tmpl +++ b/winlogbeat/_meta/config/header.yml.tmpl @@ -1,4 +1,3 @@ -{{define "header" -}} ###################### Winlogbeat Configuration Example ######################## # This file is an example configuration file highlighting only the most common @@ -8,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/winlogbeat/index.html -#======================= Winlogbeat specific options =========================== +{{header "Winlogbeat specific options"}} {{if .Reference -}} # The registry file is where Winlogbeat persists its state so that the beat can @@ -24,13 +23,4 @@ # The supported keys are name (required), tags, fields, fields_under_root, # forwarded, ignore_older, level, event_id, provider, and include_xml. Please # visit the documentation for the complete details of each option. -# https://go.es.io/WinlogbeatConfig{{end -}} - -{{define "elasticsearch_settings" -}} -#==================== Elasticsearch template settings ========================== - -setup.template.settings: - index.number_of_shards: 1 - #index.codec: best_compression - #_source.enabled: false -{{end -}} +# https://go.es.io/WinlogbeatConfig diff --git a/winlogbeat/_meta/config/processors.yml.tmpl b/winlogbeat/_meta/config/processors.yml.tmpl new file mode 100644 index 00000000000..4642130fe8b --- /dev/null +++ b/winlogbeat/_meta/config/processors.yml.tmpl @@ -0,0 +1,5 @@ +{{header "Processors"}} +processors: + - add_host_metadata: + when.not.contains.tags: forwarded + - add_cloud_metadata: ~ diff --git a/winlogbeat/_meta/config/setup.template.yml.tmpl b/winlogbeat/_meta/config/setup.template.yml.tmpl new file mode 100644 index 00000000000..4eb23e9da92 --- /dev/null +++ b/winlogbeat/_meta/config/setup.template.yml.tmpl @@ -0,0 +1,6 @@ +{{header "Elasticsearch template settings"}} + +setup.template.settings: + index.number_of_shards: 1 + #index.codec: best_compression + #_source.enabled: false diff --git a/winlogbeat/_meta/config/winlogbeat.event_logs.yml.tmpl b/winlogbeat/_meta/config/winlogbeat.event_logs.yml.tmpl new file mode 100644 index 00000000000..64a01bb8352 --- /dev/null +++ b/winlogbeat/_meta/config/winlogbeat.event_logs.yml.tmpl @@ -0,0 +1,10 @@ +winlogbeat.event_logs: + - name: Application + ignore_older: 72h + + - name: System + + - name: Security + + - name: ForwardedEvents + tags: [forwarded] diff --git a/winlogbeat/cmd/root.go b/winlogbeat/cmd/root.go index 7075a51aeb0..ecc3aa8e38f 100644 --- a/winlogbeat/cmd/root.go +++ b/winlogbeat/cmd/root.go @@ -20,6 +20,7 @@ package cmd import ( "github.com/elastic/beats/v7/libbeat/cmd" "github.com/elastic/beats/v7/libbeat/cmd/instance" + "github.com/elastic/beats/v7/libbeat/publisher/processing" "github.com/elastic/beats/v7/winlogbeat/beater" // Register fields. @@ -35,4 +36,8 @@ import ( var Name = "winlogbeat" // RootCmd to handle beats cli -var RootCmd = cmd.GenRootCmdWithSettings(beater.New, instance.Settings{Name: Name, HasDashboards: true}) +var RootCmd = cmd.GenRootCmdWithSettings(beater.New, instance.Settings{ + Name: Name, + HasDashboards: true, + Processing: processing.MakeDefaultSupport(true, processing.WithECS, processing.WithAgentMeta()), +}) diff --git a/winlogbeat/docs/fields.asciidoc b/winlogbeat/docs/fields.asciidoc index ba31d833443..3763ebc12db 100644 --- a/winlogbeat/docs/fields.asciidoc +++ b/winlogbeat/docs/fields.asciidoc @@ -36,7 +36,8 @@ Contains common beat fields available in all event types. *`agent.hostname`*:: + -- -Hostname of the agent. +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. + type: keyword diff --git a/winlogbeat/docs/modules.asciidoc b/winlogbeat/docs/modules.asciidoc index a48f0e4ed11..f8d57950a12 100644 --- a/winlogbeat/docs/modules.asciidoc +++ b/winlogbeat/docs/modules.asciidoc @@ -35,16 +35,16 @@ winlogbeat.event_logs: - name: ForwardedEvents tags: [forwarded] processors: - - script: - when.equals.winlog.channel: Security - lang: javascript - id: security - file: ${path.home}/module/security/config/winlogbeat-security.js - - script: - when.equals.winlog.channel: Microsoft-Windows-Sysmon/Operational - lang: javascript - id: sysmon - file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js + - script: + when.equals.winlog.channel: Security + lang: javascript + id: security + file: ${path.home}/module/security/config/winlogbeat-security.js + - script: + when.equals.winlog.channel: Microsoft-Windows-Sysmon/Operational + lang: javascript + id: sysmon + file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js ---- [float] diff --git a/winlogbeat/docs/modules/security.asciidoc b/winlogbeat/docs/modules/security.asciidoc index c7e7415244e..30d2d04fe3a 100644 --- a/winlogbeat/docs/modules/security.asciidoc +++ b/winlogbeat/docs/modules/security.asciidoc @@ -19,8 +19,16 @@ The module has transformations for the following event IDs: * 4647 - User initiated logoff (interactive logon types). * 4648 - A logon was attempted using explicit credentials. * 4672 - Special privileges assigned to new logon. +* 4673 - A privileged service was called. +* 4674 - An operation was attempted on a privileged object. * 4688 - A new process has been created. * 4689 - A process has exited. +* 4697 - A service was installed in the system. +* 4698 - A scheduled task was created. +* 4699 - A scheduled task was deleted. +* 4700 - A scheduled task was enabled. +* 4701 - A scheduled task was disabled. +* 4702 - A scheduled task was updated. * 4719 - System audit policy was changed. * 4720 - A user account was created. * 4722 - A user account was enabled. @@ -32,7 +40,7 @@ The module has transformations for the following event IDs: * 4728 - A member was added to a security-enabled global group. * 4729 - A member was removed from a security-enabled global group. * 4730 - A security-enabled global group was deleted. -* 4731 - A security-enabled local group was created +* 4731 - A security-enabled local group was created. * 4732 - A member was added to a security-enabled local group. * 4733 - A member was removed from a security-enabled local group. * 4734 - A security-enabled local group was deleted. @@ -65,9 +73,41 @@ The module has transformations for the following event IDs: * 4763 - A security-disabled global group was deleted. * 4764 - A group's type was changed. * 4767 - An account was unlocked. +* 4741 - A computer account was created. +* 4742 - A computer account was changed. +* 4743 - A computer account was deleted. +* 4744 - A security-disabled local group was created. +* 4745 - A security-disabled local group was changed. +* 4746 - A member was added to a security-disabled local group. +* 4747 - A member was removed from a security-disabled local group. +* 4748 - A security-disabled local group was deleted. +* 4749 - A security-disabled global group was created. +* 4750 - A security-disabled global group was changed. +* 4751 - A member was added to a security-disabled global group. +* 4752 - A member was removed from a security-disabled global group. +* 4753 - A security-disabled global group was deleted. +* 4754 - A security-enabled universal group was created. +* 4755 - A security-enabled universal group was changed. +* 4756 - A member was added to a security-enabled universal group. +* 4757 - A member was removed from a security-enabled universal group. +* 4758 - A security-enabled universal group was deleted. +* 4759 - A security-disabled universal group was created. +* 4760 - A security-disabled universal group was changed. +* 4761 - A member was added to a security-disabled universal group. +* 4762 - A member was removed from a security-disabled universal group. +* 4763 - A security-disabled global group was deleted. +* 4764 - A group's type was changed. +* 4768 - A Kerberos authentication ticket TGT was requested. +* 4769 - A Kerberos service ticket was requested. +* 4770 - A Kerberos service ticket was renewed. +* 4771 - Kerberos pre-authentication failed. +* 4776 - The computer attempted to validate the credentials for an account. +* 4778 - A session was reconnected to a Window Station. +* 4779 - A session was disconnected from a Window Station. * 4781 - The name of an account was changed. * 4798 - A user's local group membership was enumerated. * 4799 - A security-enabled local group membership was enumerated. +* 4964 - Special groups have been assigned to a new logon. More event IDs will be added. diff --git a/winlogbeat/include/fields.go b/winlogbeat/include/fields.go index 896d4ad5d5a..40c4454d9f2 100644 --- a/winlogbeat/include/fields.go +++ b/winlogbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetBuildFieldsFieldsCommonYml returns asset data. // This is the base64 encoded gzipped contents of build/fields/fields.common.yml. func AssetBuildFieldsFieldsCommonYml() string { - return "eJzsvXtTHLmSOPr/fApdNuKHOdsUD4ONuXcjfgwwM8TamDH4zJ5Zb9DqKnW3DlVSjaQC92zsd7+hTEmlegCNTfkxy5zdGbq7SkqlUql857+Q3w7enZ6c/vz/kCNJhDSEZdwQM+eaTHnOSMYVS02+GBFuyA3VZMYEU9SwjEwWxMwZOT48J6WS/2SpGf3wL2RCNcuIFPD9NVOaS0G2kt1kM/nhX8hZzqhm5JprbsjcmFLvb2zMuJlXkySVxQbLqTY83WCpJkYSXc1mTBuSzqmYMfjKDjvlLM908sMP6+SKLfYJS/UPhBhucrZvH/iBkIzpVPHScCngK/KTe4e4t/d/IGSdCFqwfbL6fw0vmDa0KFd/IISQnF2zfJ+kUjH4rNgfFVcs2ydGVfiVWZRsn2TU4MfGfKtH1LANOya5mTMBaGLXTBgiFZ9xYdGX/ADvEXJhcc01PJSF99hHo2hq0TxVsqhHGNmJeUrzfEEUKxXTTBguZjCRG7GernfDtKxUysL8J9PoBfyNzKkmQnpocxLQM0LSuKZ5xQDoAEwpyyq307hh3WRTrrSB91tgKZYyfl1DVfKS5VzUcL1zOMf9IlOpCM1zHEEnuE/sIy1Ku+mr25tbL9Y3d9e3n19s7u1v7u4/30n2dp//vhptc04nLNe9G4y7KSeWiuEL/PMSv79iixupsp6NPqy0kYV9YANxUlKudFjDIRVkwkhlj4SRhGYZKZihhIupVAW1g9jv3ZrI+VxWeQbHMJXCUC6IYNpuHYID5Gv/Ochz3ANNqGJEG2kRRbWHNABw7BE0zmR6xdSYUJGR8dWeHjt0dDD53yu0LHOeAnQr+2RlKuX6hKqVEVlh4tp+UyqZVSn8/j8xggumNZ2xOzBs2EfTg8afpCK5nDlEAD24sdzuO3TgT/ZJ9/OIyNLwgv8Z6M7SyTVnN/ZMcEEoPG2/YCpgxU6njapSU1m85XKmyQ03c1kZQkVN9g0YRkSaOVOOfZAUtzaVIqWGiYjyjbRAFISSeVVQsa4YzegkZ0RXRUHVgsjoxMXHsKhyw8s8rF0T9pFre+TnbFFPWEy4YBnhwkgiRXi6vZG/sDyX5Dep8izaIkNnd52AmNL5TEjFLulEXrN9srW5vdPduddcG7se954OpG7ojDCazv0qmzT2nzEJIV1tr/xXTEp0xgRSimPrB+GLmZJVuU+2e+joYs7wzbBL7hg55koJndhNRjY4NTf29FgGauwFN3VbQcXC4pzaU5jn9tyNSMYM/iEVkRPN1LXdHiRXaclsLu1OSUUMvWKaFIzqSrHCPuCGDY+1T6cmXKR5lTHyI6OWD8BaNSnogtBcS6IqYd928yqdwI0GC03+5pbqhtRzyyQnrObHQNkWfspz7WkPkaQqIew5kYggC1u0PuWGvJkzFXPvOS1LZinQLhZOalgqcHaLAOGocSqlEdLYPfeL3ScnOF1qJQE5xUXDubUHcVTDl1hSIE4SmTBqkuj8Hpy9AZnE3ZzNBbkdp2W5YZfCU5aQmjZi7ptJ5lEHbBcEDcKnSC1cE3u/EjNXsprNyR8Vq+z4eqENKzTJ+RUj/06nV3RE3rGMI32USqZMay5mflPc47pK55ZLv5YzbaieE1wHOQd0O5ThQQQiRxQGcaU+Haycs4Ipml9yz3XceWYfDRNZzYs6p/rWc90+S8d+DsIze0SmnCkkH64dIp/xKXAgYFN6LdC1F2rsVaYKEA+8BEdTJbW9/bWhyp6nSWXIGLebZ2PYD7sTDhkR09ijO9Pdzc1pAxHt5Qd29llLfy/4H1a+efi6w31rSRQJG967gYt9wgiQMc9uXV7WWJ799xALdGILnK+YI3R2UBOKTyE7xCtoxq8ZyC1UuNfwaffznOXltMrtIbKH2q0wDGxuJPnJHWjChTZUpE6OafEjbScGpmSJxF2npL5OWUkVnOIwNtdEMJahAnIz5+m8O1U42aks7GRWvo7WfTK1kq/nPLBUZEn+Kzk1TJCcTQ1hRWkW3a2cStnYRbtRQ+zixaK8Y/s8t7MTEG3oQhOa39j/BNxaWVDPPWnitjpxHN+1t3lSo0YEnh2wWj+LJO6mmLD6EbjC+LSx8fWOtQmgsfkFTedWJ+iiOB7H49lpmwOg+u9Oj20iuwXTi2Qz2VxX6XYsxuiGDFMZKWQhK03O4Uq4R545EITWr+AtQp4dnK/hwXTSiQMslUIw0BhPhGFKMEPOlDQylbmD9NnJ2RpRsgJ9sVRsyj8yTSqRMbzIrbCkZG4Hs9xNKlJIxYhg5kaqKyJLq0dKZQUer+SxOc2n9gVK7H2XM0KzgguujT2Z1164smNlskBJjBri9FZcRFFIMSJpzqjKFwH7UxByA7Qy5+kCBMs5s6IvLDBZ+sIUVTEJAs1dV2Uuw63d2Ap3JeA4VhGVKQhXDqLONjl5I3wdCN7tohvo2cH56RqpYPB8Ud84GoXngHo8EyeNdUekt7W79eJVY8FSzajgfwJ7TLrXyOeICaCmXMZYjlid1+9IV+UjIGOpQu+TKc11fSNkbEqr3OCQzR8be/A2WhPM18HDz1JaGnz9+jA6g2nOW7rEYf3NHcrEgXvTHjZPj1Q7AuSG27OApO+3yR1BC95UempzSoJiM6oyEB6tbCiFHkXPo+A44Whu49Jqn9Nc3hDFUqtXNVTXi8MzNyreTDWYHdjsF/bxCDI4gJqJoDLYZ87/cUpKml4x80yvJTALarulYyGdqdCsZEW7xqRe11FgM2PawuGkcY8lo6jQFIBJyLksWJCPK416hmGqICveVibVSq1ZKzb13MqBIloL1Hj03M9OD8SdnbCgB4EeGCHAHUsLlpj5ba6niOFHjdYRkZ/A3l6VrixC3Ki1AsaFBe+flcANAH0MNSxvyewZrMavkKYzpBWscL/W4UR7E1IwPOF4G36eYCqEw4OiGs0yollBheEp8H720Tipjn1EeX2EQpTnCDrIdkaSa26Xy/9ktXJtF8oUKNyam4q67TiZkoWsVJhjSvPcE5+/ESw3nUm1GNlHvVCiDc9zwoRVLx3don3SCi4Z08aSh0WpRdiU53lgaLQslSwVp4bliwcoVjTLFNN6KJ0KqB21aEdbbkIn/wQ2U0z4rJKVzhdIzfBOYJg3Fi1aFgzssiTnGuxWJ2cjQv09KxWh9mL5SLS0dJIQ8o8as05MA8Nhza/njCh642HydD9O3BdjRFlTyhRWCa+FyKxC2yFejeOEl2MLyjhBsMYjkrGSicyJ+SijS1EDASq927Faikr+113gVCdPd3gE1WRhmL5HtI/2Hi08zdcagPxof0DrTvCwuDPpSAJZZ3er9nYagCFhD6B0OB6O4yeNOWdMJik3i8uBDASHVmbv3Z03VkdgNO+CI4XhggkzFEynkbEiTNaB71QqMycHBVM8pT1AVsKoxSXX8jKV2SCowynIyflbYqfoQHh4cCtYQ+2mA6l3Qw+poFkXU8Ae71emZ0xelpKHu6npHJBixk2V4X2dUwMfOhCs/jdZycHVtP7yefJia2fv+eaIrOTUrOyTnd1kd3P31dYe+Z/VDpCPyxNbNkDN1Lq/j6OfUOL36BkRZwNBKUxOyUxRUeVUcbOIL9YFSe0FD2JndIEe+nszWJiQwrlCiSpl9sZwwvc0l1K5i2cEFpU5r0Xb+oZC8HJSzhea2z+8hyP1x1pHIJxKE7lxwX/D0e5QwAU5Y9KvtmuHmUhtpFjP0s7eKDbjUgx50t7BDHcdtPVfD2+Da6Cj5mDqPWm/VmzCmoji5T0whAeaxHlyFoQ0zxHhsogpC42x3pDjXYsnZ9c79ouTs+sXtfDZkrcKmg6AmzcHh7dBTRo2b5O08dJ7rG/BzYVVL1FLOjmzEzmdAQNTTg8uggJOnrFkljhrEs1jQwFBbdMbmhqujXBWIp3TKrVgfhQzkkuakQnNqUjh6E65YjdW5QEdX8nKnugWxu2iS6nMwwRcL+Roo3i/1Btjw47/veADddsHyHuNVZ/h258k3W034ejsyTJC5+37ceb24Dbit9xJG6ZYdtknVz7e9WaVmzmfzZk20aQeRzj3CBZSlizzIOtq4sXRsP8/1T4evKai4ZwuOpUKwkiSGcj2SSqLFcI1WYk+t11PGE7jXEoZM0wVcBWXiqVcW10L7CgUtV9wxEIYUTXJeUp0NZ3yj2FEeObZ3Jhyf2MDH8EnrI61lpALtbCUaiQaDj5ye/Xh9TpZEM2LMl8QQ6/qXUVtOafagF8DY2lQMRfSEFD6bliew9ovXh/Vzt+VVCbV1Ur3Lq2R0SAJI8tL2P4vQBFsOrUH+JrZWZ1M4/bwGbt4fbQ2Qm/OlZA3wlvJGmARh/qRN0cCikpak70bD67ILvG05w3DWjzWGALq+b7JBkjmNoqpN2I52oHvG2RTaaaSYSkm1sjQcC0VmoPt5OijKhiYSeT0No5BBXl9dHAGoRC44qMwVEwqq93VsYLyfKDFWfGfwAReZkm6AEyrPO+RJL9Lw4xd8KomdkkwHSgY9JrynE7yrjB7kE+YMuSYC22YI7EGbsDO+tUIEGYfngJxkYPF4HTjUKYu5grX513lYJHcKHNqrATSQ6gI54DqcrwTOFkXiDnV88G0dcQU8B07j+XJqVSKWdG3EfA1RcM4MChBqJBiEYePohAXkcp7zVwwyxhWwTM0aMMHu7pxCDJMpZjiXtG8MScVmb2SakcO8VHBfUQ1SExTh5SCDgZzdqF4PAX5q7G087mVttGqAsGFXHQXHfE0Cjyt4TmWFS4vOI79F7f7jTHRgCDpBf8CDEXAGTpVNAQf12GV6ADCmCSvTkBkErk1jHJK3jCjeIrhTToOn6KCHB9uY/CUpb4pM+mcaTAqRaMTbrSLXK2BtJTbDLhuRM5yHcJymiC4cVUlXEisYoU0IYiHyMponrFopjZkCBMlLmbTL8gTmKhfdQaxZmw4DloPBMGpbnKv8tlhua5BdQh7iIswBXPtcFx/9aJGEM4FQbmx44RnIdDanegFyfh0ylSssIPZj0N4sb0H7TFcN0xQYQgT11xJUTRtRjVtHfx2Hibn2cg7ZYD+ydt3P5OTDEOhIUigajOXroD64sWLly9f7u3tvXrV8nOhiMFzbhaXf9aewMfG6kE0D7HzWKyg+xFoGo5KfYg6zKHS64xqs77VsuC5+LXhyOHExy2eHHnuBbD6Q9gGlK9vbT/f2X3xcu/VJp2kGZtu9kM8oDgQYI4jTLtQR/ZG+LIbKPloEL3xfCCKmbwTjWY7KVjGq6YyXip5zbOlHNGf7eOCs+YnTPzhjPN+6I0eEfpnpdiIzNJyFA6yVCTjM25oLlNGRfemu9GNZaFRfKBFOZv4Jx63+DqWGbvUfCaovTob97LMGDlv/HL7BX0xZ5q1E0Qa4hrcdBMuqFrApCRMqpcPOcTg8HtEqImUOaOiD20/4k8gydIShAWOcZYOFos+F9XT9akZVbHVMOwt8pIHVRtqqsGCXg6yjLuQti6WgdKZstdGakV1BKUnDr1COdyliczstZ2qRWnkTNFyzlPClJIK87g6o17TnGexR86qUarSxs9HXjN6zUgloqgtPIb+1foVfz7r8cOwN1STSqRzll6xnhj/43fv3r67fH968e79+cXx0eW7t28vlt6jCjMSB3JcnePwDYYdSD/wuzoMgKdKajk15FCqUjbC8O9dCqCRLXNf3nE8Vs+NVAzl03gre7aHpPOmyfrvdk8pRPrVr9/2HqRhYeKdD20ageRq+VitNYIo6uKgpMgXzRysyYIYKXONUWwUzAyQFcPSK5RNkQ47JPOwgwzE+pl47ec7aGKBK6XJga6ZsiJfRujMCuGRNjdnNQ8Vpilp9h432kD+PWdpGcTUFwcweUfG4c6Iv7wjDjg82Iz1dFGYnXzeKMOwZKldjQMyQIFE4Ozjzhsnp/EgUXJ4dFfNWV5GVg1QdNCLF4bWToUSC3uzGh7MVsvcWEMaHurF86wp/PGCzgYVRmOhCiYLIUQIkCW0ScVzY/XAHtAMnQ0EWU1ZDi46a5mZo5T1u6ePUtfvSF5vi+kwq8sDb8w74HbUi66jJIIcijQ7lCCKo5OCCjpD5s91TQgdIQpT5iM+EoUcx5zkqPX1HbwkevTu0HRkuNHTEHaEbvGNZuZ4z5hRNPp9cejIflwc+rcYKN2I814qWjrcMq7axCNFS4dhIWr6KVr6KVr6f3e0dHwwfVCNKy3T3q8vFTIds8KnuOmnuOnHAekpbnp5nD3FTT/FTX9PcdPRJfa9BU83QCfDRFDz0s4W3/T3hA2zRrxwqfg1NYwcvfl9rS9iGE4N6CHfVNA0ROlGxhm3UjDZ1LgxkkwWgIkjBiWGHn+FQ4RBP0Bs+3Kx0LfS8tcOiM46EuVTVPRTVPRTVPRTVPRTVPRTVHSb4J6iop+iop+iop+ior9llvbZUdFZjteL9369fg0f7y7Lu0zEFcSb5HyiqOJMk2whaIFqlEe5pJmvfOyKrIJJxv38hoqFq1IXF2l1JaMkWdFzCkmOjXlWXIFcHz6Lhh4fSzepQjV8CPBgBseDWvQ0zz3qpjLP5Q0Xs30Pzd/IES5gPefiys23IM/GSZbn4zVX+M6riFKQ37jI5I2u3z9HcN9iZM6zcaJl33vvBf+4DjJbZ+0dWBpgLHI+6RuwoOnb8+Vdgc2wvOQ7intrQf4UBvfth8G1t+yvExXXWtlTkNxQQXItRD/FzN2CJysxJkW2OxBDfHO0i1M8CB49p1sDAXT+y8HWp0G0vftiOJi2d198GlS7zn47CFS7W9sPg2ogDt3Qdp1w074261KaBS21N3rHPB1aHUlBMq6vusfmiinB8ufbiZd8l1huSc1Qat1PVZ4jxHaSztpbwB/uf3CC5QesOf18+8MnLQgsjCUVi4GWdRLKzuA0nQ0a+WSYjEBrjqLkOVuHGNdHvYhLlkSADb3alov8ExZ7RuM4gvsXZ4e/7K2V/viru24WTn/gyl4kz5NXLzY3k62XO1u7D1ii7+BzCWsdNNHNLfRziPX87ODk9CI5/o/jByzRNdAZel1ums9Z30o4jR8+Hhx7NRf+fhsUVuRNK3cjIFggRKOs/tHp+X0WiJ8asbZ2wqPTc/JHxcDSYAVVKvQNi1p32d9dYrYTWBmHZNdQSrmuee/HWpBScQm2hhkzWEkah3WDPhtnQkOa4z48P15zTXQWfpJ4dLA6+1LMaC6r2xm5EXHaEDqs0VlCdWybcDCgWH3DFKv3Di2nXOM4XSjx1fHaQyKDGyt+9Jj11QNBqFJ04ZGBWHbvo5uIpnMHBtGu6rliplIiMmj6ZniuDFgkMTAC1u0rtnAoq+N1/d7gFmjm+7I1wpEnC3J8eF63zXiHJdxxrLmV4aGtQmwEKOrl4I9+ckFu7FvHh+du+HYEkt1mS34Q9YR+fOxaAr80Q8rtc57MyYEhBRe8qIqR+7K2CrhFFVbjiztoje0sYwscpP53lsF17RsZWWErDEntaCkIK9z4No5Uk1JqzSfob8igIrm9+WltKnFGQx933A8o1STFjjaNOPYWRSZpTgeLWMecfYrROWFDfG5BhhTDofERxpRgYf8Oszw57QU9qtswiIsboI24I0YstDpFusPBKBZN8HF0+GrJRKa97wWyrIFheZTEA/q1dwTtrc3E/18vFoaMW7xoOuEtxUXpyi3QSYll7nWzcRB1xhA5JYenB2+O7YGYMIss+35+zbJRzJxWVzUZo7OkZjEmyl+QwjdekkoxXUqL4mDZiwaBc5mQk8CrhDTe094e0zc3HEN7Bh8sP7Y3D4PGpJ1tubm5SW4Jw/A7Y8wyLufbApUs7iEzB2LIrsFCajk3rBcQ0LsJ3uZE03nM2NkU+FIjz4LrlKqMZQn5nSnpc+gLsNnMXSgqstAaf5MaaThFT1x7P50OWMfgYl7XMPhEFgOk2bQYMJoxdTnNfXPIIczfcGfLKdkmOTOGKeCSODOBmRuFSEpsZVQXO9gnBwcjcnE4Iu+ORuTdwYgcHI3I4dGIHL3tkKz7uE7eHdV/NuPHB3NP2x2yS8PYvdhNTTWYjeuWt0rOFC2QAkOb3oAE+wiIZZhcEw0EWWslr/NxkDnoHg1qe2trq7FuWfbEFT/64p0nSgo0l6MYhemwzhx9xQUE0KEA25BpSWhpGkcvQS9G43FXN4fBwHIcBmVkwAw4CeMxb8XRr++P3/2jgaPAGb+YxODa/LjbAvWSe4WDBgMf8l6EC7EFWnzvBXNaqyCTkGK9VFwY6NeXzim0tFaaPJuwXN6Q59uQeGchIFvbL9ZGEe1L3Xij5uVBQ8J2TEyntLRnimpGtjbhCpnBHB+Ojo7WajH8R5peEZ1TPXca3x+VhKSmMLIbKiEXdKJHJKVKcTpjTnfQKKPmPEq/mzKWxSOkUlwz5YKDP5gR+aDwrQ8C6I85n8aD7tiwzV89FvYp/vWbiX8NRBGQPyQxhElAxastC26BdQvBDol2GYUbaA4qoUusAKCBEYaZRjVqdDXZtuvcShxWgDRGDZzXEDacjF57rcdYGSGJCEmMojyH7oJMcdkv+PYj/Sn6GNnfU/Txg6KPa/r5MgqC05PuFioODg6akrHXVS8/J4fooGOiy3NycmZlOAa1wMaxaWPcsjH4H8fe1Odoh0+nPK1ysCBVmo3IhKW00sEyfU0VZ2bhlaOYUAtqtFUK7VAOrIQcfzTKt/wD+KIKAx5Qg+3PJQGraISccS2uQst3boI5C3slZOyjfbuwVBIPjSIBvgS/M6o5hKiFEevmeiipWOF2Krt1FYN20zadNL/bam8wSMJfQhHwc/WnGp6+hVigBnQDno3V+HAEA78P2chGDtFWJgX6a15e0MOwLtcTOQgglGXGr5mG7oWRa6HRzhAeSxWLQ6UyocMoU4St7SNYFooaAG/wd+6ABhCt+aGNOWChZMqt/5ks0fqaL+wQWspwrzhtDU/HWkIORAb1WlMpasXVYbV59m93VHh7vtXjHE/o8NJg+A3V9dKGC+j48D4X0Btm6HpsrPbVmZw1evnCfve1mVbsj4orlkGhs0eIcDg+PA9+VLjHAn7tYjQxMiFjlurEPTTGCH8PRs0EQTAC1lNpg/UJIdo777QPJeS3ORO4Z7CB2LU/yGtcZDxlmqyvOyOpc2BYgCw+dc5nc5P3FaWNVgPvR8G1ObMs2upvyrUppdk/Lag+TTGds4K28E8873dL6BqVk81kM6YcpWSjENhx+GLpEGZoQ++dQS7iEsh3AXaNgMf32NC2QPkBn3NuoLJkUNAlZ1gC2aLZMwIIwk+pvYVu8PYJdgzce240y6e1ok0Fjv4AN91AyeWATDT6tNwJCOCdNrhhYvpDekgPBM7QdA8YUfB9z2K9saoxsDY0vbq00sVfIQ3qAoMvU2jenLLg+wGMWmItc/ARso+tfkZfSNANuzvCk+ZK5ZpgYovDF9jHlJV1pnHEKv5Jr2mSUzFLTqs8P5Pgjjj2j8c85LrVUfz4eomG4qGRb28hQd8duT84PJdeXcGag4qnDV4QWM6BfbTVstyyh/ad7G9iaAhWMDPHcxp4U60pvJaBM8HFwUWaV66OO3htqAmuMtC0xKweI9QUtxPVi3Dj+aGoT+ewVKaML2LvStPXDdadTR0VmpDW7sb0/m/Q/eLE7RGW9+rp0j5h5saK+TS0Y3byjLoObmaczDU4Z1DDP82ltms78DtxP7qxlIQ/x1JBbS0otpOTglFdKVZgFwAImu7DbPQYBPoaesUCDcdojsmjxnHBCgkRKkxDP203XFZj2rXVvuaBZxlWgCG/Uiwh5wz3fIzl5+xFN8Zlc+MKPANT0HUL/MiTH45wHJHgILXzamP19MYlvlw1/iWq7XyyroCjBwXBOx+a9feclSPUk8FCk3FYhIjeIidQ+hNIoBZB51R4vPpO6OPadB021zKMMSBknWbZeETG7tysw7lh8NWU52wdxfxsjL4j70Fp3AYg30dBK1gfs8yBwvpq+FeaqfWSam2RuY5hSU2ZwoE+zHZgAgwcpCmZWjXIypKHOKcvkoaBXqhhg5RKDe5IbQsDZcUZtNzW2IE88GTOmaIqncdxxO29qcU/3O6VCZ+RSQX1NlYsfNGInOmmUS2SyHPDlON2rSn23c6OycJdFkFMx94izsrlHgtjQtoENwvnO0PJmmvkWfki7kviZrSbMnad/l2KkWVj9YhEVxMPVpvqw/hejXPzgg2N5rm8sRBa3TJtbpS7d9ySIlMcNVYOga0J+kaEya5qWJm5FfWiulu3y7iPZ0o4cfJlGrk5QzQdLApyxUG/hoy4CHNRdUsfslVpFi6NjOlGZw8nYGpSiajU5YgoNqMqy+PdB+4PTxMrx1T2D6mIXR7ocaBP4UUjr5mCW8Zq8UFk8pIdj7eE+aBNlHPIyVF3G3Ze7Ow1kY8c6B5ekNXGiCZ+3WnAQTrtaNgG3I83VksNvBVuxSlXUUKNYhR4m6XOGeyJVPYzWFFKXrIcej/cQtMZtzJE6orn/F+oH2poUSLboCb+ysRtUE1sJQ+3OUNro5X3fDGeEI3TvlJOBCnslay5qVAZHrmQQ3MjSZjWHbQJ61G5kfX7j2kczSJ8pjVmLOUpJBS5Sjw5hNWgYBRbm1yEgou3RBKvmUQstsC2wKuAdNyTkLGbEW4cl2hBUkjBjazj++ohVldBLfY7Zj/6Xi5GkivGSlKV6EaAl+LD1cSqVasR0iYe7dWKJy6l+Sje2dq9G+Wmx1lV25tbL9Y3d9e3n19s7u1v7u4/30n2dl/+3oxCzKihmt1XQenzKz7gNK3ANNHACLpWwBFeYClbKjDYzOlTVoWQyl83WN+Lpo17JpezkdP/cjlbG8WTh1vESCfjLOratdF5TWURld/Ddlc12LDpiqWyKIBnQy62kCZYtmB4K/c05gZVLwTJFTKr8pr0sYYHJmuj1ENJJrH9legM03PZlDSdsyTCRdjeSi1T+LGnQlbrTS7Kylz6HwUV0kXCef2vMvEDVL/hec57n0EHG9DIVi/hHLmpGzY0Ap7AMG2TkpBPIdbtmcfPzKpNijkfpKmdfo24xj5e5BkNzC4yrwrYPeWd6iJMLBO0dduVUoPauU3aFwnSm704/fderAqA27sGfIZyAupiq6r9gGU9fqF6Tp6VTM1pqe3h08Z+M+VixhSE26yB84/euJvMSLsBFP1Ske2nkEIbZZcPJgMwvFrJsU30dT+pvr8Ofjw8+mJWvZMju5pQMj1Sxlow79Gd6e7mZtaETMxYN6l6eZnkItwJQBeBq1Kl+LWPwGRQfFTR3AWUGqk6EgbIFr7eBAgD4/rCiWXxFl16cSFfEJmmlVIsSxynrG/iXMvO6A1pKp6gYBR7ovu8ZUzwsfd1VImfBAGKaHrTqwOfCKdU2tOFSr9Vw7SuCisxCEns2kDbGQVJwd293jU1V1LIXM4aRT/sVSOvfFgA1/sNXJH/r724+hu/3eOl7uzdZGtz6/els6OveJsZfWN6rg/g+iRFF4076FG0A637Udq2SUhP8WJD/LPp1OH3XBcDcKDFFtrxIkecL1IdHKK13aRXg3bxwV5rQX6HYvus4npOaM6U8YIMnIWGdawVd4CXVnO0loyKayRzeePkcYsqgKCRLRZdcGRORZZDXOGcLcBVdmNVZWGiY6qYXTMYK+svUcwAhCiZ16vmBkaBkw5NYSAASxtLDDdzBmlqIaIdW4qCo8+AW3BW5VSFUPtadVRWuOoReXLm6n4Gp0ksUw0myOIsUY4JRD3DWtqSovOKO/UBFBTkVVVZSuVMNKkUKSsh5AmHRo0ir2YgCXQtKbVbnsJJEF56Rnn4AERBuH/XRv7c4MjjVvhZQxWsXRFgBrTP3yZnNrDuef8QeH9nmTr7aILxwJKzMFyF0/fekf8dUsMtSrSV2CEWhqF0l8n0MuphmHFtJZMMDKNYDgzUWWY5E8tqorfSv4vfgShgozi79rr0+BL3pofVn7OSbL0im3v72y/2tzbR0n14/NP+5v/5l63tnf/3nKWVXQB+ImZu7xFoEcMUfreVuEe3Nt0ftRRoeYGu4JxOK3svayPLkmX+BfyvVum/bW0m9n9bJNPm37aTrWQ72dal+bet7efNOruyMlYx+qYvF6s+ferd4tY39sF4GRMQiB1zLrwxIiMr9VgGX06tM1KeW6klGFRKpnyYdbg/oIo7GmwwnZllvSLMqTQuVQHFO5/eCzWfnSsgMvRnDRMlcgvM72pdfJZX+6ItEXev764WYkbQehctdngn8tomEi0wAv3AXgUiwO8FUYqhcXAJlLLy+hp5FtaGn12SGd7PYdA6PBdFMrdG0PXrimh1cmyoSxO0b7xP7ejRfahDxBUyZnkN1TniDV5qW6/jsBK3sXHI1k+VAnqq0SJcwqzj7GA6g4RcK91qLVPn4cN9uEXkMA3uVtcWsYPXKJi23LSWMvysZh6b3vetRDFu9G6lYhFEFlBCOeQMesBIJhny1YJe1bujmdA9V4lDa4PFDNzGdvU8xKf1nTM0IsOpwuvZh9KeL7SzPHVtzq/lLLKxFigsNS7WOijOK2b+TulpFEG0nJobqthd2VfusMB1f77QhZXO5saU2Ro2v56ib8T1OHIDt4vwhRGfYdmVUV2dZN0tcd3fQesHlVWdxGzttio0jW2ESoSROeXR9/Gdn4C8f/ea5Fxc+djqu4vZeRdIWyjwo2D1RPD58jT2ITscRiOQg0iCH4XrqJHIHykt+yCuWhaqGPK9QgrwrgAzDB4a7M3VQbLdXb2/seG6Wl0zkUmVpLLAnmsb/7K5CaaPZbVExfXVpY4u79uu82kuaW+M0TuurwiMAOKq4lJxjHBuU6h2RES0zCvQv6Psp/eaOWM+rAzM6c71gEx6zlS7GV+A/dJq9kvQ2K2LWD0F0wD/k2Uw7D0LGmFMgk4peKTCIjYt2WxtbvaYUwrKXQlLV5d2ISvY9qaB2x1VLDAH6Zg6Akg3/Rl2iBtnHtHMkpOol4FYc4GRcH1hyc2WyVKzP6olT+jDelScu4F9a7VbeC1EbrUehfBQhN87AsAUrjtuyRF4ZehVM4WcfaSpIVJlzncdVN/IPxl7J8OpDuazYJjuYOuaRR2AHqXNBGYwYrBNmKB5fhri1l3+o99CrniQ4sKIcU55lK+AT3kzt3f30ihc2jMnnTifR1V6U0gUjhF2AoJ33KzcKVGpFJprEwtEjjJjywdce/YK7K3r4C7fsJ4Js2iGvobjXM4SDb8n/vcklRkbJ573+q/rpIjYuFgHy2LNFTdFW8xtOqmQq/k2KfXRPDk6X0t8NlnjjSAXObIm3OrvNyLMiJHwVh6vQ9zDuKksMQjm9uVGURNhwd1L5GWTpg1dqkXN3W4L9Inc67hwYUCx6yKiCHRh1G7yW3wX9pz+WXeZHCAL427tobEkeyBqxmF3OCwILQsuGNHB3BRHcsVotnCU5C5rT+i1/Tm6JvEAeuIg0ioQN1w3VK00ZSVmNIdJfX4R1Cmg9vhLATL5yZGbfOW4UrJkGweFNkxltFiJsp3pZKLYNSof/vHzi5U11AXIL7/sF0XNTDjN/VPrm7v7m5sray022o26/cbMB2bO1SeGYEG0UtMy0IosWtHVZB1jsVbgph8hSWFcU3R3kFpR7cR3IXkiTx8RJux+6yhgy/HVDPydMrJI4KIg97BUdktB5nTatk/ravcb+4KhVE7hX5SdxmWVGqptyGpbexAwNhSY8xKZdM0pK3uEr5k2fOZX11S9l1AsBJxbPzSmUHCxnrHSzDuj45XUbNVO0L0GQlOIdXe5YgICb0mZ05Tdqp3copXUJ/6ztJNi4fSTYuGyrK2GAnNs7G6/3MpYNlmf7k4213e2t/bW915ON9d3aLqz93KTPt+bsru1F08PU+6M/C7G/Sf/+Y4Q9wMsTNqKh4bCHR3/EISaazKxclEzWMyFbNtfIXbOBynbsd3K/f7/BJVbXR0wJ3ZFphw44GDx9Vvko8D9ZyqyDanqxZJG1MvIVaIIdsPJAqc88XZv8qb2OvznTydv/suXTNR1vLe9ZHnK9FqCL7vwf2eF6Wn8TSHVmGWIzdZ6/HGMvMLO1PSguGmMxfoMwWT1NXVeYhJq6FrRwg/da1n1Jrh6KzWGbxlF0yswqaAVsCf8gxqj+KTqdDYeoEgR4j3MF1//4UtsFIHs+ZqqhaWN0G2G/MIUhqlBFRT2cU4rDeZLSGCXU3e3NLm1ZQvM1z7y8fTueNr7kF+zEdhyIZE4G9X9fewdBY0AYpcJ+8jSyrARmfMsY2IE4ZD4bynyxchxyBG5Udz0mA5X/3PFP7syIiv49Mp/fWql9afOEE+dIZ46Qzx1hnjqDGG+784QvaH9D5MdQA6CcUAYhLrRS4oLEFGHxNZ4vykspFH42mNJN7VA4GQuihE2kAnVL+/gb6GALQzjNhAlh6oEO864sFONncrH7VlhmoxhFeNIX8Vgf8zjwNrbwapnHx1ZTTMNw3lt0sMdV/Bu4auR9/fYVxw2SHa+ad3y1gWA2kSpW/31g7AzFJShwWHIug/qDLRyd1Eqjk3FebCZ4tdRdAQUuHRmh8gU0FnhxlwWbIPmHvNhpXa4SxzmcxfbS9xHCkRRLMR5x2qbhglgzIrl7JpGlua6dVlvNF2UPlGWTFlFFy+AhvkOrs+8r1X+4bJcCVAzYFMDYFlhks5els6uFJrmD1Zh9Ezxwl4E2O7y5Ig8+/nkaO3Oo7S6tbm51TzwtX44NITt3gE9LQbbB+CL9h76Sg2GvmIXoa/YKqiOxR8uOfPEjl3biL2gitxNhL+9Kal9VrZ3Xzzfe948LQUv2OWA1SzenLw5xjhqf7v47E+AFpTCZrciRbRRjELcyWRhIlNCpaEEgzMW3tzcJJwKmkg120CfNySAbhQs43QdLMHx38nHuSny/zw5OD2oWfx0ylNOc7Qb/9fIXRm+3FmC5YJ6csms/FGC3D9x1QTDmJjeGGK/o6X7TLtlGX8xHCW9sYQUo50LIlMrtgfqor2lRFY3X+xstkjoMyXSHoE0SJIUQolBdWgeswFLA5+2G2jhZR7q/fibso73N3FH6g7KfHHP9kUqb8RgkWpoPrYTrIIFRUHa3/330+O29/pqdX2glRh0EYv0k1FrI2FvsTRoR/ht6KdZJFQ+TPjduG3vn7qOPXUde+o69tR17Gt2HYtCefifDwzk6zF62UGsGAEyW6Qxv42Va+SeUMrHRTxwTVbsx55Cw1svnu/tNAA1VM2YufyL3FIXsBq8pyCYYlGAr/+LlZqDfQMJ9RlSYcYVeKgdJGsd6gvu5BBcMWi/ESu5gCHgPRgCVB0LHJVBfHbeshKg4HO7rSBYChhmjbs4gJ/dxzvCAH5mMq6VmVKlFpjEh04tWgv+YGrCDm2hMFGwpTdjPVwzVxleib1lobw4pmJjwCNL55A3XqcYWMhOzryLVCqnbKh1XVk9JdjGlyqhyc1iKP/Sod28XmH0jRRW72tmAmDsDBOD+btOG34uN1m3nrNUZk4OsLBdC8BKGLW45Fr2lJ1+HJThFOTk/G1/tenDg16QhtpBB07vJh5SQVvWbU/V94AyY/KylLHsFauIUsy4gYqKIiM5NfChe8L/m6zkUqzsk/WXz5MXWzt7zzdHZCWnZmWf7Owmu5u7r7b2yP+sfilVcvW9PYI+ZKglnNKAmpH3d2CQnZySmaKiyqmKXdfQTjOFCCvLbKIr9jAuRhLJFly5VGmItMZKS2SaS6lcyPwInXZxlb8wKIKXk3K+0JglB/mGI2APGCPS6tlYpzFBSCIXhFZGFsD9IvbWvegnUhsp1rO0sS+KzbgUQ56sdzDDXQdr/dfDPpgGOloOnt6T9WvFJiz9oc/O7e+v8MXtN5i9VNF4HZVq7Qlnh2d0HbzTco7EYe3LFxgftqdIo1hU8HiZsGDIDimYSyq5raUPFeT10cGZvUEPMC2z9p7F3USaLGQwIej2os+4KNeXEi2+GyFK60vxtxjnAFDyQ0+pIEefv/jP95QSnmPVHyDPmiLrnBP4neYzqbiZF6GyLFcu9CyKoWR55qLZsBIxhKXOsVUWhpq/OdodgQNjDei8VMxx64QcZJkHYxpCHjEC1w0xWUDCuEqp9kalJnDIjC2AaLvGehaQI6ZZSRU1MnQUproRXf1MC3qF8bMjgnlwc/r8cndr+yFNi7+0q+nLe5m+joPpS/qWwnmSulGb+xf/+c64ZQgSbsctu+xusDRUBsuoaENFlDx1fHgO7yZ/84fg1oz4bpwvTCpFXeQ51ntCEW1QNUGhua8YNKwVnTQtC+2cquyGKjYi11yZiuakoOmcC6ZH5EimV0yFTqLKpW78ezVhSjCIdJUZe1BVZpXOuWGpqe5NfP2UjX/bSrFuzNeRCD7uvbh8sfO1bli8C+U02jtPav6ave2OrQMrUPZMY/HVDrK6qm+7fcOIUpFTZn48eXve7fL1movqY8/YNdDRTGFEuPd9BYGeeI23pxdvz98GzNxjU5sxmXxDijSA860r0wjkN6dQx2B9I0q1BembV6wtkE/K9bepXNu9+RYV7Aiur6lkN6WugSBZ/cWNHd9IjUrBdT+DkCF941P1xx6yMSg29vy6hr5eK4T72IlD9yisj7Mep62iHBDHDR/ogEdfOo3mN3ShSQWvjCBX0FUaCEaHglHBxQwKX7i620xccyUh0KfRVt3tH/SerhSoiZUv+DaeMGqAEY3bWCjvwUJ/E0gQRnlZNz5s9V6i6QDI/cVt5m2zDkWjp3fSZ9R1EikzosqIGt8L/tEXEnGMEorK/VHRHIJ7wpiRLOfb20BlB9djPTT0qDRTiasCAl16M5byDKqtWXEUSKlm7tBVs7X5UidTWvB8qAiMt+cExyfPvJNGsQzStjM24VSMyFQxNtHZiNygONz1t+GTHbir/BFTmr+a/7Oj7uCuN6N0QsyD677WL/LS1OL7jfwnvWZtbEUFpgbY5fYacLYANqjbit64Qi4dyHeSnWRzfWtrex10cp62oX9cAepb2+s4gs6h7LbN/Y82Zry180vtrJ/PnWcr90k9ItWkEqa66wxTdcM7Z3jYkKEO8MvS49ZmsrWTNPvqDlZ2w5VXbl0rVoM/zGWVBWXc2wnqindOqsHgBSihPTbbScEyXhVjKKJzXbRKGzYsAcEm1Gish9XvwMIbu+BrOSSM2CePtKpOlEuGxd4WVXOObQpqSS4UFUAze3Pbnm/vNqe39+PXcrhA2MaQ/hZYHSsoH4qtW9WSwARe3kq6ANhr+JHD4b4af7YLXtUglvlreEroNeU5nfRkthzkE6YMOeZCG9ZiboAb9Ab9dT1+0SK/aedfBOeX9gO2gBiwc4hXPIHvgAcOyu4oDL1q8HJo3ugYlCBUSLEo+J9xN2lAYfj4PhReHMMqeDa2lIIfvPaN+k8qxRT3ql3wQGSuAngYttl0qYGnL9M8OCTEw5xdKB5PnfxqLO18LpUPtYXaEbXpv150Ixtigh0BgunHmEaAxS8XF2fw+XaH20/ebR1i/uxLUfNC1zmbjCuV+2pcmmEpThNh2AKpcg+vYn9UTD8g1MK/MJHZIomzqB5YqDN+tYncONq3BSaBWdvo3dt7eTuILuHnL3CRXjjjBm78nRj5heW5JDdSubYaHcwMsG8XEmsz3LF7zyywwLTmjFrpu6vSbO0879/Mgpm5HOo+XG2gFKdqpWZH5e2wqfOExcVtjQwBG1iV7I+KqYXVg0IX4EymVeHT38LYvvfvyomvXGp1q+PD856w9RkzI1JCh+eyMr1oggLXarDsr3du+LrwWoy5zm76jMpJLmeJz1hKZbHRgl2XUmj2xXkKTrssU4mB/Otylbtwcjtb8bj50nzFQftpjMUBjZVwehxVn19zuolTVy+o11+1s9mMtxjWiANw3WYV2wIjTZ11bpia0rRR2PCk8eXdQaFhgE4Pf4gLTaXKCBczqwljf0T8szkvaYi9kOqjWCmVK3VEhS/Mq9pFkImSFWRX5pJmZEJzKlKm1sKowWjDPoZ08TAW9KGC7kg9vfATaOFm6q4hbszQKSQMU6MAgfNjaSa0VK50e0kFsStaw6IhMRyJw08PKnpCp5aX5WjO6VA12gKJ4CzopKh3rFYvRz0OaL97gZuFst7Y2RdNaxaVXGiesRGRlXF/KJIVf4YWHzXqBS36zJLuxR/u4ZqDx+PW+Do5aiOrQd41ts5P35x1zgkhJ0c93G9z2QUOnYTp94LdThHdPHczvwf+OiVkFvOp1+7jHXGMR50Qw1BE2xcFLFg6p4LrgkSVAkMzlijZCjrL1GGN0Csl7Na9oY2d6dy4oes01BDz5VfD/FG8fNP8hPXYw0RYnd6PCZ7NuGz738aNhfi34laDnTr/rRUKaWARLIvH/1so4jupDFHUGcF9sd+/gdXDKtDww/HhuUPfA4IngVCbRPs4foS3vuOHRWSI8nGb1W3oOe2p04X4cv4GDeE5YSgFclwFnYh8uf1GkT9X+Qt7QFNDZpLV7QVgEHRJxE3HM8m0WF01oY+0FFEvJl/Nv6xMvJ+Bmizdh24DULIkNPOJex2sdXrzI9Uh0Y9vqBLjERkzpex/OPyrvrVo3tMDAIptNrfV0pIaYF8vWp2NcCJ3l0D5N6zAgrd8XS60AjKPS7LEo6Q51T5KALrzeNUwzAC3ky+5TNJKG1n0u52lmiUsp9rwFPv6JRMpjTaKlsmP/q8GsjCVHooGJDlfqhUBdCIMCO5gyI7S6pUSSqhQLrwb3ZEduNBdy3I8Ne3eUNGRaa12Z/vWpQx4HbWp4JEWF5UyNI5yLGM0XZrrL+0Vtjf5J72mvYipRDpgyYsOXtx0roLjXGYdVNyzv/Y09CxkmM6c/rgC44z5t+/USdv9zEH9jZ4IGzthU0ioKXNuMJfBkKpsNAcoqWr0xD3BqCUFlYcwl23shvVGWUReHN+E1f0VhSLWdsRmCX8WA9doJdhYhl/sqLMg39UtjIkt/FyvD+iEgLWQUideU8zsRv83E6mEoBmpiGA3wBes6FbI6/gQSJJC3daqbIP8uY1OiZauj6m91iYMbGtxaNfEx3mAde6z+51CAC04xt8sgkQZ8nPgIlzi6GGJffcVfrjsI+vO2XNXbSiW2uzzxWOxAvJY7NVdcBNzpGtO3TAJOcuZVU81Y+TdT4ea7O5s79itfL71YifpWVoypSnPfQOfx7aIrEYr9C2m/IQd2artKg7rO4jbINWrsjRkl+XOSLuaJhX+ygvdpTbDkPbd7edd4th+fieOBr6ffOcd9tGsT6hVBJZGVmsdQNQv+9biG8o9+la3tvmWxnWfvsWsHpJrskf+ViPnX4OkmjR5T93QzaobyN9D/wDXUgVYsqOeQCgw89arrZ5iMs93+9Da6IP1MNzee2LaTdnuPzF9zb9czy+L45phxKpKnRnbnrjmNIClts3t5Oh8bRRrJVat6ADvTuZM9jYJuxP00LfMKznU9bBPTat1mb0N7mpd1m7itlS/sl6eEDZ8yMyUb4EYmg38wqhLEQGYWW+hgEip/YqbH0HR7bbgdNRgLENDbmxyOo2+uicd3ZuBmzm0aI8uiko4cQzLOMlrFvoa1wm7BIWyqEGPy4HVDWuOe+KTMm796D7SwA3bbhkUOgg/IOe11rKHOi4HqMnM+DUTro9WNKuzw5RKGpnK3Kn6XkFXE24UVTwiHCwG65pVG3tYNMrIBZROc02LRiCQ0lxLmGyBikD9sL5alJFJhqd/jOzNxSZSXo2IubGynPKtzOL6rlbz0NxUTkqvq5Bj190wIpSzAljqIk/2FspCUae6uyUcqY2MaUNOzrC+lR6BI0KPSDTmDVe+qu436BmnvGiQVo8jcpmeqLc6IVfRC4neR5C4wQ8OOzKR9txAZJ/dliafHbvOofDmGISIsUW21Zu5FOF7xciVkDdiRMb+sLqfUFSJ+tnrqui5kV7sNRDgOIhZXA7msVg9wIg4aKaH5mAB2ZJ+ceTkDF16jpqoJjcszx2TC+vxx69OP2zyv9oCR6GnyTqdCamNvfkMFRlVQGO++nMYdpo36+u/ZlS5isvUhMiEGTfzagIxCZZAcj6bm42AvHWerdtLpkfo25+//Vd9uvPLv775effNPzb25ifqP87+SHd+//XPzX9rbEUgjQGsHStHfnB/+3t2bRSdTnmafBDvmF0P7Dmptev9D4J8CMj5QP5GuJjISmQfBCF/I7Iy0SfuykziJ9+JED9VAgj3g/ggfpszEY9Z0LKMWj8C08HLyykzRd0JzrlgR+FCiuwc8ZiBc0GSvSaQgAzdwTi7SRCGWyb2qJGKlEzxghmmEJAG0MvBVAPSgMD+F0QeN1k8cpg0WelayADbDbqZSnVDVcayy8/JJjw583HmdZtYd1yjn5y9rFTyYzfsY+vVdrKVbCVNKy2ngl6iOjUQgzk5OD0gZ547nKLm9uzeKu2en6wjcN0vsF571MP23PERuK98tzn/lnb8h+bQ+xw4GEg8p8z8lMsb4HAa/nLBmWHcXM68Q6By0Zl9a+rW020iWixXzfuTDE5OXE1gkthxSbPMcWPXa80yWX81XedUuIdjA6DPRkejJQwJNev//vrgFKnvj3Uu1v/ALwxFf2fUgo4c5FZWiGKmESDf9ITYiROO1kL4G0tznAD0EVQtz2SlozEBEM1E5ty4lk3ijgar7t7mdrL1B2EipaW2Jx/kLSs/tmI3WsrP74xdjchvXDE9p+oqWQsovy+swC4gcasb6DgB0rvBBY1Ak87RXzpuIFrBgPrvW6fM4WJuCyO4dTkPDPYYOq8B1ZLJgkhIqpMKaMzJvbquBuGPXXs5P0O46m98yhtglzS9Yve2jbzd3gSirhvkk4Rd926PuFv/0iPw+h9rzciJvv0i73YzYs7z6wGkrNXXLz2jrKVV5DzsYwKy5IjkwMv/SVOrw4XgjKBbfns6U0hCCHGmHuohUHjuzqrf7Eh8QH0ZEr6or2dnl/jvOE98DIkXc2sM53RhxYIqK0fEpOWI8PL6xTpPi3JEmEmTtW8P8yZtIX6gNFgXnvj2/ATasuQovt7E6aqerF9bLCYWdzuIwcg+UWqWjkjJC0Dot4dOC3QDn9/zPfpXuEGDm9+NAk87++jb+Lu76gtGMY+d5uglg95KjpeMQvF2LOzRMStip8YQSJcxw1Iz8uNjVA4G19074npTxncKpr3nsKG4btZeD6nhIdzHlxXEQSn0y1fQ8B2W2mryLsWUzypV77skqhLLI4BoOTV2usSXsmmXOfT2ej0iN2wCGiBn0JjfqAoS+xFdXIqNUsF6YVxfcsXLw7Xa/IM/wVZAdsPGIEUzgn87lxo0gM7QFqsHZ28canTyQ812An1GFm2KnT5vMWi7e8PHHPMpoWLhmRxgHdepA11oH2qJtKFr4f8OfMMqvA4WusyTNy725I+KVTgwOb54DVUypQAS8savUsmUaR1ZL8IwoZ6rYuD+SCUErFnJzOMDogOPD88fYIVncWj5o+uX/rgnLqx/LlGfqyPYwSQehWmjmg/tLmkRmcktY0Sa+FOKZuqtkQSj7/h04fMHvP2LkHOMxqeqaFic6qvG2cTbul0rLt/7TDA83+rzt4TnYywMNWwmFf+TBUiWvQFwAUlASfIUpv9gza2Dw7983H5nxd9nIH9nQd+zLBcv4TsX6TqLskx4KNuIY8PA5+U0+CKCse6O1REjw4GKeTCkNNSeKaoYBNa5y8KP7Oqh+65aI3LsXB31NXT05vcR+eXdiLxmM/uEVTHbGD2rJjlPL3EYtnTPt6fCvk+FfR8OUu+GPhX2fSrs+1TY969X2Ldd17d5qde+mC+j0/m07eGVOj/T96vVudGe1DryOdnXHST+5fW67pK/d8XOr+h71uwaa/jLqHZ+VV9Qt+MilUUciPFpul2dj05x1KZel3h21dHrQJ8Lo96j1x29+X1pVH5ayFYdklVXuem/44epBf/m4PB2ABrzDymlH9aZ0V0khM2qo0LhQbDhu3DnON47vNmI7p6zvJxWeVyjt77upnUkUHBWBAcCxWxJlteFbDCFU6oZFfxPlKkbcRFCxsnekPnIWMYypwBgKifClbOpIawozaIn5vQS4vPOf25sxFO1effDt1aB/Kna/FO1+adq848M/OdUmy+VzKr0EYv2ddJ13Qy33FwtEPX25mYDPs0Up/mwMdVed3eTOc28KVoMVpV/7srqt8usgXWeGkogYgLEwamSRTNmTrkGP1En1RCrXY+0KJlO+krS+Gh6Na7FvbG/3aE+TabhPyX8B25a+EPmOYMqNmg/sH/VQQk9OYIN7bku5xclaD0mUv8OAy9HcOeLggrTMlb1nt/H6TnpNyViiHUBkFpWgnd9dFD7+3tSKONxfCQIE4qncyQoCAFpVMwOeY2pLEoqvNRkxUCwpzaIsZXkGOdU6lDP0IqSkG1KlaJiBvE8U54b5qy9UH3ZC4lQ7gJCfgU86AXNAEa9nodUwPoKleKb4i4ZTDX4eld9TFteXKtvvgbZhmvqHK6pe0j3AoIyPf34kgP9ZCpbN+Dy1R2/S63gSSVo4eh2leA71gf+KhzikZWB71gT+ObVgDg5xtf4ctz7LPrqTqZd3/m382y447WhORauwuhbP6uH78TUpbt8x/Seofxro+DNQgKLGIfmf8ajQtGBMLQDBMd0gbD1WIb7/hVpdIkvVbjh1mblj7bjbk8e3Kd8UvE8uxyWGlcPXEpk767ZUw9Q1Ns0dfmQjiwCnwlUEb6JCriGlNFUFgU35PyXA4xSEBiFziCD2g/RUxBgujN9yfZeZdmLrcnmq729ydY2Y5ubm5NXe69evNh78fLl1mZaO3jvMWinc5Ze6Woo3nTohu8gy68Q5M5rpkKVum7W7N7k+farjL7ae/WcPd/ZfPUqfZnt0Ww3nbxKX+00de1o8oFWdNSMLoH06iYXCJC/LZkIdXiUnClagBKcUzGr7NqNdCSlwRW7oVjO6SRnG2w65SmvQ85JHfDf1A8QnZc6lW3d/hGdhxlsjZiRubyJFwx16sKOuiC7SjO1DiEtIzLL5YTmHbzg130LYcvoOxk1/S0PLOODLOBe+JqYy3nKhB7M1fEah3cFkzFXvI05f9ibzaMIJTr0IXI4hZglN2KssilZkPOzo/8gfrrXXBusH1MzI6k1n+SszrDXZfYRsuvdkHpjrctnDkqazlkYeDvZHFDS670ioilqypFNwYqaoTqEnVEzjyrx+H3jHYKKoNuotNoA0t84ZHlO1cZMbmwlW9vJq3ZnFCi5lQ6Fwl9kYUFGm0WYjLx/9zq4u7wEA50SuK5FEl6XKL296mAosyItL7PEtOx9YwWbJVb9oIqEnmIazUS698j29vP72pQ+YkE3ZxDtygLgrnThSV7ejEkM6hXbmUe+qrqZ0+YjBRW0rvBMXM6yzwTbJ6osRiQrr2YjMlHsZkSE/WLGihERFXz9T6q6Z16VxbLbOKwk5je0OUvcyWQ7eRUL/025/5j8Au1iPkXy/w2VI3ImlbGkT44/srTCP5+dHa+F+q3Li9VNi+QgsT1WZHXTNGzGlpZGvtpfRqiBp3jO1q2W0NVeodyZnBpyKFUpVTPZ8h6SGF70CkvNujLYA1d6RuMw6HtWZsceWPcIS2spFw9c1ovkefLqxeZmsvVyZ2t32fX5CtOXsNCh49DsKj+HRs/PDk5OL5Lj/zhedn3DOgjDovq8hA9c3Eo4gR8+Hhx7ZgR/t23RK3evPlp76qNdPX+MvrrbD7OUYcRP0e9FSamoPSl1h1WX+dps/wT1Jv1whGcbESm6Wl+N6udgcB/76UvotDo1VucydKF9EyicinCjWT4lVITdtasqOeaO2wdRLfFlwMB6i+DWwfTLWVFmQ4X/rh4oRReuihUgiaoZVFnQI7toBfQBeLQLohMt88owrDQaRdlB6dVwr0WyyRu6IBPm3FyImVJJw6ACq9Acuh1He9aRIdzHdZSFJ1xs6NDEd52s5+FPqyaGD1ubif3f1osOIi8h2+ZhAmNLE2NiZuZBVXfEYscGx96iv4q9C9uqsJlvXOHClZmzKLCfJlV6xQyhguYLzTWRwmrJYcjC3shhk8iN1ScCN4AWrlTFZ4i8gUKG4YUCNySq8c+dOo53hK50yVMuK123jO3IdTvLMspUZuxS85mgYJdjH7m+t97QRMqcUdGH+x/xJ4ywL+2QkJ9PwgxxjbA20KtGVWz1EyHHlnyDncL77IQpUwYNWr47YE98Y0RbvkVUqhalkTNFyzlPsXOOro9zPOo1zXkWZy1B66hKGz8fec3oNSOVqOsmuBYD/tX6FZ+nV48fhr2hmlQCjISh+XRcOPndu7fvLt+fXrx7f35xfHT57u3bi0/dsgrTVAbKsDnH4RuXM3jnoPKvelRJuLUyQPJSlq07ztLquZGKaVckqd7ons0j6ZzyOFT173bHUXaoX7/tPc9yrJwC5S9Yhpk8jQ5Wrg81arGQY9Mo0TFZQElXjdG7wJlYvkBjM9ofkEo7BPVZpx4o+zPR3M+zIHiEzzi2LI24F1qurWQ3o1xo07hiJ1xQtSCuqWyzZm33bNLGXtxz8B6Kp6KgIrtcsoHU1/HPNvfhpyrPPdzYsgpICe5L15jI3Zlt97uXesJcTvppST1I1DTP69u23fyscw1/ulzUkIfIOhRFVi25Z5kkfYhlGrD28+1xQW0pH6XvZgoZMhW83lyHwTrdA4OmwBuCleF0HM1XX2RTcgMh/40K6WCIhZxcDwgGIMDhef/+5Ghk1aJCCq/dkJ/fnxzpUXw/0qiudWGPn11qvgglprE0cKjcA0657qoPpdBGVanB/rGoNOQLN1yMOchhsCQsBSmVZYIpuHwKbvgsvmTPTo6IYpVmjVLade1rXxprCt1WcHnQN8DqkCNC7VWl2yFnxGdPWuxJbXqYbbqd7uzuZq+mr149f7m7tMuwPkPfLC9ZPtbjoKUjxbTe0JHuOM8t7HDzCU2nuzGQdiAUUZq6S51MjqXTmVVEoipVvSUpo25JEytuu0stBN/Wk/nzjl0nsP5tbESw/wAX7nEabble3EsQkT2KSZHtDsTI3hzt4hTdSfWcbg006/kvB1t3TLu9+2K4ibd3X9wx9e7W9nBT725t90z9FwkGW/UXCobxNSQEy381SV1AA3r4nYahiOYFz/vcLG2OUVJlj+3XsRsNYvx5uM1nGStujaYnq9CXtAo5xH+/xqH+BTzZiL59G9EtO/fXMRX1L/DJYjSUxagf30+Go/vQ9WQ/+kvYj9x+PpmRnsxIX92M5Gnx27cmDWMwegiKnkxKy2Pri1qWHgjWl7M9PRywL2idejhwX9B+tTxw37SF6wsZsZbHVjlbSt54UOT3SX1NOo4GsVmRpYvpBoOeMDu+vRYfutllG/plGs/eEbMeoty6ObbbO9sPBa4D3WNE1UNXcIe5VVL2g7r1QFCB0S8B661ZPlYf5QVrbKsT67t2ou3NrRfrm7vr288vNvf2N3f3n+8ke7vPf3+oBmTmitFsubKGD8LyBQxMTo4egwwclANG8Dpwe1Pacfb1pYsteqC5+V5kv8BGAeaWVGRpEb4foWKAfDXUlqM6UCumaxxSgXm9E1Y34d8PQ0YV7AglEyVvNJT3MaAxcOOA8BIoNPmhM0bSStmBcug+KCITwLL7UZUW8s8QNc9ZKkXW5Luh9VFVdpO5n28vHaruYLyR6oqL2SV2LJTqEZMrhqQfSyYOdBJAbzshOorDXBZsg+Y8XbrgZ8mS/yVJJyVL/rp5JyVL/uqpJyVL/vLZJyz535iAEiHgWxT8A3BfXqwPU39toT3k5H5DInm4ar+iwN2C4VsQpwNI37Sw/AlRNd+fJO3x8/XkZA/B9yMFL08YjyAi11UWZlwbhxWX+/gu/u725MefMHnRNYW1lOHzwv0AvoAfNEsnS6YGQt44VCcYiJ+svnXCFNZAIDeKG8NcauWEavZihzCRygyKaoXN+UmqsEDVXWBdW+qcmb/TvGLHH8H7+Y7Nfq2YWrjvRk2PP6RP6hJpXNbOO2hBhQ69cV5e2u/GSQh5kb41wqQyXm6px5wwY5giiqXymik64Tk3C4CldkfUznF78t8d/3z548npwbt/4MqZa2vd48j6/dcfq4PDzYO///rjxcHBwQF8xn/+bVlhB7YYb5/7gqM+rYY+xgRgnRu7vVA9DeZzVXLrbT0LiKCaWB4JUYB9b8K+uD3yBJAAWWjoxxOGdM8HIoEpyTOL5PPfR4Ds4/84Ozg9ujz/fQ3pIXYUBRh4KNxCoGSqq/OGU7I/KiZSbFTgJgQCtqO/ef/64gTmgrH9cNAjOIx4TRXUUSI5hPnhsKKCPnOw1pqi7ZhHv719d4QEffzz5a/2UwP0iPrabYixAWHKC5oTxVy4GnrOnrFkRsYrWyvjHrfW6n+uHO5/UIZ+UCy7NKb8MOHiQ7GgZZmwj2zlv5a22gDBDVTa+dxQkVGVNfcbL1THRXyQim6vEEli2VXM+fUQCziYTBS7xkq/oBV5V6Sdr3ON/PLvr98sC/AVWwwA7y/8mmErcn7tPMxyakfq3nnnb3+6+O3g3fGHWmPzLPz04sMhyi5/R5X+w0lhBZqfeKhnYgkUm9DoDzdcWEAt3S2t0nUKLz3K8iFox44dx+TYrRrZ4eCEAu/u27gPn42QcMx7EPPhiE2qWV1z5/4CORGcQzXWhDn8Hd/tarMUxLWwVPe/D7JS/dWddSJCfLRmxl7hBaPC2OtkSlN7QVPDSMmvJca6KOj5SknJWWqX4uGDmjruA4RPwQMa+/7UEbQuBltbIRliD8WClDlNoQO+vWGOD89d1AK5iEFwQ2sGtSfFzPOCYoSlvOvbSU4hrgumQFnB3Y1cRUJNrV/i4rkgY4fFZBxWcmAZZKqYCTFKFkNxP6CRKw/ng8uhYtxcahM61quRD3iqKcK3vB2RNOdMmBHxj0I3PmzHlPjq+NklLxNyMsV65mXJXOjayZnn20bW0PNyPMJ6HVh3SjikAcao68JzckaM4tec5vliRIQkBQXRLK4+xw1MRhXLRlbcC9Hy0VT7W6+2k81kO9naHT+gysac6qFKvx3kOd4RVM+ZRjKQwiJEecJykhWGDHryh7Y/NRepNKqXENBf48+NGuqicEE0N5VrwYcV5xayWlWWFHSlGMSx1fqWA4zQfCYVN/PC0tMzDLdlik0lvGEJyrJMuPQCAGvLtzUsl0Buf68riz7HoE7OetHXVKP1YE0x/EZCrKSd7XZo7uePVd4oMvbOf76DM9pnfB2c0FQqig8Gi4aLyMNAQbGoe16EvhJ0ZgV+C4CLjvYhi4TmTBlNpCISCsUJiYXKYGG1JuALw9kpovBJN9oNSOderkUVIAIcL2K273mKByoruAZ3gRUAlcxD1Wk9Cq05JTIycnJ0vnFydl7/ENpvjcgNm/ghSwwfx54P4YFK5S5wVo8IExmojyRjhqWYUiGsfGpZsmbk2fHRuzVXTTqEbTKTPqR+T2Xm7Z4ej9cnD4p6xj0WoLlmqVmVSbEIdXIRCAg3hb8sZ5AkVYyaqNBw2CtPWYEygCs16LuTpHVuqFp/HfeCva+KAPbmG8qneFA3/0MaQPHGDYVLdDHArqUHcliPhIAVy2Vr8vCxxL3IIAfGsKK06sFJJGO8ZvRqaf1rcPfjBTa5b3seYePdhns89C/yx1ymV0RZtVobkGVK6GRPjk7PMQL4l4uLs3OyQS5en0Ngukxlrpe+K4YKIz/ANZ4cIaPi2kdHW9XbVfeCysfIO5FRRlJTbWHwDLKXcB5EMFubSwc8DVtiOFYE8luqDd/OGwJqMCbXCu00Y3dUfHX1gH0d4CWWP6jbpNF/HdcJxiqfYbPcuXj99vDfL49Ozy/tIbi8eH2+7NqGLuC7+q5RtNdIqy7cnU8Y73XY3d77IPxq0WiHT6FpNkedDbtbiEyq1VVNMplWdV5GczZQKOzJXF2t6UlIU1PRyIq/aeSdoSTn4grWQwoZ9ilHhwuiYOKl6vqac7V0Qdzp2tJ8MWImkht+xUuWcQr1re2njU/aXitrsaH89actytXMjEgpc54uRiiboEyArlx/61pFAU72g25/DOgvWN0NLjYhOfPe5Zlj+Zc/oZy1LJ6q6hvh/WB5kCoEAQQcwZWg6ztBj1qXAWd6qeugyTC718LW5ib+/9IGokGDei6iPkQbRLFrrtuiw4TZVQPtgF7vctW7S0vuWVPU59B3E3ZK0nn9zR1q0oF7zm6y7wBItfNFgKnF/iailv+pFMJtzzSI6qj0EMVmVIHhUDNQUPQoeh73f8LRtYj8dJrLG/AoqazWmX6SilwcnrlRsaOvDmAibCnj13UAChfccJqT83+cQqFuZp7pNfejG9QOWMOCbgmkxSB0tWdyDDJfdPDxQ80FPF6MokJTNzjY0JwmRGhqKswvc91HDFMFWQnjrVj+AbdaNKyHQrQA1wnQl/vZ6YmOeTPfkKa+LLzhDVv8UJfypltTxOtwVpbzxgSoQcMq3IhRFiyoof+sBBIFuGbQLube7husRq2QpjPkFFiw3cZ1OJxtpfoQh9/wS2h6f9DAQ7OMaFZQYXiKjpKPxrWvZh/TORUzNmowda5DB2sjyTW3y/W90LF5oYBkX9qwGnnLngpzTK3q7McUvoc2XiRo2nNOOW14nhOGhibMkHUt10UWmxkBYVMedeigZalkqTg1LF88RL1Gu+dQghO2CIWrz21M3ffcriEwmGLCZ5WsdL5AaoZ3ApcHj6IO2THQkJQKcnI2IpRksrAbAMbQSvCPREtLJwkh/6gxS/MbutBoWm5e2fTGw+Tpfpy4L8aIsqaMJqwUVTtRs8pn2YPRNuHl2IIyThCs8YhkrGRgnybSyQyk7vwPVlmuW8EsVCdL96e9LZ7FJf3iOITm0ICqLq9MKyOFLGSlfctDwHv9dQDQd13DgZ4dnJ+uddJs7b3NaDqvbU2ISgyGZD039O7Wi1ftNTeaXX7T6VzLR9D09rdsoOJnKWc5I69fHzbw0ROYskwwZPxas8ILhKBAaihU7474vSMJZNHdrdprNv9Cwr4Hsk/ybyM0OH7TLD1jMkm5WQxVZOSQm0X/7ryRwijW6o8E4EhhuGBisMInp42CJ26yDnynUpk5OYBgCtoDZCWMWlxyLXtSlh8HdTgFOTl/C/nFHQgPD24Fa6jddCD1bughFTTrYsr357sHnBmTl6Cc9837WooZN1WG93VODXzoxtz+N1nJpVjZJ+svnycvtnb2nm+OyEpOzco+2dlNdjd3X23tkf9Z7QA5oBFn9b1mat3fxy0DJw3tC0eEoskBpTA5JTNFRZVTFZc2MnO2IClUdrBiZ6PQgrs3TdNoxF0b55QJdC1AtHwuMVJowlSdFO9F2/qGQvByUs4Xmts/0LA4Iqk/1nEc1qk0Fk/2QZTAsWt0ZWQBF+SMydCssWPdmEhtpFjP0s7eKDbjUgx50t7BDHcdtPVfD2+Da6Cj5mDqPWm/VmzS6oPedmR2YOh3Yq7WHvrQMst1X68pCx32rY7f5OTsesd+cXJ2/aIWPlvyVkHTAXDz5uDwNqhJwzJrks9w8K5eWDXTKV6QchErChPoX3l6cBH0b1fxgTvJrD6zkpSKX1PDyNGb39cimbd5VkCbyyXNyITmVKRwWiMHoVREycoe4haS7TpLuVRqw4NSCGIE2PG/YRSgBvsAqa7Th4uZT5PhWrkunW34zDwbh/bbSBwDFpli2WWf9PiIfd4gmHA2Z9pEk3oc4dwjWEhZsiyAXE280Bm2POoRO4oCcWE4p3FOpSIrUymTGUjwSSqLFcI1WYk+t6sIohfVBRdlDGu7QKUHlnJtNSrXdwd03JxfuTQe9BDqajrlH8OI8Aw0ktzf2MBH8AmrSa0l5ALDe4xE88BHXgRz9GSBXU4XxNCreldRJ86pNsTcSJLTCcs1qt9CGkgFwFpGdu0Xr490iNxdSWVSXa10b8waGQ2SMLK8hO3/AhTBplMGJezsrE5ycXv4jF28PloboUvkSsgb4W1hDbCIQ/3ImxsBRSWtyd6NhykwHeJpzxuGtXisMQTU832TDZDMbRRTb8RytAPfN8im0kwlw1JMrHfVOS8hcily4RA5vY1jUEFeHx2c2avgAFd8FIaKSWW1uzpWUJ4PtDgr5BOYwEsm3fCvZFrl+SNn/n4184td8KomdkkwHagRd/jV8wlThhxzoQ1rNd8H3IA19asRIDrUBqdAXORgzsTbyxE6h6HzJ4LdccMHsvUQKsI5oFIc7wRO1gViwNBXX7gR+A6EmRoZde2LIw8wFhgZlCBUSLEo+J9RcBqiMHx8j6WM+ZSMYRXQrU+5D3Z149BkMJViinvVjnYQUIO7dtcQX9mxj6juzex+FFIKmhbM2YXi8dTgr8bSzkM/coKFqLnoLjriaRR4Wssz7MuXRK5h/9XdTSj92x1Ho4l/w2BJ0FHq+KeMGuqAu6GapDLPWWqijuuNVpWhTeWUiwxpLVB+LmfakXyooennhrQU9LU/wA/GyjkrmKL5gGVYj/0cMevz8W0e/Gd8CjYMLOi+1qlCngHxgC6KLkvtS4UqBkn+Guuwjt2AcLIzybQVx7oS1h7dme5ubk4byBjkqPZUoQ3xD0JghABCjIFMNTVBa9CiVFxH/ExOMdlEyIw5c2FjybWHLmSqA8GAXJqxbnn3kLPaKSEbA+MyYwt6xTThpu7nH3PmWtK2dGoJ0jdYhYMhWIdqmykb9sBY3YKnVU4VwBuGZAU3vmRyO4LsVBrnNuaYWyKY62DAWP2CxnPZAAPiwmUD7XW8ZuSgxshvvKGpIWP7nrsu7O0BHy32QX6iPQWvs+cv2S6bTNkmZS/SnVcvt7MJezXd3Hq5Q7dePH85mext77ycvmhZjgaxXTYELU9s6NePuBNgqxWmJ3pehDKr7mTCPQyJOY5eaJ7LG9z+jGuj+KSKI8fdGC4FQFWQFBFMmFDot3n1o0HCR1toQyFBFyxd9QkRwcgegX+C36ZUwwqOrdLGU5cR0zhFXgpod8ZP80qbTrt7K3v+yKjRfYOg5uguOKifXIYqAuFRu5HjWl7BLK6pPRiA7rj6dJeuWLyOdXfcmkQkMzaoA8VTEw0kAVO2+ExECeZGIi8KpGRH8C97ruilYfsbHNMooDSusAFpteDEx7SjUbQJfumBLdb+j4mvmR0GdddJgMynmPnRlqOlFkuOQOhSVAsA+yzueRRd2CRUR4OJBcFO71O1GidZMi1WV2upa06vmfempqw0uLgwG0IMKPbClQPS5StFDWeipA8JJ5qLWcX1POxafSjhSNv7glRl46p395zUFlQSS9GuzoLDi2DaW6wDS6iHb3GhJtXUDMZTzxpZR64QcOwWVVCBIWma9YgJfr71TfdPqzm0jlI6H9WTi3nCOH5rrU3pfqCcexB5fcTzg+8JeDGiGggLBh23R55tyAnhho4Ec7+SaJJjv0EnUxxEqjAGVawFXfuE3sJ6b7zkNG5w1fE9XLexHb3xtI+zI39vFsbzGxKC8hq6RXdXah5sJMmlvCLUXkmYiccMNkNp6RZRLb7A3bvYeJ5sJzuxngWxew01q/7mDi0Ln7o/ktMHB2JPA3AObTRFwuZIUcjmPcGasfvMRWx+kyGFLjjyKaTwKaTwKaTwGwkpxDPpK0zVjOQrxhUiSE9xhU9xhY8D0lNc4fI4e4orfIor/K7iCuGy+O7iCh3UZMi4Qne13xNPR3MXhFafWhlC7Xpj6qJUNmIUBWVLzL75GMNb0ZF8Jj6+wRjD5YW6Lxho2EPzXz3QMBY1nwINnwINnwINnwINnwINnwIN2wT3FGj4FGj4FGj4FGj4LbO0zw40hJ4pCIxzgF3U39zhAHP9HiwN5lRrPl34yCVs8g5lNmmaSqwsA/WrcC5i6EcpZOFNRv7itzC/4UYxcnBx8X8O/51MFS0YFOXtDT6E+hpSwTqbgLjZQTWiobYqV6GKJ+h+bsyTo/MROf35p99GUPVyzQc0hA7iHlz0lOAaEgNdxZO/ARS+erMbMS5WavUPJ+yFslRufxw2UA9d4UVJU7Oy1pyFpXMg6uRvXv2q1x5qRvv5XA1bLkCXAXGNpnMoBBUqQYINzYDb1dM5TDWCHUpTWZQ51xhlNJM09+BFVUSFPfpWt0Yf68raA/yOYUu/AI92+A1TBu/+tFJQQSgUz0SbrSefhhiL+wy/h80IMZHMqs4Q5we7RX4KU7mxeMOuTLzMHnqLQcAVlM0Ss1CClTAr4GMTCkO4mFn9FRvOS0UUM0rqEiXnPAKWzma4PF91p3Xy35xcvDt2R6upfCEpD3bDW3rmqF4jMhvU6HH3D1c821dbijlBWOQbahT/SC5wnGbx01HctSghz9jHJNS5o8bQ9Cop7JhQ5w4h0RsXB5ubO5sbYYK1NtbwgT58fSFJI8S1LI+7Gl0xN/3yuEOW1oe7oYtBXsDp9PUgK5V/pxh80Ai1vOEvjS9xpANTbOIV97n/VIf1PjpePTB642Jr59Wru861/f0WtP1FtN1GEPR3uk23ix237N3X4SxLY7chWwzEXJbH7oPGCLh2ZfK8tuBqxD6kMxz9/9n71t5GcmPt7/kVhPfD2gdSW5Lv+2JP4JHsXb+xZ3xGnt3gBIlMdVMS162mhmRLdn79AYuXZl90a1szk8ECQbBjNYtksVgsFqueAtRsH9YxZ9iPWJgKe/HPMGgt4COiUpB4BDYZhUpKAEoZvyA8ZxTw95sRmcmJA+jMDDY9hOfgpHVhjXXCpTbUdOXXLWrThXQ22Vklhr6u4kWTCIxIg7aqu9RiFqXc/dmE4HosLSm82/7gqtv79WrwsX85+P3m4dfB5VV/0O6cD7rvuoP+r5edk9O/rNEwbuYawcLj3Y64cH9117Q16ITESdTEMUtIbtUYBNc7pHszNnCVO9GHO5COqpymGtezSZ7DOBV0DgrysTylQTjBNHlEgiah8Xj7JYqQfibQOWAOMjKmohync3dzEwQbFxJZNpIdsfjSFvDxee11XoqOz3E/u9pMIBpz+VrUWoMs4NmuApbm/SOfPDaiXMicWNhMmIkLKKuo6JBbmWa9hZpgMQmm0cmO1qebU1DJmPAZVydiBsF81ztBEYVrIhuh3tVHt4z5CG9IyNtg51zrrApBhSRJaF6TNOgu+B11gaeGd5a5R6lsUbRnMKukmM5mhEMWCvCruEVa12en3bPrTvfk5N1176x3fnX+7vz6+N31u+tW9+KqW2dNxAS3v9qi9H+9bP/Hr8rF1dHFUe/iqH10fn5+3uucn3dOT7ud3kX7pNM+7rV77W736l3nsubqZCfOV1mfzslp9Qo5Hno5Ba9foYyqXqm32Ten52fXp6enl62T46vr9tll6/yqc91pn3auLt8dd991W73O6clVu3d2fnby7urs+N31Ufes3eleXnR6l9cbl6Ywc6RCpDszeXpZjpYtPqns/XT4Bwnd07oegf0XWHKV55GBli6tUpGB3fc/37309BPYR8Yk6l420IdPP98kI46F5GkIvtUHgqcN1Ov+PH2xgSO97s82jmFzBv6Bj3Z1jptHIUgtzsLzdb8m71QZ1RO20DGaM8KVsCkh6/dvDzNDG6EJTiIxwU/lN9HomJwM2+fR6fDkJDxrd8465xdHnU47vDgd4s7xtvKUMDnAI7mRSC2rpd/Dkhw+0CnxjWUo2WvwzHNWgUAJg3gmYjZrpLayvzcr6v//2Gl12s2W+t9Dq/UT/C9otVr/u3HNWW++Q0j9/IITNrbRxpNtX5y13mKyGtHtjYMHCuXqBEMhjmOlLhPUf39jtKokcZyDy9dvIxMmZGLq+5UrgxjuUYGwrnFlHq7MrSpAvysee1pbfZkr3FIofjwmiu0zapKE/Jg8kyZUYv5isQhMxl4Qsm0ZrlXl11TPJYWcKWLHlrUKefpiK3R++PRzL1dP5630sEhn+vFmoK/Uu0qFc7cr00217ZC7y+u/TEgcs6X3liW3+c7J6eCX7p26zR+dH1d8fdXtbfD9j0EQbL7ZU14sRL1rJ4jqMSvDAk+VkP2uedzQutDURqwK7BEknHVOTvnGlWeIkHgYg+BvMNMhYzHBSdWE3umf0CjGuWnRkXV2oYSMmaRa2hcY4uJCIsQojRFOvJx2jhMB9a2MTy1BJAn5C1Tmk2mSkHjji2xCnuXAute+6FI6n54uraPHTaIA3RO9sKaYsBckCfmFl+8vswrr+9aPqZQnxYkuZYWFoONEaQ5xKGPRhJkoa17NoanpLv0heJ7IafwDjmdJ046xSSNxULhfmVr7mfkeswW8LIuy1KlRHq4tDeTHSYt0ulOBo6LgiAWBM/1C+ETm60q0p0u1LUjpxmJmUGe/Sa+hGdu2XsPylL6W13DZSHZ9ru3Aa+ivRa01+Ka9hma4343X0K7Wf7LX0F+T78Nr+DVX5a29hoXV+U68hhuukH9Z/4/zGpo57tRr2N/KP1jyC2ZHhYeJ/xX8g6b7P/DRzq6i1Q5CU+XzrRyERxfHx8dtPDw9OTs5Jp1O62zYJu3h8cnZ8Oj0uB1tyY+3cBA+0Km6wE1nJX+ZcQ59Cw5Cb76vdhBuO+Ev7iA0k92tv6q/sWeqoJIrVIC6WdqdHYRsuhMVsNv6tu9TwAnJ5Snak2qGubD4Y+rvjNMxTXBs7rcVEhB0Nl5s08muHQzvAdiT/ptE+hIOp5/zL4C70p/muinKddX8XTwUx6FNfrQxUd6flsdF9TKQUUukGrMWwpj+Taw+xvpKw1k6nrDU7h6MpjTkzCEs83BCJdGSieNYXWzUFXhOySK7WWUB/2YTeANHXuoE4uRzStSNtZkJia3euyBD+7u9Po04S2STJFEBG6+ppvM5JVwdPFA+38wjw2wY4vDJb7lFPJYa/Q6DXpeDI+uOs3yqS/0XPVyRzc0kyOiM3KzwsLkrD4k6dZBkY6KsP7AMHcksk0/ndVmGq4M41ovnAU9KwpvGq0M8TpZSao+Ho4vO6Ojk7Gx4dBzhU3wUkovORdQiLXJ8dnRaZK8rlfx1mOy6L7Da/t3mY9ukf4dTAzkZU4JFyg1sAyT4OGBnkXpPQcqCdvyFaEVzLpTY12qNWqdnGLeG+KLVGZ55WiHlsa8RPn28XaMNPn28tfGPFlrUvFGAkxv2KZHElLmHjffp461oQBik+dJqLMWDISeQlI0itkiUSDAkwgmZkoZDPphhOTHtGbJ+vE022m4zXo2xbbPYeNzIcsPzz2N7eZxbwabEIM1i4OcUv+hgXeMgv7lXsz1ULFR81em08UsDJIKl0qEKOqo6g//GvPop2jqF38Ok0UicY2aRNx7N054BESwJTcULn3tmsJ7oXbH2YWKCbG0+pzBuMKWcbOcVZoDZDY4tKY8LKKoFElRojE5BAOecSuPxbKhVTJhUqpC/QPz0BPZbvn2BeEwwJBHOCKcsQtNUSCAyVLoujNOIRBUwC/qODB8PCdqbJeO9zM+hmu8F6m/lFZqZE9BLWhtPM3CYN1+Ve8alB5aqmAJXHi1OPzx68i/ZbK/AnMcfHvWlJQ9BYQddyL4dpfEbGmBfLbfhZqSz+JUKhGRIOlVb2iREQmH3VJBsw754vhIAA83uODRBj0qeFb1HeDsE3wtseANwLhAn6nYEpr66JHN7d7AGTx631Ee9qQi3z2uAn46Pjw41Ou9fP/+cQ+v9QbJZbvXshvwOVvDHT8mURYAUn+kZEH2BBCFJjrNlxC+vjELi0EenLKGSKXNeawA2hJM7cofBkChVYwSnofHIsfBFAcNjK+A0axqqKWQQSJKgP1KAEsoujqC71DlaxGhxkuOydF0zRxaDpb/Awg20kTvnK4uB1BIiRW3Jzzn5mmEhPKl583c5Q75wqwgKY5C7glC4x3JS6NvTrYZBe4Xh7ACpzEfIKo3j+PiopDmOj49yg1JXqJddGgnQgRFih7kI49W/mHfvqjn4dvReQdhKZ9df4eyC97zId0D4vQAGvzbonNWSMNUWdqiXqKZ9d97YbZkarmO1oL9hKt1XDa8zPVltpjiKGkgpQWQ6k9l4YOj6y0fTugAgn6v4gIZELgjJhzDIBdO2auGA/troaEoF/wmN9u1Ao+lL266EoA/Ul+tEOG32CueuzoJ8/KnS7tTjXXJu5f0Jf4K+oT9B32qBvu0wpPiTIV9ho/gjyDl37L/XVOUDx12xYkQOQ8lVjYBPtXkLmbNkjt39wvgZ8lUkTJKtkg8ooQPl6QAI2wfEVX+hRJgT1SJJoSkDtBqsXcQ0stdk64jCCcIQ72MMbjithecfnm4BAfPd4vV9Tai+P1H6KlH6vneAvv8AbL6vDcv3JyLfWkS+rw7G9ycOnzYqBnhs3YieaYGyv25gYGga1szI6tCyKTGAeGjI2cJ7Q/TR9V6Mo0tM2AIp5ZXA8659VYbyZSGbKuPQ3dXNq3rqhmrvyVvYBMQVovwCWsL0VlwSej+xBZqWC+ZOBpSxrjSoPh5hTnOD+uadwAU94MnHICcfxbnesX/TOMaHJ0EL7evV+H+oe//JrAz60EftzqCtLzd3OFR/+PsBupzNYvI7Gf6NysPT1knQDtonbnj7f/v14e62odv8QsIndoBMcbrDdidooTs2pDE5bJ9ctY/PDbsPT1vHJk/DMV0EIzyl8a68bh/6SNNH+/ZOxEk0wbKBIjKkOGmgESdkKKIGWtAkYgtxUE7OhS9L4/4+nnw+zAjHHlCitQ3hNmLjc13oLYcyKUvKOmnRuWN/4DkpcuuJ8ITsyowvzUH35oatQw/wYtkOOQ6Og1az3e40xyQhnIbF0X8nV4Ala22f6b2VXra4fy9yxlqnX2plbX9mP4ckkUw0UDpME5mu2sOYL2hpD+82NLA0+E3lsd0K2kVNuduhFgqLrjg5lXb37Kt5bDSjsax+u718v4lNpb7LF+fUHn5XeP681Qnan5HE431x4Nf5tF4ULLT7CwtEkzHEjCjTnOj/BPpYCBbqbDpdzjmxT4JwX4ALhZq1gxj26p7qzkwlZIf+Zb57r19GAzX7qllwEjIeKXI0GcdmthKPAWoWnlBTCESA5EG7eF456c9NmjQ/I5KEeCZSPUrRMNedqpGh3GunK8VlSPvAuNg96wqSCMYNEvH/EvLUQL9TTsQE86cDeLMEKFyDx2srK3M8GtGwxAmaJIQvXVVNAumPzOSyBRZo37rSDFXzW37+B0smuXp6OVDqbWe5Yno5TAIIyrHvVOomGkXUSJYdT05WoAxSpMOlDTskHo9BFxiSH4Y2y8MTbiu9gS/lJpe3Qv7s54akk23/Ogvx625XmFBKewmOqAg5gUt3cYcZmjACj96ydfHKN5naTQ19o/OrPG1xtdmZcwYmdNPTlqIBojZx7I77ZX39lzUH8Re4+XyYacBGPQO4Mm8zB5ZKQSOyeiJO66dxQjge0tiWKLTqv/TD8nNAHQM5Qhs48XFF16jk0beJ+3N3gG2EO2mA5He0Prly6sYgUPrcjyiHicgSXzC87jjscQvYb0JvrEnUdPt7f+T7QHtwfVF99T/1rw7Uf4CZi2P40BHNGmCJh3AScXRt9u1B7u0twwb4nOL4RYxTzKNA/3cQsunh5wUZTkg8OxyxAUSQxYdPCVvEJBoTRfowN8GBxWUlIpjI6T/+Bwi5geWZkX37z4PK6CAbmmifV8qvXz/+Y8/Oa++fW8DvVIDP7wIIN9+RSyrJcUGEjGeWZW5xsku6H9QEyUiA4BDOhTgsgdZ2f+v3N+WEN+Jv9lZU4mqh/mqZpbD5zJkl3BGOYzgN/d6qWi/ZHuGcePi/oMMOR/gziHn8QzgnA3hNHHiDE4OQEyxJ9I8uFMpw3fq6lRJ9Fl89z5hQmqP725U/w3+W1vcmQVMcfugjnQaHOkG7E5w2/DCePDtMoODH++4WWfgkSadw6dnpBrFa1HtB8WBrqFixNOXNUbVEFbvjalMW7BgdXs/YqIb9m96BDZwwFeVnWdRz9WGJ9AN2gG78N2dTg77YgSFq36fKfC2eHpuK/mKC5YCKgdoCNDowsl6UcUe9JOs3vX9WrFGz02pfNFutVmsLOJjdIptfIk5sDdFlCiZnPxttozNIplTSsb7+OF7YxXDSHxXWpciY6hUJx7Q5pIn6K7jzwjH9q/qPnx0fT9vtLdioBG+wU+E3t0jGkQhxUi2qpcmrmbRb7fNgG6FQ9BPCgzlJIrarDPuHfLnu0gEPQ0B6CGXccZLgYbzGXPcnxDgJlOW1wWRGMcOVxdh/7CsyOhyG42Rsnr5aQUtZ3O1W0NLORPhPiz01IWjKhESCzAn3Y83fKRNTGIpM3T6VxSYEEWIKb22gtWcxo9IyZUokp6FA+xpaH83hKT9LP9Fh3s9QqHzG6ZzGZExMMpd5JZaE66y2g4appJJR9d98FQ1HVzUbcyALZbh01ASM6cCkeoVsRpYYARXmlzXVQXSbkcHiOyhZqifByXZLTJI55QzwuTZ6yvpCa33lD2vdouPkBbkkBpASs0INVGeF4EGWcgKYZd/AEkkynTH+La3OgxnRuoWBt58plqlmtGJpZCD1YBaN3Hlt1yp8u32xIYd36yuHi/x7bL0tOa3trs7773/rHWSHvboaU4klnfvIKHPCQT5x8kSTMbio927ZYq+B9u5IRNPpnpbmvV/peLIHS6CuaWjeUYvq1KejCJIgig5IDcHg+pLQVUbrKGiZyNwX8CFGZESTfCKXopB9nFsjT4rgCyoQWySAGxuhKU7wWPuerm8+9h+CD3zcQDdJGKB9+INSnuhTv6lBUhIGqIAj6l21+BgnrlzLYsKUMqDCJkNKhiYknoHeB4+6ICEIp7JsQU8o62vGEr9EDMFTgXDImdCG84LxOFoiosk8ChIqZDBmc/BZNI0qAnEtKwP9OLKZqJol2aF14Va90sKAoFbFPVAU9hC05V94FgqB1FnKOJVmIRAnY6zrT3oqoB4HS0a86iZ0XVdysakY8hMa6nKaOAknjOt/NkN7ZTb+yHf6mxxn/htod23OiylHOYSihubpwkZFwlaKY5MtpxYDnHBV3kP9WmaRkFcsX24sv1rkZLNC5s0tR3kIJSvplPzbxtFYwjimLs1uhuXkJ+PyLHw8pWN9Jf8JSZ6SPHU9lxxZ5sPH6H8M1s7kvzM9YDkLFhecAuOUAzt1Z1XzKzGtPDfFW/+7ldMCopWrUSZcuXQrqSsGC4DbCGgiJM6uj2v5BADjui2ybRGNrFCHMUujTH676p/2GOFqk+IIS1wt0nfmV20LhLmmcN/MngFwFA3gg4Elqb4MiRD6rmElPDdraBDMOFMSkYXHZgne+pfm82r58EO0TBO1z36BZA09Y33dqeicTvGYVHSNp7SJh2HU7hxVasOs9xtFAd303DVa88kuhZHNH9ClEhP4iMWRv0vsgBTjAscSYPIaOav8eKWceX3YAWZX7NXduAm577fuaYOtU+hr0/3j9TbF4YQmBBTMRp2ZBoHXYNO+/FvBYANturrVpr0aGd904Ur7a9N+OBlnRu/qPnKfVtK3+ihi4RPIqlFIPfvviu2lf0NCYnhCjmONkwPaSP+m9rWYMC4H+ljI7CJ7iuv+mk4ZLTlt3bBQxeNevklOieijya+UXs0sj2HVTSqZtqQrpXG27w00nbehtuy10HKzTut3Z1I10Q/o4UPvgzJsFso6n2IAKRbkr6Wx5KwMtNrSQMv1OXI6XQ8hsJKrzvNMbn/V/6ogcpOMmC+t5lhQzZHVNZ6Aqr9Xiqc5N666fT8ChtqYj4CEIniZGvT4H8wTLjb1zNXVJ2tZSLVgDiJmuaQvX5pcPkQ1tPk69o4yjsBDUbbs5X6ZCIYpjctdllfUnd577fNeu3Wxt9lwPvQR9OC7zasHErKIVO6DVWMRkhMZTjYfjO1FJ1QlL04Cn9Ih4QmR8I5h5PBv/t8q6Ga/O2Mvb7llRJEvhau1atZorWbNDXq1zBU5PmNRtdrZajN7HJgxXRClvLiqq7RCh9ft6Z5F6NNNr9yR+n8xw+HbTSqjWO6MRSWV/8rObLR2uTOjLv/r1YrZ+3kwxbMZTcbm273/2nAXeSM2B8kUz8pDhqwr/Rr2zY3bG1v14DmBwimCyLdd4ozukoWOyCxmLwBa9aYdZ3SXdKwMQTJK4zefskd4Sddr7KC6HTuya7utNvpe36+maw4Yo8uz0+Xe/aGCrvkxO1fcpbbqHMhoo60OAfK8qdlpegjIMwlT6b1mogrT08z4DxazJ4qbOJUsogIeKrLp/3/9K+qZX16Q/x3ybt5rvScVpPxT2IzDkVzmFTTfBdrFlH+X2MKlZsPzTTgGG7kBeEH61X3SVa7kJd1d4XBicg41jKALDjEF3wxeBqGA6ebifE25LSExl+ks59NEGrBmquNSnFNQGphkPCVSTYybtypYNyLBJNewCvAH9c+GCX6AoYGHG8cAGCK00/vmvmFdSyDuNGpAFjE8XuWGBK5uKYAz1Sw0sbIzzqI0lNszEqL53N41ZJSZ6Oa2qtva4pLr9kfh8k72vZ4P1nTtBT5s2bNua1mdTd+TBYF4miS6cFX1OCzQ69a9f/p4a6D21VUFujPSCiNZxfQw5ZtXgMp6/d1BG9r5LbBwIm6ulDiVE5JIF9OpYeisWlvQJGbjTJHt/Q5/GBIs96q1lQEmcdnCv+vEJ3QFLxW3bLzUixuzcTCiMQk8RLgqHptX81X5ZHnO++8YgDua4Qzq+lfY+IIg5SCbIfJKQ3EDSWoxZE1udYICMpfPQLbweqBfPArJu0pjLziVxJwea8fO8QL9/e42QxPMhd7qSbAhCLKZhGF4obSVwyeEjJDsHUnRKmSDCQsvbN/mDcVMC9ulRJf3N2j/DnDI2Ui6tf6NCnU7TCKUkAXhB0EBI9GHKLCAruoAM49q+onWBEvrcCL4WRAJY3o0bQbP01jzMSsAhUVJi7NZBsmQQHrLnEYpto9nSuzyKHXr+A0lqgCvAXwynKXDmIgJY9LHR5qlfMYEERo+Dg5wi5aut4ifAeEJnVMK0xn2cUvNg7lPSA0UwOfwOGEa/X4Yk2nxHcttYlRpMC2Rvss4dilFNn7fjKG0p/33eTSBGA5P7Kq8QHhGPaMq29LerlgxNrtSbgVBEiH6Nss0wpEBdAoZj/QqOJT9LDUpR3NvQROgGbPxnvO4laeremMc7dlvxzTJvs9RdG3UJ6qdJ2t2FqVvQC1FRNBxYrS0HUJfZ0N1Wq2jHBnvk06r1SrvaQC1zu3PhifRZgo5kjRf7UJrSjuoAH0okIOkKQy2ku07R86Mo7GCo3pfRYG/G5R5NcUylzVoRUPZC8IYfrD+DECAFb+sunbTExWOOBxKOqfyZbCRv6b62FkjpJdoHLMhJJ2lJXCdYmUeqWudcjABzdhAbnMkTSYcNFbbDuDcoKADhEuAeeF1Ap/4eH35ayfKeqq6NerolcGGd9W629g/oF3EDExQ5wNBxQxvK0NdZl2kq3g2OQ4pMVjoYBBTQwt6UdpVg3Xq0/Ix/8b+WMEFIDcwr9t5Fmzr2aklQ07RZXlUajDFQ9VWS0RZtUQlEDlSgCcEU3mEy8sLS0FAQjyT+oqkeQeHDEusfS50nE2OlGRV6sTdmCDY6zFj3SP0oxgaoUf4qv3YyI8N/tp59CoUNNCQhFjt6UzRez1AYmmiadIkLwKYxxSClM0E2MgZRlutsH9Qrl0nU2QFHk3I8wSnApC9AH2Rjfyhu91rEnNzhCxf9U4P0LsXNMFzY40JkmUj6vPWGAOSTGexhm0keU3pY9XSBEVYTIYM80iYWDh4SmrGBHO4+fzBhl5GX9mh4nPuMnd7uMfhEx6T90UVs1xjZJTe0QTzlxrNZAxPrp8E4TfJLJUPtE7vjMk7FtVr+JA9+G/RMKVx9FuhxuOmjbvKOEy251aXcZ6C8F5CqHlf+sVZN6bCCSz3JxnWYnavlKW3TVuZoS5u02xOw3qCmTW9Jcm4oFA3J1CTU6qpkZI7/EdBB29PgiZ1SHA6r8k71ZK/smldtqtffsPby8pVIvlLl6WJ3L7ps+T4JhmxrVteYxqnvB6XvbY1eXVNY1JXF13TBMdKkaRi67a/FE7XzVrdRDG5mc5iSJzAtdSIIgHKr94630xnhAuWQOe3ZE62l7IbmxdSs/nsUuPC1mh5n5U42LzZ38hLTeG6xUKqY/IXxqJajfuTVEZskdQjMH2NVXLLxiz5Jf+gv0XLm5rtzItY/SHXMknggKmrBu7wM52m0/usZuE94SGpsbnuADu31tx1036N5bqjyVsNX1N6mHAmZfwaMvUX4z1ZGBGqIYFZ41pr8J4sdJWBWtvmPVnUspTes6k6h64B4zQJtzeN38ML5tbNPsTRK2b7IY5qzfaDeVVQ53atRbrHNRS5tzleefzeczKnLBWvvUlYOjUbmzzcWyq235/199drNpdpW2/5TNsacnqfe2feol2K6xlY9ym+ZzENX2rw939GtW3ZjwSLGs1AA+C6nfbhMvwuZuHTg4/msnF7DdZcS55M29pDN5aZ9ibUMjosCXgK6NZxwVgKdRePjhMsU16jY9uy5u2nD08p2zeTmNfzctVz99SdnWSzeqPUddx1Wdh6Uq0p1DXBTfNPoqYV6rWvY4o+9GuM+QHzMXkNzzSBWr4L3bT+VclrX3vm+mH0FTOvvdpZ81qLDXWdcNwnkDteZ/7siSRXMZnj2jr4geNETKmUJDIHwvbbvS4D6h49vzP+JGqan/r9qV6zTr1mR/WaHddrdlKv2Wm9Zmf1mp2vbPaXYhv9SLflU32dcJLs8VwHjehKbLQcBmPQs/zYoIrHRTM8sdW4t3wetn3ksHYNWuOLhzhaGlw4wUmScz/u9G1f92aD7WhowhUMKrANxgv8Cng0r4pY4qgpyl6goXlsjtlYPNq0OUDjSGy4ZxYTVsEJPYbdypeZ500vH08Ws3EuSgmgZxxTuNLLeR3nochC44yFJg67rR/RGYR/6p+0bOjfC0anAJ+JpDiOX2wF1QJBTnA4MSEqU+3pM+uz3/nXUedfOXo2bqoc16QG1fnX6fG/VsdWHeQjA2CxybMsjAkqDg4JalWuZowliQbfYoiPHZNaRtgEOXIhSyRnMWwGqc7lEeEcNnRgZAhmZsOAFlDlXE6IqQMoJ4UN44cCQf+Uo0ePLY++vqtIVpyF+TvZm+su3UMxmkErLPSAxZMWZf0VpKU6HG+j6armq7MhYmbCmg1VPIOUJB0PaiJ+eSEeOEctZuMxiSr4YvMrBuPNsv2+jGg5PA74UQ++tJWWnQU27WVWMSGv3PWrFtv0MaCuxnA3porxJrDxY5pIOiXWV7WK8cluY9IqDndPUaP9ojgx7qpmKeZXSJGnVg8qJiaxeNrlPlP0v+1dptNDDNi57tZkNZVPkn2WoBkn+Ri0vKVQDIEFQFpznGoTzobPrdgMcgIWyfZbokTRhd6VKH3ZKEL0+ihCL6yvgnWmgKFVVK+S6awuWrPdPGl22s2jk+P28VHronPe7LRO2mftdqfdaraPLtpH58dHpxfNdganugFLrPxk+GyZht3v3/QOXGZWGLI0ka50i4m3LWhXKkrqFWmggnyEf8IAAI7Fc70v+jc9sOqAhQ19noNRmxWALERLwg+6UqgJmdR/Ujx+tCGC1kRi+nKfGcteBQJvjC8sRS4p0RtwNlq1nfo3PdFAnMwpWZj9P0ajQlxRqOPrhTZyTPEAk59g6gMsE50NFfuKhS3X2ssvWvVC5QZRKNq9A31sqry6ygpLBMwb6/LSsrmhy7wL6O0PEgNyvn7AFSOcl9w9rzEwHrwScFkKqhPwH02QLDV3PgOeRYS7fngQWibjzaZ0ZDlvt2SMw1yKjk0bXpb+pj8gAmn4SJZLKjsNng3gNqO6IoiXE2SB4oHWPKt9lbUPPIAWnflrKNAkIs9ZFPBiYi6Njy5jOTgdSDY4C3RKlcmcAscGkUuuydXJw3I9JJbOOQqyNJ+VYFHla9o6uqUGK+lXJTKs6aGqyco+Cv6pNeQLX6+kXPAgraFc+Hol5ZiNt2FJzlm0Bv1LCDwmA8I5W4c+B98EpsUmxI2rJvEjNtYMvejdWUN/mfNgbS/LGq7sL3fHXtNF7tuVVKtuqGuIVzVZ14e5zm3cQeGKuZK8voNtIaFVl8PVeJnZpWsNae/L1RThwrA1R4r3jJV9VFvYy3qyXVW3Wt9RzhRaM51yg/X0Nz9Nip+vpF0FUrCUcv7jlXSfp/E6hVaV+V2k+X8BAAD//5Sx9Ts=" + return "eJzsvXtTHLmSOPr/fApdNuKHOdsUD4ONuXcjfgwwM8TamDH4zJ5Zb9DqKnW3DlVSjaQC92zsd7+hTEmlegCNTfkxy5zdGbq7SkqlUql857+Q3w7enZ6c/vz/kCNJhDSEZdwQM+eaTHnOSMYVS02+GBFuyA3VZMYEU9SwjEwWxMwZOT48J6WS/2SpGf3wL2RCNcuIFPD9NVOaS0G2kt1kM/nhX8hZzqhm5JprbsjcmFLvb2zMuJlXkySVxQbLqTY83WCpJkYSXc1mTBuSzqmYMfjKDjvlLM908sMP6+SKLfYJS/UPhBhucrZvH/iBkIzpVPHScCngK/KTe4e4t/d/IGSdCFqwfbL6fw0vmDa0KFd/IISQnF2zfJ+kUjH4rNgfFVcs2ydGVfiVWZRsn2TU4MfGfKtH1LANOya5mTMBaGLXTBgiFZ9xYdGX/ADvEXJhcc01PJSF99hHo2hq0TxVsqhHGNmJeUrzfEEUKxXTTBguZjCRG7GernfDtKxUysL8J9PoBfyNzKkmQnpocxLQM0LSuKZ5xQDoAEwpyyq307hh3WRTrrSB91tgKZYyfl1DVfKS5VzUcL1zOMf9IlOpCM1zHEEnuE/sIy1Ku+mr25tbL9Y3d9e3n19s7u1v7u4/30n2dp//vhptc04nLNe9G4y7KSeWiuEL/PMSv79iixupsp6NPqy0kYV9YANxUlKudFjDIRVkwkhlj4SRhGYZKZihhIupVAW1g9jv3ZrI+VxWeQbHMJXCUC6IYNpuHYID5Gv/Ochz3ANNqGJEG2kRRbWHNABw7BE0zmR6xdSYUJGR8dWeHjt0dDD53yu0LHOeAnQr+2RlKuX6hKqVEVlh4tp+UyqZVSn8/j8xggumNZ2xOzBs2EfTg8afpCK5nDlEAD24sdzuO3TgT/ZJ9/OIyNLwgv8Z6M7SyTVnN/ZMcEEoPG2/YCpgxU6njapSU1m85XKmyQ03c1kZQkVN9g0YRkSaOVOOfZAUtzaVIqWGiYjyjbRAFISSeVVQsa4YzegkZ0RXRUHVgsjoxMXHsKhyw8s8rF0T9pFre+TnbFFPWEy4YBnhwkgiRXi6vZG/sDyX5Dep8izaIkNnd52AmNL5TEjFLulEXrN9srW5vdPduddcG7se954OpG7ojDCazv0qmzT2nzEJIV1tr/xXTEp0xgRSimPrB+GLmZJVuU+2e+joYs7wzbBL7hg55koJndhNRjY4NTf29FgGauwFN3VbQcXC4pzaU5jn9tyNSMYM/iEVkRPN1LXdHiRXaclsLu1OSUUMvWKaFIzqSrHCPuCGDY+1T6cmXKR5lTHyI6OWD8BaNSnogtBcS6IqYd928yqdwI0GC03+5pbqhtRzyyQnrObHQNkWfspz7WkPkaQqIew5kYggC1u0PuWGvJkzFXPvOS1LZinQLhZOalgqcHaLAOGocSqlEdLYPfeL3ScnOF1qJQE5xUXDubUHcVTDl1hSIE4SmTBqkuj8Hpy9AZnE3ZzNBbkdp2W5YZfCU5aQmjZi7ptJ5lEHbBcEDcKnSC1cE3u/EjNXsprNyR8Vq+z4eqENKzTJ+RUj/06nV3RE3rGMI32USqZMay5mflPc47pK55ZLv5YzbaieE1wHOQd0O5ThQQQiRxQGcaU+Haycs4Ipml9yz3XceWYfDRNZzYs6p/rWc90+S8d+DsIze0SmnCkkH64dIp/xKXAgYFN6LdC1F2rsVaYKEA+8BEdTJbW9/bWhyp6nSWXIGLebZ2PYD7sTDhkR09ijO9Pdzc1pAxHt5Qd29llLfy/4H1a+efi6w31rSRQJG967gYt9wgiQMc9uXV7WWJ799xALdGILnK+YI3R2UBOKTyE7xCtoxq8ZyC1UuNfwaffznOXltMrtIbKH2q0wDGxuJPnJHWjChTZUpE6OafEjbScGpmSJxF2npL5OWUkVnOIwNtdEMJahAnIz5+m8O1U42aks7GRWvo7WfTK1kq/nPLBUZEn+Kzk1TJCcTQ1hRWkW3a2cStnYRbtRQ+zixaK8Y/s8t7MTEG3oQhOa39j/BNxaWVDPPWnitjpxHN+1t3lSo0YEnh2wWj+LJO6mmLD6EbjC+LSx8fWOtQmgsfkFTedWJ+iiOB7H49lpmwOg+u9Oj20iuwXTi2Qz2VxX6XYsxuiGDFMZKWQhK03O4Uq4R545EITWr+AtQp4dnK/hwXTSiQMslUIw0BhPhGFKMEPOlDQylbmD9NnJ2RpRsgJ9sVRsyj8yTSqRMbzIrbCkZG4Hs9xNKlJIxYhg5kaqKyJLq0dKZQUer+SxOc2n9gVK7H2XM0KzgguujT2Z1164smNlskBJjBri9FZcRFFIMSJpzqjKFwH7UxByA7Qy5+kCBMs5s6IvLDBZ+sIUVTEJAs1dV2Uuw63d2Ap3JeA4VhGVKQhXDqLONjl5I3wdCN7tohvo2cH56RqpYPB8Ud84GoXngHo8EyeNdUekt7W79eJVY8FSzajgfwJ7TLrXyOeICaCmXMZYjlid1+9IV+UjIGOpQu+TKc11fSNkbEqr3OCQzR8be/A2WhPM18HDz1JaGnz9+jA6g2nOW7rEYf3NHcrEgXvTHjZPj1Q7AuSG27OApO+3yR1BC95UempzSoJiM6oyEB6tbCiFHkXPo+A44Whu49Jqn9Nc3hDFUqtXNVTXi8MzNyreTDWYHdjsF/bxCDI4gJqJoDLYZ87/cUpKml4x80yvJTALarulYyGdqdCsZEW7xqRe11FgM2PawuGkcY8lo6jQFIBJyLksWJCPK416hmGqICveVibVSq1ZKzb13MqBIloL1Hj03M9OD8SdnbCgB4EeGCHAHUsLlpj5ba6niOFHjdYRkZ/A3l6VrixC3Ki1AsaFBe+flcANAH0MNSxvyewZrMavkKYzpBWscL/W4UR7E1IwPOF4G36eYCqEw4OiGs0yollBheEp8H720Tipjn1EeX2EQpTnCDrIdkaSa26Xy/9ktXJtF8oUKNyam4q67TiZkoWsVJhjSvPcE5+/ESw3nUm1GNlHvVCiDc9zwoRVLx3don3SCi4Z08aSh0WpRdiU53lgaLQslSwVp4bliwcoVjTLFNN6KJ0KqB21aEdbbkIn/wQ2U0z4rJKVzhdIzfBOYJg3Fi1aFgzssiTnGuxWJ2cjQv09KxWh9mL5SLS0dJIQ8o8as05MA8Nhza/njCh642HydD9O3BdjRFlTyhRWCa+FyKxC2yFejeOEl2MLyjhBsMYjkrGSicyJ+SijS1EDASq927Faikr+113gVCdPd3gE1WRhmL5HtI/2Hi08zdcagPxof0DrTvCwuDPpSAJZZ3er9nYagCFhD6B0OB6O4yeNOWdMJik3i8uBDASHVmbv3Z03VkdgNO+CI4XhggkzFEynkbEiTNaB71QqMycHBVM8pT1AVsKoxSXX8jKV2SCowynIyflbYqfoQHh4cCtYQ+2mA6l3Qw+poFkXU8Ae71emZ0xelpKHu6npHJBixk2V4X2dUwMfOhCs/jdZycHVtP7yefJia2fv+eaIrOTUrOyTnd1kd3P31dYe+Z/VDpCPyxNbNkDN1Lq/j6OfUOL36BkRZwNBKUxOyUxRUeVUcbOIL9YFSe0FD2JndIEe+nszWJiQwrlCiSpl9sZwwvc0l1K5i2cEFpU5r0Xb+oZC8HJSzhea2z+8hyP1x1pHIJxKE7lxwX/D0e5QwAU5Y9KvtmuHmUhtpFjP0s7eKDbjUgx50t7BDHcdtPVfD2+Da6Cj5mDqPWm/VmzCmoji5T0whAeaxHlyFoQ0zxHhsogpC42x3pDjXYsnZ9c79ouTs+sXtfDZkrcKmg6AmzcHh7dBTRo2b5O08dJ7rG/BzYVVL1FLOjmzEzmdAQNTTg8uggJOnrFkljhrEs1jQwFBbdMbmhqujXBWIp3TKrVgfhQzkkuakQnNqUjh6E65YjdW5QEdX8nKnugWxu2iS6nMwwRcL+Roo3i/1Btjw47/veADddsHyHuNVZ/h258k3W034ejsyTJC5+37ceb24Dbit9xJG6ZYdtknVz7e9WaVmzmfzZk20aQeRzj3CBZSlizzIOtq4sXRsP8/1T4evKai4ZwuOpUKwkiSGcj2SSqLFcI1WYk+t11PGE7jXEoZM0wVcBWXiqVcW10L7CgUtV9wxEIYUTXJeUp0NZ3yj2FEeObZ3Jhyf2MDH8EnrI61lpALtbCUaiQaDj5ye/Xh9TpZEM2LMl8QQ6/qXUVtOafagF8DY2lQMRfSEFD6bliew9ovXh/Vzt+VVCbV1Ur3Lq2R0SAJI8tL2P4vQBFsOrUH+JrZWZ1M4/bwGbt4fbQ2Qm/OlZA3wlvJGmARh/qRN0cCikpak70bD67ILvG05w3DWjzWGALq+b7JBkjmNoqpN2I52oHvG2RTaaaSYSkm1sjQcC0VmoPt5OijKhiYSeT0No5BBXl9dHAGoRC44qMwVEwqq93VsYLyfKDFWfGfwAReZkm6AEyrPO+RJL9Lw4xd8KomdkkwHSgY9JrynE7yrjB7kE+YMuSYC22YI7EGbsDO+tUIEGYfngJxkYPF4HTjUKYu5grX513lYJHcKHNqrATSQ6gI54DqcrwTOFkXiDnV88G0dcQU8B07j+XJqVSKWdG3EfA1RcM4MChBqJBiEYePohAXkcp7zVwwyxhWwTM0aMMHu7pxCDJMpZjiXtG8MScVmb2SakcO8VHBfUQ1SExTh5SCDgZzdqF4PAX5q7G087mVttGqAsGFXHQXHfE0Cjyt4TmWFS4vOI79F7f7jTHRgCDpBf8CDEXAGTpVNAQf12GV6ADCmCSvTkBkErk1jHJK3jCjeIrhTToOn6KCHB9uY/CUpb4pM+mcaTAqRaMTbrSLXK2BtJTbDLhuRM5yHcJymiC4cVUlXEisYoU0IYiHyMponrFopjZkCBMlLmbTL8gTmKhfdQaxZmw4DloPBMGpbnKv8tlhua5BdQh7iIswBXPtcFx/9aJGEM4FQbmx44RnIdDanegFyfh0ylSssIPZj0N4sb0H7TFcN0xQYQgT11xJUTRtRjVtHfx2Hibn2cg7ZYD+ydt3P5OTDEOhIUigajOXroD64sWLly9f7u3tvXrV8nOhiMFzbhaXf9aewMfG6kE0D7HzWKyg+xFoGo5KfYg6zKHS64xqs77VsuC5+LXhyOHExy2eHHnuBbD6Q9gGlK9vbT/f2X3xcu/VJp2kGZtu9kM8oDgQYI4jTLtQR/ZG+LIbKPloEL3xfCCKmbwTjWY7KVjGq6YyXip5zbOlHNGf7eOCs+YnTPzhjPN+6I0eEfpnpdiIzNJyFA6yVCTjM25oLlNGRfemu9GNZaFRfKBFOZv4Jx63+DqWGbvUfCaovTob97LMGDlv/HL7BX0xZ5q1E0Qa4hrcdBMuqFrApCRMqpcPOcTg8HtEqImUOaOiD20/4k8gydIShAWOcZYOFos+F9XT9akZVbHVMOwt8pIHVRtqqsGCXg6yjLuQti6WgdKZstdGakV1BKUnDr1COdyliczstZ2qRWnkTNFyzlPClJIK87g6o17TnGexR86qUarSxs9HXjN6zUgloqgtPIb+1foVfz7r8cOwN1STSqRzll6xnhj/43fv3r67fH968e79+cXx0eW7t28vlt6jCjMSB3JcnePwDYYdSD/wuzoMgKdKajk15FCqUjbC8O9dCqCRLXNf3nE8Vs+NVAzl03gre7aHpPOmyfrvdk8pRPrVr9/2HqRhYeKdD20ageRq+VitNYIo6uKgpMgXzRysyYIYKXONUWwUzAyQFcPSK5RNkQ47JPOwgwzE+pl47ec7aGKBK6XJga6ZsiJfRujMCuGRNjdnNQ8Vpilp9h432kD+PWdpGcTUFwcweUfG4c6Iv7wjDjg82Iz1dFGYnXzeKMOwZKldjQMyQIFE4Ozjzhsnp/EgUXJ4dFfNWV5GVg1QdNCLF4bWToUSC3uzGh7MVsvcWEMaHurF86wp/PGCzgYVRmOhCiYLIUQIkCW0ScVzY/XAHtAMnQ0EWU1ZDi46a5mZo5T1u6ePUtfvSF5vi+kwq8sDb8w74HbUi66jJIIcijQ7lCCKo5OCCjpD5s91TQgdIQpT5iM+EoUcx5zkqPX1HbwkevTu0HRkuNHTEHaEbvGNZuZ4z5hRNPp9cejIflwc+rcYKN2I814qWjrcMq7axCNFS4dhIWr6KVr6KVr6f3e0dHwwfVCNKy3T3q8vFTIds8KnuOmnuOnHAekpbnp5nD3FTT/FTX9PcdPRJfa9BU83QCfDRFDz0s4W3/T3hA2zRrxwqfg1NYwcvfl9rS9iGE4N6CHfVNA0ROlGxhm3UjDZ1LgxkkwWgIkjBiWGHn+FQ4RBP0Bs+3Kx0LfS8tcOiM46EuVTVPRTVPRTVPRTVPRTVPRTVHSb4J6iop+iop+iop+ior9llvbZUdFZjteL9369fg0f7y7Lu0zEFcSb5HyiqOJMk2whaIFqlEe5pJmvfOyKrIJJxv38hoqFq1IXF2l1JaMkWdFzCkmOjXlWXIFcHz6Lhh4fSzepQjV8CPBgBseDWvQ0zz3qpjLP5Q0Xs30Pzd/IES5gPefiys23IM/GSZbn4zVX+M6riFKQ37jI5I2u3z9HcN9iZM6zcaJl33vvBf+4DjJbZ+0dWBpgLHI+6RuwoOnb8+Vdgc2wvOQ7intrQf4UBvfth8G1t+yvExXXWtlTkNxQQXItRD/FzN2CJysxJkW2OxBDfHO0i1M8CB49p1sDAXT+y8HWp0G0vftiOJi2d198GlS7zn47CFS7W9sPg2ogDt3Qdp1w074261KaBS21N3rHPB1aHUlBMq6vusfmiinB8ufbiZd8l1huSc1Qat1PVZ4jxHaSztpbwB/uf3CC5QesOf18+8MnLQgsjCUVi4GWdRLKzuA0nQ0a+WSYjEBrjqLkOVuHGNdHvYhLlkSADb3alov8ExZ7RuM4gvsXZ4e/7K2V/viru24WTn/gyl4kz5NXLzY3k62XO1u7D1ii7+BzCWsdNNHNLfRziPX87ODk9CI5/o/jByzRNdAZel1ums9Z30o4jR8+Hhx7NRf+fhsUVuRNK3cjIFggRKOs/tHp+X0WiJ8asbZ2wqPTc/JHxcDSYAVVKvQNi1p32d9dYrYTWBmHZNdQSrmuee/HWpBScQm2hhkzWEkah3WDPhtnQkOa4z48P15zTXQWfpJ4dLA6+1LMaC6r2xm5EXHaEDqs0VlCdWybcDCgWH3DFKv3Di2nXOM4XSjx1fHaQyKDGyt+9Jj11QNBqFJ04ZGBWHbvo5uIpnMHBtGu6rliplIiMmj6ZniuDFgkMTAC1u0rtnAoq+N1/d7gFmjm+7I1wpEnC3J8eF63zXiHJdxxrLmV4aGtQmwEKOrl4I9+ckFu7FvHh+du+HYEkt1mS34Q9YR+fOxaAr80Q8rtc57MyYEhBRe8qIqR+7K2CrhFFVbjiztoje0sYwscpP53lsF17RsZWWErDEntaCkIK9z4No5Uk1JqzSfob8igIrm9+WltKnFGQx933A8o1STFjjaNOPYWRSZpTgeLWMecfYrROWFDfG5BhhTDofERxpRgYf8Oszw57QU9qtswiIsboI24I0YstDpFusPBKBZN8HF0+GrJRKa97wWyrIFheZTEA/q1dwTtrc3E/18vFoaMW7xoOuEtxUXpyi3QSYll7nWzcRB1xhA5JYenB2+O7YGYMIss+35+zbJRzJxWVzUZo7OkZjEmyl+QwjdekkoxXUqL4mDZiwaBc5mQk8CrhDTe094e0zc3HEN7Bh8sP7Y3D4PGpJ1tubm5SW4Jw/A7Y8wyLufbApUs7iEzB2LIrsFCajk3rBcQ0LsJ3uZE03nM2NkU+FIjz4LrlKqMZQn5nSnpc+gLsNnMXSgqstAaf5MaaThFT1x7P50OWMfgYl7XMPhEFgOk2bQYMJoxdTnNfXPIIczfcGfLKdkmOTOGKeCSODOBmRuFSEpsZVQXO9gnBwcjcnE4Iu+ORuTdwYgcHI3I4dGIHL3tkKz7uE7eHdV/NuPHB3NP2x2yS8PYvdhNTTWYjeuWt0rOFC2QAkOb3oAE+wiIZZhcEw0EWWslr/NxkDnoHg1qe2trq7FuWfbEFT/64p0nSgo0l6MYhemwzhx9xQUE0KEA25BpSWhpGkcvQS9G43FXN4fBwHIcBmVkwAw4CeMxb8XRr++P3/2jgaPAGb+YxODa/LjbAvWSe4WDBgMf8l6EC7EFWnzvBXNaqyCTkGK9VFwY6NeXzim0tFaaPJuwXN6Q59uQeGchIFvbL9ZGEe1L3Xij5uVBQ8J2TEyntLRnimpGtjbhCpnBHB+Ojo7WajH8R5peEZ1TPXca3x+VhKSmMLIbKiEXdKJHJKVKcTpjTnfQKKPmPEq/mzKWxSOkUlwz5YKDP5gR+aDwrQ8C6I85n8aD7tiwzV89FvYp/vWbiX8NRBGQPyQxhElAxastC26BdQvBDol2GYUbaA4qoUusAKCBEYaZRjVqdDXZtuvcShxWgDRGDZzXEDacjF57rcdYGSGJCEmMojyH7oJMcdkv+PYj/Sn6GNnfU/Txg6KPa/r5MgqC05PuFioODg6akrHXVS8/J4fooGOiy3NycmZlOAa1wMaxaWPcsjH4H8fe1Odoh0+nPK1ysCBVmo3IhKW00sEyfU0VZ2bhlaOYUAtqtFUK7VAOrIQcfzTKt/wD+KIKAx5Qg+3PJQGraISccS2uQst3boI5C3slZOyjfbuwVBIPjSIBvgS/M6o5hKiFEevmeiipWOF2Krt1FYN20zadNL/bam8wSMJfQhHwc/WnGp6+hVigBnQDno3V+HAEA78P2chGDtFWJgX6a15e0MOwLtcTOQgglGXGr5mG7oWRa6HRzhAeSxWLQ6UyocMoU4St7SNYFooaAG/wd+6ABhCt+aGNOWChZMqt/5ks0fqaL+wQWspwrzhtDU/HWkIORAb1WlMpasXVYbV59m93VHh7vtXjHE/o8NJg+A3V9dKGC+j48D4X0Btm6HpsrPbVmZw1evnCfve1mVbsj4orlkGhs0eIcDg+PA9+VLjHAn7tYjQxMiFjlurEPTTGCH8PRs0EQTAC1lNpg/UJIdo777QPJeS3ORO4Z7CB2LU/yGtcZDxlmqyvOyOpc2BYgCw+dc5nc5P3FaWNVgPvR8G1ObMs2upvyrUppdk/Lag+TTGds4K28E8873dL6BqVk81kM6YcpWSjENhx+GLpEGZoQ++dQS7iEsh3AXaNgMf32NC2QPkBn3NuoLJkUNAlZ1gC2aLZMwIIwk+pvYVu8PYJdgzce240y6e1ok0Fjv4AN91AyeWATDT6tNwJCOCdNrhhYvpDekgPBM7QdA8YUfB9z2K9saoxsDY0vbq00sVfIQ3qAoMvU2jenLLg+wGMWmItc/ARso+tfkZfSNANuzvCk+ZK5ZpgYovDF9jHlJV1pnHEKv5Jr2mSUzFLTqs8P5Pgjjj2j8c85LrVUfz4eomG4qGRb28hQd8duT84PJdeXcGag4qnDV4QWM6BfbTVstyyh/ad7G9iaAhWMDPHcxp4U60pvJaBM8HFwUWaV66OO3htqAmuMtC0xKweI9QUtxPVi3Dj+aGoT+ewVKaML2LvStPXDdadTR0VmpDW7sb0/m/Q/eLE7RGW9+rp0j5h5saK+TS0Y3byjLoObmaczDU4Z1DDP82ltms78DtxP7qxlIQ/x1JBbS0otpOTglFdKVZgFwAImu7DbPQYBPoaesUCDcdojsmjxnHBCgkRKkxDP203XFZj2rXVvuaBZxlWgCG/Uiwh5wz3fIzl5+xFN8Zlc+MKPANT0HUL/MiTH45wHJHgILXzamP19MYlvlw1/iWq7XyyroCjBwXBOx+a9feclSPUk8FCk3FYhIjeIidQ+hNIoBZB51R4vPpO6OPadB021zKMMSBknWbZeETG7tysw7lh8NWU52wdxfxsjL4j70Fp3AYg30dBK1gfs8yBwvpq+FeaqfWSam2RuY5hSU2ZwoE+zHZgAgwcpCmZWjXIypKHOKcvkoaBXqhhg5RKDe5IbQsDZcUZtNzW2IE88GTOmaIqncdxxO29qcU/3O6VCZ+RSQX1NlYsfNGInOmmUS2SyHPDlON2rSn23c6OycJdFkFMx94izsrlHgtjQtoENwvnO0PJmmvkWfki7kviZrSbMnad/l2KkWVj9YhEVxMPVpvqw/hejXPzgg2N5rm8sRBa3TJtbpS7d9ySIlMcNVYOga0J+kaEya5qWJm5FfWiulu3y7iPZ0o4cfJlGrk5QzQdLApyxUG/hoy4CHNRdUsfslVpFi6NjOlGZw8nYGpSiajU5YgoNqMqy+PdB+4PTxMrx1T2D6mIXR7ocaBP4UUjr5mCW8Zq8UFk8pIdj7eE+aBNlHPIyVF3G3Ze7Ow1kY8c6B5ekNXGiCZ+3WnAQTrtaNgG3I83VksNvBVuxSlXUUKNYhR4m6XOGeyJVPYzWFFKXrIcej/cQtMZtzJE6orn/F+oH2poUSLboCb+ysRtUE1sJQ+3OUNro5X3fDGeEI3TvlJOBCnslay5qVAZHrmQQ3MjSZjWHbQJ61G5kfX7j2kczSJ8pjVmLOUpJBS5Sjw5hNWgYBRbm1yEgou3RBKvmUQstsC2wKuAdNyTkLGbEW4cl2hBUkjBjazj++ohVldBLfY7Zj/6Xi5GkivGSlKV6EaAl+LD1cSqVasR0iYe7dWKJy6l+Sje2dq9G+Wmx1lV25tbL9Y3d9e3n19s7u1v7u4/30n2dl/+3oxCzKihmt1XQenzKz7gNK3ANNHACLpWwBFeYClbKjDYzOlTVoWQyl83WN+Lpo17JpezkdP/cjlbG8WTh1vESCfjLOratdF5TWURld/Ddlc12LDpiqWyKIBnQy62kCZYtmB4K/c05gZVLwTJFTKr8pr0sYYHJmuj1ENJJrH9legM03PZlDSdsyTCRdjeSi1T+LGnQlbrTS7Kylz6HwUV0kXCef2vMvEDVL/hec57n0EHG9DIVi/hHLmpGzY0Ap7AMG2TkpBPIdbtmcfPzKpNijkfpKmdfo24xj5e5BkNzC4yrwrYPeWd6iJMLBO0dduVUoPauU3aFwnSm704/fderAqA27sGfIZyAupiq6r9gGU9fqF6Tp6VTM1pqe3h08Z+M+VixhSE26yB84/euJvMSLsBFP1Ske2nkEIbZZcPJgMwvFrJsU30dT+pvr8Ofjw8+mJWvZMju5pQMj1Sxlow79Gd6e7mZtaETMxYN6l6eZnkItwJQBeBq1Kl+LWPwGRQfFTR3AWUGqk6EgbIFr7eBAgD4/rCiWXxFl16cSFfEJmmlVIsSxynrG/iXMvO6A1pKp6gYBR7ovu8ZUzwsfd1VImfBAGKaHrTqwOfCKdU2tOFSr9Vw7SuCisxCEns2kDbGQVJwd293jU1V1LIXM4aRT/sVSOvfFgA1/sNXJH/r724+hu/3eOl7uzdZGtz6/els6OveJsZfWN6rg/g+iRFF4076FG0A637Udq2SUhP8WJD/LPp1OH3XBcDcKDFFtrxIkecL1IdHKK13aRXg3bxwV5rQX6HYvus4npOaM6U8YIMnIWGdawVd4CXVnO0loyKayRzeePkcYsqgKCRLRZdcGRORZZDXOGcLcBVdmNVZWGiY6qYXTMYK+svUcwAhCiZ16vmBkaBkw5NYSAASxtLDDdzBmlqIaIdW4qCo8+AW3BW5VSFUPtadVRWuOoReXLm6n4Gp0ksUw0myOIsUY4JRD3DWtqSovOKO/UBFBTkVVVZSuVMNKkUKSsh5AmHRo0ir2YgCXQtKbVbnsJJEF56Rnn4AERBuH/XRv7c4MjjVvhZQxWsXRFgBrTP3yZnNrDuef8QeH9nmTr7aILxwJKzMFyF0/fekf8dUsMtSrSV2CEWhqF0l8n0MuphmHFtJZMMDKNYDgzUWWY5E8tqorfSv4vfgShgozi79rr0+BL3pofVn7OSbL0im3v72y/2tzbR0n14/NP+5v/5l63tnf/3nKWVXQB+ImZu7xFoEcMUfreVuEe3Nt0ftRRoeYGu4JxOK3svayPLkmX+BfyvVum/bW0m9n9bJNPm37aTrWQ72dal+bet7efNOruyMlYx+qYvF6s+ferd4tY39sF4GRMQiB1zLrwxIiMr9VgGX06tM1KeW6klGFRKpnyYdbg/oIo7GmwwnZllvSLMqTQuVQHFO5/eCzWfnSsgMvRnDRMlcgvM72pdfJZX+6ItEXev764WYkbQehctdngn8tomEi0wAv3AXgUiwO8FUYqhcXAJlLLy+hp5FtaGn12SGd7PYdA6PBdFMrdG0PXrimh1cmyoSxO0b7xP7ejRfahDxBUyZnkN1TniDV5qW6/jsBK3sXHI1k+VAnqq0SJcwqzj7GA6g4RcK91qLVPn4cN9uEXkMA3uVtcWsYPXKJi23LSWMvysZh6b3vetRDFu9G6lYhFEFlBCOeQMesBIJhny1YJe1bujmdA9V4lDa4PFDNzGdvU8xKf1nTM0IsOpwuvZh9KeL7SzPHVtzq/lLLKxFigsNS7WOijOK2b+TulpFEG0nJobqthd2VfusMB1f77QhZXO5saU2Ro2v56ib8T1OHIDt4vwhRGfYdmVUV2dZN0tcd3fQesHlVWdxGzttio0jW2ESoSROeXR9/Gdn4C8f/ea5Fxc+djqu4vZeRdIWyjwo2D1RPD58jT2ITscRiOQg0iCH4XrqJHIHykt+yCuWhaqGPK9QgrwrgAzDB4a7M3VQbLdXb2/seG6Wl0zkUmVpLLAnmsb/7K5CaaPZbVExfXVpY4u79uu82kuaW+M0TuurwiMAOKq4lJxjHBuU6h2RES0zCvQv6Psp/eaOWM+rAzM6c71gEx6zlS7GV+A/dJq9kvQ2K2LWD0F0wD/k2Uw7D0LGmFMgk4peKTCIjYt2WxtbvaYUwrKXQlLV5d2ISvY9qaB2x1VLDAH6Zg6Akg3/Rl2iBtnHtHMkpOol4FYc4GRcH1hyc2WyVKzP6olT+jDelScu4F9a7VbeC1EbrUehfBQhN87AsAUrjtuyRF4ZehVM4WcfaSpIVJlzncdVN/IPxl7J8OpDuazYJjuYOuaRR2AHqXNBGYwYrBNmKB5fhri1l3+o99CrniQ4sKIcU55lK+AT3kzt3f30ihc2jMnnTifR1V6U0gUjhF2AoJ33KzcKVGpFJprEwtEjjJjywdce/YK7K3r4C7fsJ4Js2iGvobjXM4SDb8n/vcklRkbJ573+q/rpIjYuFgHy2LNFTdFW8xtOqmQq/k2KfXRPDk6X0t8NlnjjSAXObIm3OrvNyLMiJHwVh6vQ9zDuKksMQjm9uVGURNhwd1L5GWTpg1dqkXN3W4L9Inc67hwYUCx6yKiCHRh1G7yW3wX9pz+WXeZHCAL427tobEkeyBqxmF3OCwILQsuGNHB3BRHcsVotnCU5C5rT+i1/Tm6JvEAeuIg0ioQN1w3VK00ZSVmNIdJfX4R1Cmg9vhLATL5yZGbfOW4UrJkGweFNkxltFiJsp3pZKLYNSof/vHzi5U11AXIL7/sF0XNTDjN/VPrm7v7m5sray022o26/cbMB2bO1SeGYEG0UtMy0IosWtHVZB1jsVbgph8hSWFcU3R3kFpR7cR3IXkiTx8RJux+6yhgy/HVDPydMrJI4KIg97BUdktB5nTatk/ravcb+4KhVE7hX5SdxmWVGqptyGpbexAwNhSY8xKZdM0pK3uEr5k2fOZX11S9l1AsBJxbPzSmUHCxnrHSzDuj45XUbNVO0L0GQlOIdXe5YgICb0mZ05Tdqp3copXUJ/6ztJNi4fSTYuGyrK2GAnNs7G6/3MpYNlmf7k4213e2t/bW915ON9d3aLqz93KTPt+bsru1F08PU+6M/C7G/Sf/+Y4Q9wMsTNqKh4bCHR3/EISaazKxclEzWMyFbNtfIXbOBynbsd3K/f7/BJVbXR0wJ3ZFphw44GDx9Vvko8D9ZyqyDanqxZJG1MvIVaIIdsPJAqc88XZv8qb2OvznTydv/suXTNR1vLe9ZHnK9FqCL7vwf2eF6Wn8TSHVmGWIzdZ6/HGMvMLO1PSguGmMxfoMwWT1NXVeYhJq6FrRwg/da1n1Jrh6KzWGbxlF0yswqaAVsCf8gxqj+KTqdDYeoEgR4j3MF1//4UtsFIHs+ZqqhaWN0G2G/MIUhqlBFRT2cU4rDeZLSGCXU3e3NLm1ZQvM1z7y8fTueNr7kF+zEdhyIZE4G9X9fewdBY0AYpcJ+8jSyrARmfMsY2IE4ZD4bynyxchxyBG5Udz0mA5X/3PFP7syIiv49Mp/fWql9afOEE+dIZ46Qzx1hnjqDGG+784QvaH9D5MdQA6CcUAYhLrRS4oLEFGHxNZ4vykspFH42mNJN7VA4GQuihE2kAnVL+/gb6GALQzjNhAlh6oEO864sFONncrH7VlhmoxhFeNIX8Vgf8zjwNrbwapnHx1ZTTMNw3lt0sMdV/Bu4auR9/fYVxw2SHa+ad3y1gWA2kSpW/31g7AzFJShwWHIug/qDLRyd1Eqjk3FebCZ4tdRdAQUuHRmh8gU0FnhxlwWbIPmHvNhpXa4SxzmcxfbS9xHCkRRLMR5x2qbhglgzIrl7JpGlua6dVlvNF2UPlGWTFlFFy+AhvkOrs+8r1X+4bJcCVAzYFMDYFlhks5els6uFJrmD1Zh9Ezxwl4E2O7y5Ig8+/nkaO3Oo7S6tbm51TzwtX44NITt3gE9LQbbB+CL9h76Sg2GvmIXoa/YKqiOxR8uOfPEjl3biL2gitxNhL+9Kal9VrZ3Xzzfe948LQUv2OWA1SzenLw5xjhqf7v47E+AFpTCZrciRbRRjELcyWRhIlNCpaEEgzMW3tzcJJwKmkg120CfNySAbhQs43QdLMHx38nHuSny/zw5OD2oWfx0ylNOc7Qb/9fIXRm+3FmC5YJ6csms/FGC3D9x1QTDmJjeGGK/o6X7TLtlGX8xHCW9sYQUo50LIlMrtgfqor2lRFY3X+xstkjoMyXSHoE0SJIUQolBdWgeswFLA5+2G2jhZR7q/fibso73N3FH6g7KfHHP9kUqb8RgkWpoPrYTrIIFRUHa3/330+O29/pqdX2glRh0EYv0k1FrI2FvsTRoR/ht6KdZJFQ+TPjduG3vn7qOPXUde+o69tR17Gt2HYtCefifDwzk6zF62UGsGAEyW6Qxv42Va+SeUMrHRTxwTVbsx55Cw1svnu/tNAA1VM2YufyL3FIXsBq8pyCYYlGAr/+LlZqDfQMJ9RlSYcYVeKgdJGsd6gvu5BBcMWi/ESu5gCHgPRgCVB0LHJVBfHbeshKg4HO7rSBYChhmjbs4gJ/dxzvCAH5mMq6VmVKlFpjEh04tWgv+YGrCDm2hMFGwpTdjPVwzVxleib1lobw4pmJjwCNL55A3XqcYWMhOzryLVCqnbKh1XVk9JdjGlyqhyc1iKP/Sod28XmH0jRRW72tmAmDsDBOD+btOG34uN1m3nrNUZk4OsLBdC8BKGLW45Fr2lJ1+HJThFOTk/G1/tenDg16QhtpBB07vJh5SQVvWbU/V94AyY/KylLHsFauIUsy4gYqKIiM5NfChe8L/m6zkUqzsk/WXz5MXWzt7zzdHZCWnZmWf7Owmu5u7r7b2yP+sfilVcvW9PYI+ZKglnNKAmpH3d2CQnZySmaKiyqmKXdfQTjOFCCvLbKIr9jAuRhLJFly5VGmItMZKS2SaS6lcyPwInXZxlb8wKIKXk3K+0JglB/mGI2APGCPS6tlYpzFBSCIXhFZGFsD9IvbWvegnUhsp1rO0sS+KzbgUQ56sdzDDXQdr/dfDPpgGOloOnt6T9WvFJiz9oc/O7e+v8MXtN5i9VNF4HZVq7Qlnh2d0HbzTco7EYe3LFxgftqdIo1hU8HiZsGDIDimYSyq5raUPFeT10cGZvUEPMC2z9p7F3USaLGQwIej2os+4KNeXEi2+GyFK60vxtxjnAFDyQ0+pIEefv/jP95QSnmPVHyDPmiLrnBP4neYzqbiZF6GyLFcu9CyKoWR55qLZsBIxhKXOsVUWhpq/OdodgQNjDei8VMxx64QcZJkHYxpCHjEC1w0xWUDCuEqp9kalJnDIjC2AaLvGehaQI6ZZSRU1MnQUproRXf1MC3qF8bMjgnlwc/r8cndr+yFNi7+0q+nLe5m+joPpS/qWwnmSulGb+xf/+c64ZQgSbsctu+xusDRUBsuoaENFlDx1fHgO7yZ/84fg1oz4bpwvTCpFXeQ51ntCEW1QNUGhua8YNKwVnTQtC+2cquyGKjYi11yZiuakoOmcC6ZH5EimV0yFTqLKpW78ezVhSjCIdJUZe1BVZpXOuWGpqe5NfP2UjX/bSrFuzNeRCD7uvbh8sfO1bli8C+U02jtPav6ave2OrQMrUPZMY/HVDrK6qm+7fcOIUpFTZn48eXve7fL1movqY8/YNdDRTGFEuPd9BYGeeI23pxdvz98GzNxjU5sxmXxDijSA860r0wjkN6dQx2B9I0q1BembV6wtkE/K9bepXNu9+RYV7Aiur6lkN6WugSBZ/cWNHd9IjUrBdT+DkCF941P1xx6yMSg29vy6hr5eK4T72IlD9yisj7Mep62iHBDHDR/ogEdfOo3mN3ShSQWvjCBX0FUaCEaHglHBxQwKX7i620xccyUh0KfRVt3tH/SerhSoiZUv+DaeMGqAEY3bWCjvwUJ/E0gQRnlZNz5s9V6i6QDI/cVt5m2zDkWjp3fSZ9R1EikzosqIGt8L/tEXEnGMEorK/VHRHIJ7wpiRLOfb20BlB9djPTT0qDRTiasCAl16M5byDKqtWXEUSKlm7tBVs7X5UidTWvB8qAiMt+cExyfPvJNGsQzStjM24VSMyFQxNtHZiNygONz1t+GTHbir/BFTmr+a/7Oj7uCuN6N0QsyD677WL/LS1OL7jfwnvWZtbEUFpgbY5fYacLYANqjbit64Qi4dyHeSnWRzfWtrex10cp62oX9cAepb2+s4gs6h7LbN/Y82Zry180vtrJ/PnWcr90k9ItWkEqa66wxTdcM7Z3jYkKEO8MvS49ZmsrWTNPvqDlZ2w5VXbl0rVoM/zGWVBWXc2wnqindOqsHgBSihPTbbScEyXhVjKKJzXbRKGzYsAcEm1Gish9XvwMIbu+BrOSSM2CePtKpOlEuGxd4WVXOObQpqSS4UFUAze3Pbnm/vNqe39+PXcrhA2MaQ/hZYHSsoH4qtW9WSwARe3kq6ANhr+JHD4b4af7YLXtUglvlreEroNeU5nfRkthzkE6YMOeZCG9ZiboAb9Ab9dT1+0SK/aedfBOeX9gO2gBiwc4hXPIHvgAcOyu4oDL1q8HJo3ugYlCBUSLEo+J9xN2lAYfj4PhReHMMqeDa2lIIfvPaN+k8qxRT3ql3wQGSuAngYttl0qYGnL9M8OCTEw5xdKB5PnfxqLO18LpUPtYXaEbXpv150Ixtigh0BgunHmEaAxS8XF2fw+XaH20/ebR1i/uxLUfNC1zmbjCuV+2pcmmEpThNh2AKpcg+vYn9UTD8g1MK/MJHZIomzqB5YqDN+tYncONq3BSaBWdvo3dt7eTuILuHnL3CRXjjjBm78nRj5heW5JDdSubYaHcwMsG8XEmsz3LF7zyywwLTmjFrpu6vSbO0879/Mgpm5HOo+XG2gFKdqpWZH5e2wqfOExcVtjQwBG1iV7I+KqYXVg0IX4EymVeHT38LYvvfvyomvXGp1q+PD856w9RkzI1JCh+eyMr1oggLXarDsr3du+LrwWoy5zm76jMpJLmeJz1hKZbHRgl2XUmj2xXkKTrssU4mB/Otylbtwcjtb8bj50nzFQftpjMUBjZVwehxVn19zuolTVy+o11+1s9mMtxjWiANw3WYV2wIjTZ11bpia0rRR2PCk8eXdQaFhgE4Pf4gLTaXKCBczqwljf0T8szkvaYi9kOqjWCmVK3VEhS/Mq9pFkImSFWRX5pJmZEJzKlKm1sKowWjDPoZ08TAW9KGC7kg9vfATaOFm6q4hbszQKSQMU6MAgfNjaSa0VK50e0kFsStaw6IhMRyJw08PKnpCp5aX5WjO6VA12gKJ4CzopKh3rFYvRz0OaL97gZuFst7Y2RdNaxaVXGiesRGRlXF/KJIVf4YWHzXqBS36zJLuxR/u4ZqDx+PW+Do5aiOrQd41ts5P35x1zgkhJ0c93G9z2QUOnYTp94LdThHdPHczvwf+OiVkFvOp1+7jHXGMR50Qw1BE2xcFLFg6p4LrgkSVAkMzlijZCjrL1GGN0Csl7Na9oY2d6dy4oes01BDz5VfD/FG8fNP8hPXYw0RYnd6PCZ7NuGz738aNhfi34laDnTr/rRUKaWARLIvH/1so4jupDFHUGcF9sd+/gdXDKtDww/HhuUPfA4IngVCbRPs4foS3vuOHRWSI8nGb1W3oOe2p04X4cv4GDeE5YSgFclwFnYh8uf1GkT9X+Qt7QFNDZpLV7QVgEHRJxE3HM8m0WF01oY+0FFEvJl/Nv6xMvJ+Bmizdh24DULIkNPOJex2sdXrzI9Uh0Y9vqBLjERkzpex/OPyrvrVo3tMDAIptNrfV0pIaYF8vWp2NcCJ3l0D5N6zAgrd8XS60AjKPS7LEo6Q51T5KALrzeNUwzAC3ky+5TNJKG1n0u52lmiUsp9rwFPv6JRMpjTaKlsmP/q8GsjCVHooGJDlfqhUBdCIMCO5gyI7S6pUSSqhQLrwb3ZEduNBdy3I8Ne3eUNGRaa12Z/vWpQx4HbWp4JEWF5UyNI5yLGM0XZrrL+0Vtjf5J72mvYipRDpgyYsOXtx0roLjXGYdVNyzv/Y09CxkmM6c/rgC44z5t+/USdv9zEH9jZ4IGzthU0ioKXNuMJfBkKpsNAcoqWr0xD3BqCUFlYcwl23shvVGWUReHN+E1f0VhSLWdsRmCX8WA9doJdhYhl/sqLMg39UtjIkt/FyvD+iEgLWQUideU8zsRv83E6mEoBmpiGA3wBes6FbI6/gQSJJC3daqbIP8uY1OiZauj6m91iYMbGtxaNfEx3mAde6z+51CAC04xt8sgkQZ8nPgIlzi6GGJffcVfrjsI+vO2XNXbSiW2uzzxWOxAvJY7NVdcBNzpGtO3TAJOcuZVU81Y+TdT4ea7O5s79itfL71YifpWVoypSnPfQOfx7aIrEYr9C2m/IQd2artKg7rO4jbINWrsjRkl+XOSLuaJhX+ygvdpTbDkPbd7edd4th+fieOBr6ffOcd9tGsT6hVBJZGVmsdQNQv+9biG8o9+la3tvmWxnWfvsWsHpJrskf+ViPnX4OkmjR5T93QzaobyN9D/wDXUgVYsqOeQCgw89arrZ5iMs93+9Da6IP1MNzee2LaTdnuPzF9zb9czy+L45phxKpKnRnbnrjmNIClts3t5Oh8bRRrJVat6ADvTuZM9jYJuxP00LfMKznU9bBPTat1mb0N7mpd1m7itlS/sl6eEDZ8yMyUb4EYmg38wqhLEQGYWW+hgEip/YqbH0HR7bbgdNRgLENDbmxyOo2+uicd3ZuBmzm0aI8uiko4cQzLOMlrFvoa1wm7BIWyqEGPy4HVDWuOe+KTMm796D7SwA3bbhkUOgg/IOe11rKHOi4HqMnM+DUTro9WNKuzw5RKGpnK3Kn6XkFXE24UVTwiHCwG65pVG3tYNMrIBZROc02LRiCQ0lxLmGyBikD9sL5alJFJhqd/jOzNxSZSXo2IubGynPKtzOL6rlbz0NxUTkqvq5Bj190wIpSzAljqIk/2FspCUae6uyUcqY2MaUNOzrC+lR6BI0KPSDTmDVe+qu436BmnvGiQVo8jcpmeqLc6IVfRC4neR5C4wQ8OOzKR9txAZJ/dliafHbvOofDmGISIsUW21Zu5FOF7xciVkDdiRMb+sLqfUFSJ+tnrqui5kV7sNRDgOIhZXA7msVg9wIg4aKaH5mAB2ZJ+ceTkDF16jpqoJjcszx2TC+vxx69OP2zyv9oCR6GnyTqdCamNvfkMFRlVQGO++nMYdpo36+u/ZlS5isvUhMiEGTfzagIxCZZAcj6bm42AvHWerdtLpkfo25+//Vd9uvPLv775effNPzb25ifqP87+SHd+//XPzX9rbEUgjQGsHStHfnB/+3t2bRSdTnmafBDvmF0P7Dmptev9D4J8CMj5QP5GuJjISmQfBCF/I7Iy0SfuykziJ9+JED9VAgj3g/ggfpszEY9Z0LKMWj8C08HLyykzRd0JzrlgR+FCiuwc8ZiBc0GSvSaQgAzdwTi7SRCGWyb2qJGKlEzxghmmEJAG0MvBVAPSgMD+F0QeN1k8cpg0WelayADbDbqZSnVDVcayy8/JJjw583HmdZtYd1yjn5y9rFTyYzfsY+vVdrKVbCVNKy2ngl6iOjUQgzk5OD0gZ547nKLm9uzeKu2en6wjcN0vsF571MP23PERuK98tzn/lnb8h+bQ+xw4GEg8p8z8lMsb4HAa/nLBmWHcXM68Q6By0Zl9a+rW020iWixXzfuTDE5OXE1gkthxSbPMcWPXa80yWX81XedUuIdjA6DPRkejJQwJNev//vrgFKnvj3Uu1v/ALwxFf2fUgo4c5FZWiGKmESDf9ITYiROO1kL4G0tznAD0EVQtz2SlozEBEM1E5ty4lk3ijgar7t7mdrL1B2EipaW2Jx/kLSs/tmI3WsrP74xdjchvXDE9p+oqWQsovy+swC4gcasb6DgB0rvBBY1Ak87RXzpuIFrBgPrvW6fM4WJuCyO4dTkPDPYYOq8B1ZLJgkhIqpMKaMzJvbquBuGPXXs5P0O46m98yhtglzS9Yve2jbzd3gSirhvkk4Rd926PuFv/0iPw+h9rzciJvv0i73YzYs7z6wGkrNXXLz2jrKVV5DzsYwKy5IjkwMv/SVOrw4XgjKBbfns6U0hCCHGmHuohUHjuzqrf7Eh8QH0ZEr6or2dnl/jvOE98DIkXc2sM53RhxYIqK0fEpOWI8PL6xTpPi3JEmEmTtW8P8yZtIX6gNFgXnvj2/ATasuQovt7E6aqerF9bLCYWdzuIwcg+UWqWjkjJC0Dot4dOC3QDn9/zPfpXuEGDm9+NAk87++jb+Lu76gtGMY+d5uglg95KjpeMQvF2LOzRMStip8YQSJcxw1Iz8uNjVA4G19074npTxncKpr3nsKG4btZeD6nhIdzHlxXEQSn0y1fQ8B2W2mryLsWUzypV77skqhLLI4BoOTV2usSXsmmXOfT2ej0iN2wCGiBn0JjfqAoS+xFdXIqNUsF6YVxfcsXLw7Xa/IM/wVZAdsPGIEUzgn87lxo0gM7QFqsHZ28canTyQ812An1GFm2KnT5vMWi7e8PHHPMpoWLhmRxgHdepA11oH2qJtKFr4f8OfMMqvA4WusyTNy725I+KVTgwOb54DVUypQAS8savUsmUaR1ZL8IwoZ6rYuD+SCUErFnJzOMDogOPD88fYIVncWj5o+uX/rgnLqx/LlGfqyPYwSQehWmjmg/tLmkRmcktY0Sa+FOKZuqtkQSj7/h04fMHvP2LkHOMxqeqaFic6qvG2cTbul0rLt/7TDA83+rzt4TnYywMNWwmFf+TBUiWvQFwAUlASfIUpv9gza2Dw7983H5nxd9nIH9nQd+zLBcv4TsX6TqLskx4KNuIY8PA5+U0+CKCse6O1REjw4GKeTCkNNSeKaoYBNa5y8KP7Oqh+65aI3LsXB31NXT05vcR+eXdiLxmM/uEVTHbGD2rJjlPL3EYtnTPt6fCvk+FfR8OUu+GPhX2fSrs+1TY969X2Ldd17d5qde+mC+j0/m07eGVOj/T96vVudGe1DryOdnXHST+5fW67pK/d8XOr+h71uwaa/jLqHZ+VV9Qt+MilUUciPFpul2dj05x1KZel3h21dHrQJ8Lo96j1x29+X1pVH5ayFYdklVXuem/44epBf/m4PB2ABrzDymlH9aZ0V0khM2qo0LhQbDhu3DnON47vNmI7p6zvJxWeVyjt77upnUkUHBWBAcCxWxJlteFbDCFU6oZFfxPlKkbcRFCxsnekPnIWMYypwBgKifClbOpIawozaIn5vQS4vPOf25sxFO1effDt1aB/Kna/FO1+adq848M/OdUmy+VzKr0EYv2ddJ13Qy33FwtEPX25mYDPs0Up/mwMdVed3eTOc28KVoMVpV/7srqt8usgXWeGkogYgLEwamSRTNmTrkGP1En1RCrXY+0KJlO+krS+Gh6Na7FvbG/3aE+TabhPyX8B25a+EPmOYMqNmg/sH/VQQk9OYIN7bku5xclaD0mUv8OAy9HcOeLggrTMlb1nt/H6TnpNyViiHUBkFpWgnd9dFD7+3tSKONxfCQIE4qncyQoCAFpVMwOeY2pLEoqvNRkxUCwpzaIsZXkGOdU6lDP0IqSkG1KlaJiBvE8U54b5qy9UH3ZC4lQ7gJCfgU86AXNAEa9nodUwPoKleKb4i4ZTDX4eld9TFteXKtvvgbZhmvqHK6pe0j3AoIyPf34kgP9ZCpbN+Dy1R2/S63gSSVo4eh2leA71gf+KhzikZWB71gT+ObVgDg5xtf4ctz7LPrqTqZd3/m382y447WhORauwuhbP6uH78TUpbt8x/Seofxro+DNQgKLGIfmf8ajQtGBMLQDBMd0gbD1WIb7/hVpdIkvVbjh1mblj7bjbk8e3Kd8UvE8uxyWGlcPXEpk767ZUw9Q1Ns0dfmQjiwCnwlUEb6JCriGlNFUFgU35PyXA4xSEBiFziCD2g/RUxBgujN9yfZeZdmLrcnmq729ydY2Y5ubm5NXe69evNh78fLl1mZaO3jvMWinc5Ze6Woo3nTohu8gy68Q5M5rpkKVum7W7N7k+farjL7ae/WcPd/ZfPUqfZnt0Ww3nbxKX+00de1o8oFWdNSMLoH06iYXCJC/LZkIdXiUnClagBKcUzGr7NqNdCSlwRW7oVjO6SRnG2w65SmvQ85JHfDf1A8QnZc6lW3d/hGdhxlsjZiRubyJFwx16sKOuiC7SjO1DiEtIzLL5YTmHbzg130LYcvoOxk1/S0PLOODLOBe+JqYy3nKhB7M1fEah3cFkzFXvI05f9ibzaMIJTr0IXI4hZglN2KssilZkPOzo/8gfrrXXBusH1MzI6k1n+SszrDXZfYRsuvdkHpjrctnDkqazlkYeDvZHFDS670ioilqypFNwYqaoTqEnVEzjyrx+H3jHYKKoNuotNoA0t84ZHlO1cZMbmwlW9vJq3ZnFCi5lQ6Fwl9kYUFGm0WYjLx/9zq4u7wEA50SuK5FEl6XKL296mAosyItL7PEtOx9YwWbJVb9oIqEnmIazUS698j29vP72pQ+YkE3ZxDtygLgrnThSV7ejEkM6hXbmUe+qrqZ0+YjBRW0rvBMXM6yzwTbJ6osRiQrr2YjMlHsZkSE/WLGihERFXz9T6q6Z16VxbLbOKwk5je0OUvcyWQ7eRUL/025/5j8Au1iPkXy/w2VI3ImlbGkT44/srTCP5+dHa+F+q3Li9VNi+QgsT1WZHXTNGzGlpZGvtpfRqiBp3jO1q2W0NVeodyZnBpyKFUpVTPZ8h6SGF70CkvNujLYA1d6RuMw6HtWZsceWPcIS2spFw9c1ovkefLqxeZmsvVyZ2t32fX5CtOXsNCh49DsKj+HRs/PDk5OL5Lj/zhedn3DOgjDovq8hA9c3Eo4gR8+Hhx7ZgR/t23RK3evPlp76qNdPX+MvrrbD7OUYcRP0e9FSamoPSl1h1WX+dps/wT1Jv1whGcbESm6Wl+N6udgcB/76UvotDo1VucydKF9EyicinCjWT4lVITdtasqOeaO2wdRLfFlwMB6i+DWwfTLWVFmQ4X/rh4oRReuihUgiaoZVFnQI7toBfQBeLQLohMt88owrDQaRdlB6dVwr0WyyRu6IBPm3FyImVJJw6ACq9Acuh1He9aRIdzHdZSFJ1xs6NDEd52s5+FPqyaGD1ubif3f1osOIi8h2+ZhAmNLE2NiZuZBVXfEYscGx96iv4q9C9uqsJlvXOHClZmzKLCfJlV6xQyhguYLzTWRwmrJYcjC3shhk8iN1ScCN4AWrlTFZ4i8gUKG4YUCNySq8c+dOo53hK50yVMuK123jO3IdTvLMspUZuxS85mgYJdjH7m+t97QRMqcUdGH+x/xJ4ywL+2QkJ9PwgxxjbA20KtGVWz1EyHHlnyDncL77IQpUwYNWr47YE98Y0RbvkVUqhalkTNFyzlPsXOOro9zPOo1zXkWZy1B66hKGz8fec3oNSOVqOsmuBYD/tX6FZ+nV48fhr2hmlQCjISh+XRcOPndu7fvLt+fXrx7f35xfHT57u3bi0/dsgrTVAbKsDnH4RuXM3jnoPKvelRJuLUyQPJSlq07ztLquZGKaVckqd7ons0j6ZzyOFT173bHUXaoX7/tPc9yrJwC5S9Yhpk8jQ5Wrg81arGQY9Mo0TFZQElXjdG7wJlYvkBjM9ofkEo7BPVZpx4o+zPR3M+zIHiEzzi2LI24F1qurWQ3o1xo07hiJ1xQtSCuqWyzZm33bNLGXtxz8B6Kp6KgIrtcsoHU1/HPNvfhpyrPPdzYsgpICe5L15jI3Zlt97uXesJcTvppST1I1DTP69u23fyscw1/ulzUkIfIOhRFVi25Z5kkfYhlGrD28+1xQW0pH6XvZgoZMhW83lyHwTrdA4OmwBuCleF0HM1XX2RTcgMh/40K6WCIhZxcDwgGIMDhef/+5Ghk1aJCCq/dkJ/fnxzpUXw/0qiudWGPn11qvgglprE0cKjcA0657qoPpdBGVanB/rGoNOQLN1yMOchhsCQsBSmVZYIpuHwKbvgsvmTPTo6IYpVmjVLade1rXxprCt1WcHnQN8DqkCNC7VWl2yFnxGdPWuxJbXqYbbqd7uzuZq+mr149f7m7tMuwPkPfLC9ZPtbjoKUjxbTe0JHuOM8t7HDzCU2nuzGQdiAUUZq6S51MjqXTmVVEoipVvSUpo25JEytuu0stBN/Wk/nzjl0nsP5tbESw/wAX7nEabble3EsQkT2KSZHtDsTI3hzt4hTdSfWcbg006/kvB1t3TLu9+2K4ibd3X9wx9e7W9nBT725t90z9FwkGW/UXCobxNSQEy381SV1AA3r4nYahiOYFz/vcLG2OUVJlj+3XsRsNYvx5uM1nGStujaYnq9CXtAo5xH+/xqH+BTzZiL59G9EtO/fXMRX1L/DJYjSUxagf30+Go/vQ9WQ/+kvYj9x+PpmRnsxIX92M5Gnx27cmDWMwegiKnkxKy2Pri1qWHgjWl7M9PRywL2idejhwX9B+tTxw37SF6wsZsZbHVjlbSt54UOT3SX1NOo4GsVmRpYvpBoOeMDu+vRYfutllG/plGs/eEbMeoty6ObbbO9sPBa4D3WNE1UNXcIe5VVL2g7r1QFCB0S8B661ZPlYf5QVrbKsT67t2ou3NrRfrm7vr288vNvf2N3f3n+8ke7vPf3+oBmTmitFsubKGD8LyBQxMTo4egwwclANG8Dpwe1Pacfb1pYsteqC5+V5kv8BGAeaWVGRpEb4foWKAfDXUlqM6UCumaxxSgXm9E1Y34d8PQ0YV7AglEyVvNJT3MaAxcOOA8BIoNPmhM0bSStmBcug+KCITwLL7UZUW8s8QNc9ZKkXW5Luh9VFVdpO5n28vHaruYLyR6oqL2SV2LJTqEZMrhqQfSyYOdBJAbzshOorDXBZsg+Y8XbrgZ8mS/yVJJyVL/rp5JyVL/uqpJyVL/vLZJyz535iAEiHgWxT8A3BfXqwPU39toT3k5H5DInm4ar+iwN2C4VsQpwNI37Sw/AlRNd+fJO3x8/XkZA/B9yMFL08YjyAi11UWZlwbhxWX+/gu/u725MefMHnRNYW1lOHzwv0AvoAfNEsnS6YGQt44VCcYiJ+svnXCFNZAIDeKG8NcauWEavZihzCRygyKaoXN+UmqsEDVXWBdW+qcmb/TvGLHH8H7+Y7Nfq2YWrjvRk2PP6RP6hJpXNbOO2hBhQ69cV5e2u/GSQh5kb41wqQyXm6px5wwY5giiqXymik64Tk3C4CldkfUznF78t8d/3z548npwbt/4MqZa2vd48j6/dcfq4PDzYO///rjxcHBwQF8xn/+bVlhB7YYb5/7gqM+rYY+xgRgnRu7vVA9DeZzVXLrbT0LiKCaWB4JUYB9b8K+uD3yBJAAWWjoxxOGdM8HIoEpyTOL5PPfR4Ds4/84Ozg9ujz/fQ3pIXYUBRh4KNxCoGSqq/OGU7I/KiZSbFTgJgQCtqO/ef/64gTmgrH9cNAjOIx4TRXUUSI5hPnhsKKCPnOw1pqi7ZhHv719d4QEffzz5a/2UwP0iPrabYixAWHKC5oTxVy4GnrOnrFkRsYrWyvjHrfW6n+uHO5/UIZ+UCy7NKb8MOHiQ7GgZZmwj2zlv5a22gDBDVTa+dxQkVGVNfcbL1THRXyQim6vEEli2VXM+fUQCziYTBS7xkq/oBV5V6Sdr3ON/PLvr98sC/AVWwwA7y/8mmErcn7tPMxyakfq3nnnb3+6+O3g3fGHWmPzLPz04sMhyi5/R5X+w0lhBZqfeKhnYgkUm9DoDzdcWEAt3S2t0nUKLz3K8iFox44dx+TYrRrZ4eCEAu/u27gPn42QcMx7EPPhiE2qWV1z5/4CORGcQzXWhDn8Hd/tarMUxLWwVPe/D7JS/dWddSJCfLRmxl7hBaPC2OtkSlN7QVPDSMmvJca6KOj5SknJWWqX4uGDmjruA4RPwQMa+/7UEbQuBltbIRliD8WClDlNoQO+vWGOD89d1AK5iEFwQ2sGtSfFzPOCYoSlvOvbSU4hrgumQFnB3Y1cRUJNrV/i4rkgY4fFZBxWcmAZZKqYCTFKFkNxP6CRKw/ng8uhYtxcahM61quRD3iqKcK3vB2RNOdMmBHxj0I3PmzHlPjq+NklLxNyMsV65mXJXOjayZnn20bW0PNyPMJ6HVh3SjikAcao68JzckaM4tec5vliRIQkBQXRLK4+xw1MRhXLRlbcC9Hy0VT7W6+2k81kO9naHT+gysac6qFKvx3kOd4RVM+ZRjKQwiJEecJykhWGDHryh7Y/NRepNKqXENBf48+NGuqicEE0N5VrwYcV5xayWlWWFHSlGMSx1fqWA4zQfCYVN/PC0tMzDLdlik0lvGEJyrJMuPQCAGvLtzUsl0Buf68riz7HoE7OetHXVKP1YE0x/EZCrKSd7XZo7uePVd4oMvbOf76DM9pnfB2c0FQqig8Gi4aLyMNAQbGoe16EvhJ0ZgV+C4CLjvYhi4TmTBlNpCISCsUJiYXKYGG1JuALw9kpovBJN9oNSOderkUVIAIcL2K273mKByoruAZ3gRUAlcxD1Wk9Cq05JTIycnJ0vnFydl7/ENpvjcgNm/ghSwwfx54P4YFK5S5wVo8IExmojyRjhqWYUiGsfGpZsmbk2fHRuzVXTTqEbTKTPqR+T2Xm7Z4ej9cnD4p6xj0WoLlmqVmVSbEIdXIRCAg3hb8sZ5AkVYyaqNBw2CtPWYEygCs16LuTpHVuqFp/HfeCva+KAPbmG8qneFA3/0MaQPHGDYVLdDHArqUHcliPhIAVy2Vr8vCxxL3IIAfGsKK06sFJJGO8ZvRqaf1rcPfjBTa5b3seYePdhns89C/yx1ymV0RZtVobkGVK6GRPjk7PMQL4l4uLs3OyQS5en0Ngukxlrpe+K4YKIz/ANZ4cIaPi2kdHW9XbVfeCysfIO5FRRlJTbWHwDLKXcB5EMFubSwc8DVtiOFYE8luqDd/OGwJqMCbXCu00Y3dUfHX1gH0d4CWWP6jbpNF/HdcJxiqfYbPcuXj99vDfL49Ozy/tIbi8eH2+7NqGLuC7+q5RtNdIqy7cnU8Y73XY3d77IPxq0WiHT6FpNkedDbtbiEyq1VVNMplWdV5GczZQKOzJXF2t6UlIU1PRyIq/aeSdoSTn4grWQwoZ9ilHhwuiYOKl6vqac7V0Qdzp2tJ8MWImkht+xUuWcQr1re2njU/aXitrsaH89actytXMjEgpc54uRiiboEyArlx/61pFAU72g25/DOgvWN0NLjYhOfPe5Zlj+Zc/oZy1LJ6q6hvh/WB5kCoEAQQcwZWg6ztBj1qXAWd6qeugyTC718LW5ib+/9IGokGDei6iPkQbRLFrrtuiw4TZVQPtgF7vctW7S0vuWVPU59B3E3ZK0nn9zR1q0oF7zm6y7wBItfNFgKnF/iailv+pFMJtzzSI6qj0EMVmVIHhUDNQUPQoeh73f8LRtYj8dJrLG/AoqazWmX6SilwcnrlRsaOvDmAibCnj13UAChfccJqT83+cQqFuZp7pNfejG9QOWMOCbgmkxSB0tWdyDDJfdPDxQ80FPF6MokJTNzjY0JwmRGhqKswvc91HDFMFWQnjrVj+AbdaNKyHQrQA1wnQl/vZ6YmOeTPfkKa+LLzhDVv8UJfypltTxOtwVpbzxgSoQcMq3IhRFiyoof+sBBIFuGbQLube7husRq2QpjPkFFiw3cZ1OJxtpfoQh9/wS2h6f9DAQ7OMaFZQYXiKjpKPxrWvZh/TORUzNmowda5DB2sjyTW3y/W90LF5oYBkX9qwGnnLngpzTK3q7McUvoc2XiRo2nNOOW14nhOGhibMkHUt10UWmxkBYVMedeigZalkqTg1LF88RL1Gu+dQghO2CIWrz21M3ffcriEwmGLCZ5WsdL5AaoZ3ApcHj6IO2THQkJQKcnI2IpRksrAbAMbQSvCPREtLJwkh/6gxS/MbutBoWm5e2fTGw+Tpfpy4L8aIsqaMJqwUVTtRs8pn2YPRNuHl2IIyThCs8YhkrGRgnybSyQyk7vwPVlmuW8EsVCdL96e9LZ7FJf3iOITm0ICqLq9MKyOFLGSlfctDwHv9dQDQd13DgZ4dnJ+uddJs7b3NaDqvbU2ISgyGZD039O7Wi1ftNTeaXX7T6VzLR9D09rdsoOJnKWc5I69fHzbw0ROYskwwZPxas8ILhKBAaihU7474vSMJZNHdrdprNv9Cwr4Hsk/ybyM0OH7TLD1jMkm5WQxVZOSQm0X/7ryRwijW6o8E4EhhuGBisMInp42CJ26yDnynUpk5OYBgCtoDZCWMWlxyLXtSlh8HdTgFOTl/C/nFHQgPD24Fa6jddCD1bughFTTrYsr357sHnBmTl6Cc9837WooZN1WG93VODXzoxtz+N1nJpVjZJ+svnycvtnb2nm+OyEpOzco+2dlNdjd3X23tkf9Z7QA5oBFn9b1mat3fxy0DJw3tC0eEoskBpTA5JTNFRZVTFZc2MnO2IClUdrBiZ6PQgrs3TdNoxF0b55QJdC1AtHwuMVJowlSdFO9F2/qGQvByUs4Xmts/0LA4Iqk/1nEc1qk0Fk/2QZTAsWt0ZWQBF+SMydCssWPdmEhtpFjP0s7eKDbjUgx50t7BDHcdtPVfD2+Da6Cj5mDqPWm/VmzS6oPedmR2YOh3Yq7WHvrQMst1X68pCx32rY7f5OTsesd+cXJ2/aIWPlvyVkHTAXDz5uDwNqhJwzJrks9w8K5eWDXTKV6QchErChPoX3l6cBH0b1fxgTvJrD6zkpSKX1PDyNGb39cimbd5VkCbyyXNyITmVKRwWiMHoVREycoe4haS7TpLuVRqw4NSCGIE2PG/YRSgBvsAqa7Th4uZT5PhWrkunW34zDwbh/bbSBwDFpli2WWf9PiIfd4gmHA2Z9pEk3oc4dwjWEhZsiyAXE280Bm2POoRO4oCcWE4p3FOpSIrUymTGUjwSSqLFcI1WYk+t6sIohfVBRdlDGu7QKUHlnJtNSrXdwd03JxfuTQe9BDqajrlH8OI8Aw0ktzf2MBH8AmrSa0l5ALDe4xE88BHXgRz9GSBXU4XxNCreldRJ86pNsTcSJLTCcs1qt9CGkgFwFpGdu0Xr490iNxdSWVSXa10b8waGQ2SMLK8hO3/AhTBplMGJezsrE5ycXv4jF28PloboUvkSsgb4W1hDbCIQ/3ImxsBRSWtyd6NhykwHeJpzxuGtXisMQTU832TDZDMbRRTb8RytAPfN8im0kwlw1JMrHfVOS8hcily4RA5vY1jUEFeHx2c2avgAFd8FIaKSWW1uzpWUJ4PtDgr5BOYwEsm3fCvZFrl+SNn/n4184td8KomdkkwHagRd/jV8wlThhxzoQ1rNd8H3IA19asRIDrUBqdAXORgzsTbyxE6h6HzJ4LdccMHsvUQKsI5oFIc7wRO1gViwNBXX7gR+A6EmRoZde2LIw8wFhgZlCBUSLEo+J9RcBqiMHx8j6WM+ZSMYRXQrU+5D3Z149BkMJViinvVjnYQUIO7dtcQX9mxj6juzex+FFIKmhbM2YXi8dTgr8bSzkM/coKFqLnoLjriaRR4Wssz7MuXRK5h/9XdTSj92x1Ho4l/w2BJ0FHq+KeMGuqAu6GapDLPWWqijuuNVpWhTeWUiwxpLVB+LmfakXyooennhrQU9LU/wA/GyjkrmKL5gGVYj/0cMevz8W0e/Gd8CjYMLOi+1qlCngHxgC6KLkvtS4UqBkn+Guuwjt2AcLIzybQVx7oS1h7dme5ubk4byBjkqPZUoQ3xD0JghABCjIFMNTVBa9CiVFxH/ExOMdlEyIw5c2FjybWHLmSqA8GAXJqxbnn3kLPaKSEbA+MyYwt6xTThpu7nH3PmWtK2dGoJ0jdYhYMhWIdqmykb9sBY3YKnVU4VwBuGZAU3vmRyO4LsVBrnNuaYWyKY62DAWP2CxnPZAAPiwmUD7XW8ZuSgxshvvKGpIWP7nrsu7O0BHy32QX6iPQWvs+cv2S6bTNkmZS/SnVcvt7MJezXd3Hq5Q7dePH85mext77ycvmhZjgaxXTYELU9s6NePuBNgqxWmJ3pehDKr7mTCPQyJOY5eaJ7LG9z+jGuj+KSKI8fdGC4FQFWQFBFMmFDot3n1o0HCR1toQyFBFyxd9QkRwcgegX+C36ZUwwqOrdLGU5cR0zhFXgpod8ZP80qbTrt7K3v+yKjRfYOg5uguOKifXIYqAuFRu5HjWl7BLK6pPRiA7rj6dJeuWLyOdXfcmkQkMzaoA8VTEw0kAVO2+ExECeZGIi8KpGRH8C97ruilYfsbHNMooDSusAFpteDEx7SjUbQJfumBLdb+j4mvmR0GdddJgMynmPnRlqOlFkuOQOhSVAsA+yzueRRd2CRUR4OJBcFO71O1GidZMi1WV2upa06vmfempqw0uLgwG0IMKPbClQPS5StFDWeipA8JJ5qLWcX1POxafSjhSNv7glRl46p395zUFlQSS9GuzoLDi2DaW6wDS6iHb3GhJtXUDMZTzxpZR64QcOwWVVCBIWma9YgJfr71TfdPqzm0jlI6H9WTi3nCOH5rrU3pfqCcexB5fcTzg+8JeDGiGggLBh23R55tyAnhho4Ec7+SaJJjv0EnUxxEqjAGVawFXfuE3sJ6b7zkNG5w1fE9XLexHb3xtI+zI39vFsbzGxKC8hq6RXdXah5sJMmlvCLUXkmYiccMNkNp6RZRLb7A3bvYeJ5sJzuxngWxew01q/7mDi0Ln7o/ktMHB2JPA3AObTRFwuZIUcjmPcGasfvMRWx+kyGFLjjyKaTwKaTwKaTwGwkpxDPpK0zVjOQrxhUiSE9xhU9xhY8D0lNc4fI4e4orfIor/K7iCuGy+O7iCh3UZMi4Qne13xNPR3MXhFafWhlC7Xpj6qJUNmIUBWVLzL75GMNb0ZF8Jj6+wRjD5YW6Lxho2EPzXz3QMBY1nwINnwINnwINnwINnwINnwIN2wT3FGj4FGj4FGj4FGj4LbO0zw40hJ4pCIxzgF3U39zhAHP9HiwN5lRrPl34yCVs8g5lNmmaSqwsA/WrcC5i6EcpZOFNRv7itzC/4UYxcnBx8X8O/51MFS0YFOXtDT6E+hpSwTqbgLjZQTWiobYqV6GKJ+h+bsyTo/MROf35p99GUPVyzQc0hA7iHlz0lOAaEgNdxZO/ARS+erMbMS5WavUPJ+yFslRufxw2UA9d4UVJU7Oy1pyFpXMg6uRvXv2q1x5qRvv5XA1bLkCXAXGNpnMoBBUqQYINzYDb1dM5TDWCHUpTWZQ51xhlNJM09+BFVUSFPfpWt0Yf68raA/yOYUu/AI92+A1TBu/+tFJQQSgUz0SbrSefhhiL+wy/h80IMZHMqs4Q5we7RX4KU7mxeMOuTLzMHnqLQcAVlM0Ss1CClTAr4GMTCkO4mFn9FRvOS0UUM0rqEiXnPAKWzma4PF91p3Xy35xcvDt2R6upfCEpD3bDW3rmqF4jMhvU6HH3D1c821dbijlBWOQbahT/SC5wnGbx01HctSghz9jHJNS5o8bQ9Cop7JhQ5w4h0RsXB5ubO5sbYYK1NtbwgT58fSFJI8S1LI+7Gl0xN/3yuEOW1oe7oYtBXsDp9PUgK5V/pxh80Ai1vOEvjS9xpANTbOIV97n/VIf1PjpePTB642Jr59Wru861/f0WtP1FtN1GEPR3uk23ix237N3X4SxLY7chWwzEXJbH7oPGCLh2ZfK8tuBqxD6kMxz9/9n79uY2cmPf//MpUNo/1jpFjknqvbf2pmRSWutGsnVMeTeVVEKBMyCJ1cyABjCilE9/C43HYB58jUTb2dpTqVNrcdAAGo1Go9H9a0DN9mEdC4b9hIWZsBf/HIPWAj4iKgWJJ2CTUaikBKCU8TPCj4wC/n47InM5cwCducGmh/AUHHXOrLFOuNSGmq78ukVtupDOZzurxDDUVbxoGoERadBWdZdazKKMuz+bEFyPpRWFdz0cXfQH7y9Gn4bno9+u7t6Pzi+Go27vdNR/1x8N35/3jo7/skbDuJlrBAuPdzviwu3FTdvWoBMSp1EbxywlhVVjEFzvkO7N2MBV7kQf7kA6qjLJNK5nmzyFcSboIyjI++qURuEM0/QeCZqGxuPtlyhC+plA54A5yMiYimqczs3VVRBsXEhk2Uh2xOJzW8DH57XXeSU6vsD9/Gozg2jM5WvRaA3ygGe7Clia949i8tiEciELYmEzYWYuoKymokNhZdrNFmqGxSxIoqMdrU+/oKDSKeFzrk7EHIL5ZnCEIgrXRDZBg4tPbhmLEd6QkLfBzrnUWRWCCknS0LwmadBd8DvqAk8t7yxzj1L5omjPYF5JMZvPCYcsFOBXeYt0Lk+O+yeXvf7R0bvLwcng9OL03enl4bvLd5ed/tlFv8maiBnufrNFGb4/7/7Xr8rZxcHZweDsoHtwenp6OuidnvaOj/u9wVn3qNc9HHQH3X7/4l3vvOHq5CfON1mf3tFx/Qo5Hno5BS9foZyqXqnX2TfHpyeXx8fH552jw4vL7sl55/Sid9nrHvcuzt8d9t/1O4Pe8dFFd3ByenL07uLk8N3lQf+k2+ufn/UG55cbl6Ywc6RCZDszeQZ5jpYtPqns/Wz8Ownd07oegf0XWHK155GBlq6sUpmB/Q8/3zwP9BPYJ8Yk6p+30MfPP1+lE46F5FkIvtU7gpMWGvR/Tp5t4Mig/7ONY9icgb/jg12d4+ZRCFKL8/B83a/JO1VG9YwtdIzmnHAlbErIhsPrt7mhjdAMp5GY4Yfqm2h0SI7G3dPoeHx0FJ50eye907ODXq8bnh2Pce9wW3lKmRzhidxIpJbV0h9gSd7e0YT4xjKU7DV45gWrQKCUQTwTMZs1UlvZ35s19f9/7HV63XZH/e+u0/kJ/hd0Op1/bFxz1pvvGFI/v+KEjW208WS7Zyed15isRnR75eCBUrk6wVCI41ipyxQNP1wZrSpJHBfg8vXbyIwJmZr6ftXKIIZ7VCCsa1yZhytzqwrQb4rHntZWXxYKt5SKH0+JYvucmiQhPybPpAlVmL9YLAKTsReEbFuGa1X5LdVzRSHnitixZa1CTp5thc6Pn38eFOrpvJYeFtlcP96M9JV6V6lw7nZluqm3HQp3ef2XGYljtvTesuQ23zs6Hv3Sv1G3+YPTw5qvL/qDDb7/MQiCzTd7xsuFqHftBFE95mVY4KkSst81j1taF5raiHWBPYKE897RMd+48gwREo9jEPwNZjpmLCY4rZvQO/0TmsS4MC06sc4ulJIpk1RL+wJDXFxIhJhkMcKpl9POcSqgvpXxqaWIpCF/hsp8MktTEm98kU3JkxxZ99pXXUrn09OldfS4SRSgW6IX1hQT9oIkIb/w/MN5XmH9jfVjKuVJcapLWWEh6DRVmkO8lbFow0yUNa/m0NZ0l/4QPM1kEv+A43natmNs00jsl+5XptZ+br7HbAEvy6IqdWqUb9eWBvLjpEWW7FTgqCg5YkHgTL8QPpH7ulLt6VJtS1K6sZgZ1Nnv0mtoxrat17A6pW/lNVw2kl2fazvwGvpr0WgNvmuvoRnuH8ZraFfrv9lr6K/JH8Nr+C1X5bW9hqXV+YN4DTdcIf+y/l/nNTRz3KnXcLiVf7DiF8yPCg8T/xv4B033v+ODnV1F6x2EpsrnazkID84ODw+7eHx8dHJ0SHq9zsm4S7rjw6OT8cHxYTfakh+v4SC8o4m6wCXzir/MOIe+BwehN98XOwi3nfBXdxCaye7WXzXc2DNVUsk1KkDdLO3ODkKW7EQF7La+7YcMcEIKeYr2pJpjLiz+mPo743RKUxyb+22NBAS9jRfbdLJrB8MHAPak/yGRvoTD6ef8C+Cu9Ke5bopyXTV/Fw/FcWiTH21MlPen5XFRgxxk1BKpx6yFMKb/EKuPsb7ScJZNZyyzuwejhIacOYRlHs6oJFoycRyri426Aj9SsshvVnnAv9kE3sCRlzqBOPmSEXVjbedCYqv3LsjY/m6vTxPOUtkmaVTCxmur6XzJCFcHD5TPN/PIMRvGOHzwW24Rj6VGv8Og1+XgyLrjPJ/qXP9FD1fkczMJMjojNy88bO7KY6JOHSTZlCjrDyxDRzLP5NN5XZbh6iCO9eJ5wJOS8Lbx6hCPk5WU2sPx5Kw3OTg6ORkfHEb4GB+E5Kx3FnVIhxyeHByX2etKJX8bJrvuS6y2f7f52Dbp3+HUQE5GQrDIuIFtgAQfB+wsMu8pSFnQjr8QrWjOhQr7Op1J5/gE484Yn3V64xNPK2Q89jXC50/Xa7TB50/XNv7RQouaNwpwcsM+JZKYMvew8T5/uhYtCIM0X1qNpXgw5gSSslHEFqkSCYZEOCMJaTnkgzmWM9OeIevH22Sj7Tbj1RjbNouNx608N7z4PLZXxLkVLCEGaRYDPxP8rIN1jYP86lbN9q1ioeKrTqeNn1sgESyTDlXQUdUZ/Ffm1U/R1in8HiaNRuKcMou8cW+e9gyIYEVoal743DOD9UTvirV3MxNka/M5hXGDKeVkO68xA8xucGzJeFxCUS2RoEJjdAoCOOdUGo9nS61iyqRShfwZ4qdnsN+K7UvEY4IhiXBOOGURSjIhgchY6bowziIS1cAs6DsyfDwmaG+eTvdyP4dqvheov1VXaG5OQC9pbZrk4DCvviq3jEsPLFUxBa48Wpx+uPfkX7L5Xok59z/c60tLEYLCDrqUfTvJ4lc0wL5ZbsPVRGfxKxUIyZA0UVvaJERCYfdMkHzDPnu+EgADze84NEX3Sp4VvXt4OwTfC2x4A3AuECfqdgSmvrokc3t3sAZPEbfUR72pCbcvaoCfDg8P3mp03r9++bmA1vuDZPPC6tkN+QdYwR8/pwmLACk+1zMg+gIJQtICZ6uIX14ZhdShjyYspZIpc15rADaGkztyh8GYKFVjBKel8cix8EUBw2Mr4DRrGqopZBBIkqLfM4ASyi+OoLvUOVrGaHGS47J0XTNHFoOlv8DCDbRVOOdri4E0EiJFbcnPBfmaYyE8qXn1dzlDvnSrCEpjkLuCULjFclbq29OthkF7peHsAKnMR8iqjOPw8KCiOQ4PDwqDUleo510aCdCBEWKHuQjj1b+Yd++6Ofh29F5J2Cpn11/h7IL3vMh3QPi9AAa/Nuic1ZIy1RZ2qJeopn133thtmRquY7Wgv3Em3VctrzM9WW2mOIoaSClFJJnLfDwwdP3lvWldApAvVHxAYyIXhBRDGOSCaVu1dEB/a3Q0pYL/hEb7fqDR9KVtV0IwBOrLdSKcNnulc1dnQd7/VGt36vEuObeK/oQ/Qd/Qn6BvjUDfdhhS/NmQr7FR/BEUnDv232uq8oHjrlwxooCh5KpGwKfavIXMWfKI3f3C+BmKVSRMkq2SDyihA+XpAAjbB8RVf6FEmBPVIkmhhAFaDdYuYhrZa7J1ROEUYYj3MQY3nNbC8w8nW0DA/GHx+r4lVN+fKH21KH1/dIC+/wJsvm8Ny/cnIt9aRL5vDsb3Jw6fNipGeGrdiJ5pgfK/bmBgaBrWzMjr0LKEGEA8NOZs4b0h+uh6z8bRJWZsgZTySuF5174qQ/mykCXKOHR3dfOqnrmh2nvyFjYBcYUov4KWML2Vl4TezmyBpuWCuZMB5ayrDGqIJ5jTwqC+eydwSQ948jEqyEd5rjfsPzSO8dujoIPe6NX4P6h/+9msDPo4RN3eqKsvNzc4VH/4+z46n89j8hsZ/43Kt8edo6AbdI/c8N787f3dzXVLt/mFhA9sH5nidG+7vaCDbtiYxuRt9+iie3hq2P32uHNo8jQc00UwwQmNd+V1+zhEmj56Y+9EnEQzLFsoImOK0xaacELGImqhBU0jthD71eRc+LIy7j/Gk8/HOeHYA0q0tiHcRmx8rgu95VAmZUlZJy06N+x3/EjK3HogPCW7MuMrc9C9uWHr0AO8WLZDDoPDoNPudnvtKUkJp2F59H+QK8CStbbP9N5KL1vcv5c5Y63Tr7Wytj+zn0OSSiZaKBtnqcxW7WHMF7Syh3cbGlgZ/Kby2O0E3bKm3O1QS4VFV5ycSrt79tVjbDSjsax+vT7/sIlNpb4rFufUHn5XeP600wu6X5DE0zdi36/zab0oWGj3FxaIplOIGVGmOdH/CfSxECzU2XS6nHNqnwThvgAXCjVrBzHs1T3VnZlKyA79y3z3Qb+MBmr2dbPgJGQ8UuRoOo3NbCWeAtQsPKFmEIgAyYN28bxy0l/aNG1/QSQN8VxkepSiZa47dSNDhddOV4rLkPaBcbF71hUkFYwbJOJ/EPLQQr9RTsQM84d9eLMEKFyDx2srK3M8mdCwwgmapoQvXVVNAumPzOTyBRbojXWlGarmt+L895dMcvX0CqDU285yxfQKmAQQlGPfqdRNNIqokSw7noKsQBmkSIdLG3ZIPJ2CLjAkP45tlocn3FZ6A1/KTS5vjfzZzw1JJ9v+dRbi192uMKGU9hIcURFyApfu8g4zNGEEHr1l6+KVbzK1m1r6RudXedriarMz5wxM6GqgLUUDRG3i2B33q/r6L2sO4q9w8/k414CNegZwZd5mDiyTgkZk9USc1s/ilHA8prEtUWjVf+WH5eeAOgYKhDZw4uOarlHFo28T9x/dAbYR7qQBkt/R+hTKqRuDQOlzP6IcJiIrfMHwuuOwxy1gvwm9sSZR2+3vNxPfBzqA64vqa/h5eLGv/gPMXBzDh45o3gBLPIaTiKNLs2/3C29vOTbAlwzHz2KaYR4F+r+DkCVvvyzIeEbi+dsJG0EEWfz2IWWLmERToki/LUxwZHFZiQhmMvnn/wIhN7AiM/Jv/7VfGx1kQxPt80r19evHf+7Zee39awv4nRrw+V0A4RY7ckklBS6IkPHcsiwsTn5J94OaIBkJEBzCRyHeVkBr+78Oh5tywhvxd3srqnC1VH+1ylLYfObMEu4IxzGchn5vda2XbI/wkXj4v6DD3k7wFxDz+IfwkYzgNXHkDU6MQk6wJNE/+1Aow3Xr61ZK9Fl88TRnQmmO/q8X/gz/VVnfqxQlOPw4RDoNDvWCbi84bvlhPEV2mEDBT7f9LbLwSZolcOnZ6QaxWtR7QfFga6hYsTTVzVG3RDW742JTFuwYHV7P2KiGN1eDfRs4YSrKz/Oo5/rDEukH7ABd+W/OpgZ9uQND1L5PVflaPj02Ff3FDMsRFSO1BWi0b2S9LOOOekXWrwb/qlmjdq/TPWt3Op3OFnAwu0U2P0ec2BqiyxRMwX422kZnkCRU0qm+/jhe2MVw0h+V1qXMmPoVCae0Paap+iu488Ip/av6j58dH4+73S3YqARvtFPhN7dIxpEIcVovqpXJq5l0O93TYBuhUPRTwoNHkkZsVxn2d8Vy3ZUDHoaA9BCquOMkxeN4jbnuT4hxEijLa4PJTGKGa4ux/zhUZHQ4DMfp1Dx9dYKOsri7naCjnYnwnxZ7akZQwoREgjwS7seav1MmpjAUmbp9KotNCCJEAm9toLXnMaPSMiUhktNQoDcaWh89wlN+nn6iw7yfoFD5nNNHGpMpMclc5pVYEq6z2vZbppJKTtV/81U0HF3VbMqBLJTh0lETMKZ9k+oVsjlZYgTUmF/WVAfRbUcGi2+/YqkeBUfbLTFJHylngM+10VPWV1rrC39Y6xYdp8/IJTGAlJgVaqEmKwQPspQTwCz7DpZIkmTO+Pe0OndmROsWBt5+EiwzzWjF0shA6sEsWoXz2q5V+Hr7YkMO79ZXDhf5D9h6Wwpa212d33z4dbCfH/bqakwllvTRR0Z5JBzkE6cPNJ2Ci3rvmi32WmjvhkQ0S/a0NO+9p9PZHiyBuqahx55aVKc+HUWQBFF2QGoIBteXhK5yWgdBx0TmPoMPMSITmhYTuRSF/OPCGnlSBF9QgdgiBdzYCCU4xVPte7q8+jS8Cz7yaQtdpWGA3sAflPJEn4dtDZKSMkAFnFDvqsWnOHXlWhYzppQBFTYZUjI0I/Ec9D541AUJQTiVZQt6Qllfc5b6JWIITgTCIWdCG84LxuNoiYimj1GQUiGDKXsEn0XbqCIQ16oy0I8jm4mqWZIdWhdu1WstDAhqVdwDRWEPQVv+heehEEidpYxTaRYCcTLFuv6kpwKacbBixKtuQtd1LRfbiiE/obEup4nTcMa4/mc7tFdm4498p78pcOb/Au2+zXkx5SjHUNTQPF3YqEjYSnFssuXUYoATrs57qF/LLBLyiuWrGYv6vwGZcxJCHZ02JFlqgjb0Sf+LFt/IlIb2H+kQem+RmM2Km58LIx1DCUyakP/YuBw7UBxTl7Y3x3L2k3Ghlj5O6FRf8X9CkmekSF3zpkCW+XA0+h+jLTjjVgosODhVphmH5dGd1c2vsgjVuam18r9bOS0gWru6VcK1orCSumKwAPiOgKZC4vw6upZPAFiu2yLbFtHIbpIwZlmU74e++qc9lrja9DjCEtdvkRvzq7YtwkJTuL/mzwo4ikbwwciSVF+GRAh9d7E7pjBraBDMOVMSkYfb5gnj+pf202r58EO+TBO1b3+B5A89Y71BajqnCZ6Smq5xQtt4HEbd3kGtds17v1IU0NXAXcs1n+xSGNn8AZ0rMYGPWBz5u8QOSDEucCwBJq+Rs9qPV8qZ14cdYH5lX92Nm5D7fuueNtg6pb423T9ebwkOZzQloGA26sw0CLwGm/bl3zJGG2jT1a027dXI+KYLV9lfm/bDyTQ3olf3Ufi0lr7VRxELH0BWjUIa2H/XbC/9GxISw5N0HGvcHdBG+je1r8WMcTnSx0JuZ1mrQPfXdspoyenthoVqHguLTQpKRB9NfuX1emZ5DKtvUsu0JV0pjbN9b6DpvA21Za+llpt12rw7k/qJfkB3Hwcff0Lv2UKZPgkG0GNB/loZS8HKQKstDbRcnyOn0/UQAiu56jzP5fa9/lcNkat0wnxpNceCao6srvEEVP29VjzNuXHRH/oRNdTGkAQkFMFzYtDofzBPwtjUR1dXqbxlKXWDOciZ5ZK+fGkK+RX1UOnr2DvJOQIPT/myV/tlIhhnNK52WV1Rd3rvdU8H3c7Z3mbD+ThE0IPvhq8fSMgiUrsPVo1FSE5kONt8MLYXnaCVPjsJfMjGhKdEwruIkcO/+X+roZv/7oy9ouWWE0W+FK7WqnmjtZq1MOjVMlfm+JxF9Wpnq83scWDOdIGV6uKqrrIaHd60p1sWoc9Xg2pH6v+LOQ5fb1I5xWpnLKqo/Bd2ZqO/q50Zdfk/L1bM3s+jBM/nNJ2ab/f+Z8Nd5I3YHCQJnleHDFlc+nXtuxu3N7b6wXMChVgEka+7xDndJQsdkXnMnhPrnXi1jnO6SzpWhiCZZPGrT9kjvKTrNXZQ044d2bXd1ht9L+9X0zUHjNHl+ely6/5QQ9f8mJ8r7lJbdw7ktNFWhwB52tTsND0E5ImEmfReR1GN6Wlm/DuL2QPFbZxJFlEBDx/59P+f/hUNzC/PyP8OeTfvtd6TGlL+KWzG4Ugu8zKa7wLtYiq+c2zhUrPh/ia8g03cADx/Yn2fdJVrekl3FzicmRxGDUvogk1MATmDv0EoYMS5uGFTvktIzGU2L/g0kQbASXSci3MKSgO7jBMi1cS4efuCdSMSTHIN0wB/UP9smWAKGBp4zHEMACRCO9GvblvWtQTiTqMWZCXDY1hhSOA6lwI4U89CE3s75yzKQrk9IyE60O1dQ0aZiW5uq7ptLC6Fbn8ULo/ljdfz/pquvUCKLXvWbS2r8+l7siAQz9JUF8KqH4cFjt2698+frg10v7qqQHdGWmEkq5geZnzzilJ5r785qEQ7vwUWTsTNlRJnckZS6WJENaydVWsLmsZsmiuyvd/gD2OC5V69tjJAJy77+DedSIUu4OXjmk2XenFjNg0mNCaBhzBXx2PzCr8qP63Ief8dA3BMc9xCXU8LG18QpDDkM0ReqSluIE4tJq3J1U5RQB7lE5AtvR7oF49SMrDS2AtOJTGnx9qxc7xAf7+5ztEJC6G8ehJsDIJsJmEYXiqV5fAOIcMkf5dStErZZcLCFdu3fkMx18J2KdH57RV6cwO45mwi3Vr/SoW6HaYRSsmC8P2ghLnoQx5YgFh1gJlHOv3ka4KvdXgS/CyIhDHdmzajpyTWfMwLSmFR0eJsnkM8pJAu80ijDNvHOCV2RdS7dfyGkleA/wA+Gc6ycUzEjDHp4y3NMz5ngggNRwcHuEVf11vEz6jwhM4phWSOfRxU8wDvE1IDBTA7PE2ZRtMfxyQpv2O5TYxqDaYl0ncexy5FyeYDmDFU9rT/3o9mEBPiiV2dFwjPqWdU5Vva2xUrxmZXyq0gSCJE8+aZSzgyAFEh45FeBYfan6c6FWjuLWgKNGM23XMet+p0VW+Moz377ZSm+fcFiq6N+kS182TNzqLyDailiAg6TY2WtkMY6uyqXqdzUCDjfdLrdDrVPQ0g2YX92fIk2kyhQJIWq2doTWkHFaCPJXKQhIXBVrJ9F8iZcbRWcFTvqyjwd4MyrxIsC1mIVjSUvSCM4Qfrz+C9W/HLqms3PVHjiMOhpI9UPo828tfUHztrhPQcTWM2hiS2rALWU670I3XtVA4moBkbyG2BpMmsg8Zq2wE8HBSIgPALMC+8TuATH/+veO1EeU91t0YdDTPa8K7adBv7B7SLwIEJ6vwiqMDhbWWo86yLfpXPJschJQYLHVxianJBL0q7avBPfVreF9/Y72u4AORG5nW7yIJtPTuNZMgpujwvSw2mfKja6osor76oBKJACvCJYCr3cHl5ZhkISIjnUl+RNO/gkGGptc+FjtspkJKsTp24GxMEj93nrLuHfhRDI3QPX3XvW8WxwV97917FgxYakxCrPZ0req8HSFRNNU2aFkUA85hC0LOZAJs4w2irFfYPyrXrZIq2wKMJeZrhTABSGKA5sok/dLd7TaJvgZDlq97pAXr3jGb40VhjguTZjfq8NcaAJMk81jCQpKgpfexbmqIIi9mYYR4JE1sHT0ntmGAON5/f2djLEKw6VHzOnRduD7c4fMBT8qGsYpZrjJzSO5pi/tygmYzhyfWzIPwqnWfyjjbpnTF5w6JmDe/yB/8tGmY0jn4t1YzctHFfGYfp9tzqM84zEN5zCF0fSr/Y68ZUOIHl/izDRsweVLL+tmkrcxTHbZo90rCZYOZNr0k6LSnUzQk05JRqaqTkBv9e0sHbk6BpExKcPjbknWrJX9i0KdvVL7/i7WXlIpX8uc+yVG7f9ElyfJVO2NYtLzGNM96My17bhry6pDFpqosuaYpjpUgysXXbX0qn62atrqKYXCXzGBIxcCM1okiA8mu2zlfJnHDBUuj8mjyS7aXsyuaZNGw+P9c4sw1a3uYlEzZv9jfy3FC4rrGQ6pj8hbGoUePhLJMRW6TNCCQvsUqu2ZSlvxQf9LdoedWwnXkRaz7kRiYJHDBN1cANfqJJltzmNRBvCQ9Jg811A1i8jeaumw4bLNcNTV9r+JrS3YwzKeOXkGm+GB/IwohQAwnMGzdagw9koasWNNo2H8iikaX0gSXqHLoEzNQ03N40/gAvmFs3+xhHL5jtxzhqNNuP5lVBnduNFukWN1Dk3uZ44fF7y8kjZZl46U3C0mnY2OT1XlOx/f5svr9esrlM22bLZ9o2kNPbwjvzFu0y3MzAus3wLYtp+NyAv/87aWzLfiJYNGgGGgA37XQIl+F3MQsf7nx0mI3ba/DnRvJk2jYeurHMtDehkdFhScBTQL+JC8ZSaLp4dJpimfEGHduWDW8/Q3hK2b6ZxLyZl6uZu6fp7CSbNxulrguvy8w2k2pNoakJbpp/Fg2tUK99E1P0bthgzHeYT8lLeKYJNPJd6KbNr0pe+8Yz1w+jL5h549XOmzdabKgTheMhgVz0JvNnDyS9iMkjbqyD7zhORUKlJJE5ELbf7k0Z0PTo+Y3xB9HQ/NTvT82a9Zo1O2jW7LBZs6NmzY6bNTtp1ux0ZbO/lNvoR7otn+qbhJPkj+c6aERXdqPVMBiDxuXHBtU8Lprhia3GveXzsO2jgN1r0B+fPQTTyuDCGU7Tgvtxp2/7ujcbbEdDE65gUIZtMF7gV9SjRVXEUkdNUfYCDc1jc8ym4t6mzQG6R2rDPfOYsBpO6DHsVr7MPK8GxXiymE0LUUoAZeOYwpVeLuo4D5UWGucsNHHYXf2IziD8U/+kZUP/XjI6BfhMJMVx/GwrspYIcoLDmQlRSbSnz6zPm96/D3r/LtCzcVPVuCY1qN6/jw//vTq2ar8YGQCLTZ5kaUxQwXBMUKd2NWMsSTT6HkN87JjUMsImKJALWSo5i2EzSHUuTwjnsKEDI0MwMxsGtICq6XJGTF1BOSttGD8UCPqnHN17bLn39V1NsuI8LN7JXl136R7K0QxaYaE7LB60KOuvIC3V4YIbTVc3X50NETMT1myo4jmkJOl4UBPxy0vxwAVqMZtOSVTDF5tfMZpulu33dUTL4XHAj3rwla207CywaS/zmgl55bNftNimjxF1NYv7MVWMN4GNn7JU0oRYX9Uqxqe7jUmrOdw9RY3elMWJcVeFSzG/Roo8tbpfMzGJxcMu95mi/33vMp0eYsDTdbcmq6l6krxhKZpzUoxBK1oK5RBYALg1x6k24Wz43IrNIGdgkWy/JSoUXehdhdLXjSJEL48i9ML6alhnCiJaRfUimc7rrLW77aN2r9s+ODrsHh50znqn7V7nqHvS7fa6nXb34Kx7cHp4cHzW7ubwrBuwxMpPjveWa9g3w6vBvsvMCkOWpdKVgjHxtiXtSkVFvSINVFCM8E8ZAMqx+FHvi+HVAKw6YGFLn+dg1OYFJUvRkvCDrjxqQib1nxSP722IoDWRmL7c58ayV9HAG+Mzy5BLSvQGnI9Wbafh1UC0ECePlCzM/p+iSSmuKNTx9UIbOaYYgclPMPUGlonOhop9xcJWa/cVF61+oQqDKBUB34E+NlVjXaWGJQLmjXV5qdrC0GXRBfT6B4kBTV8/4JoRPlbcPS8xMO68knJ5CqoT8B9NkCw1dz4DnkWEu354EFom482mdOQ5b9dkisNCio5NG16W/qY/IAJpOEpWSCo7Dp4MgDejusKIlxNkgeeB1mNeSytvH3gALTrz11CgaUSe8ijgxcxcGu9dxnJwPJJsdBLolCqTOQWODSKXXJPrk4flekgsnXMU5Gk+K8Giqte0dXQrDVbSr0tkWNNDXZOVfZT8U2vIl75eSbnkQVpDufT1Ssoxm27DkoKzaA36lxB4SkaEc7YOfQ6+CUyLTYgbV03qR2ysGXrZu7OG/jLnwdpeljVc2V/hjr2mi8K3K6nW3VDXEK9rsq4Pc53buIPSFXMleX0H20JC6y6Hq/Ey80vXGtLel6spwoVha46U7xkr+6i3sJf1ZLuqb7W+o4IptGY61Qbr6W9+mpQ/X0m7DqRgKeXixyvpPiXxOoVWl/ldpvn/AwAA//9f1g07" } diff --git a/winlogbeat/scripts/mage/config.go b/winlogbeat/scripts/mage/config.go index 1dae96510dd..15240127193 100644 --- a/winlogbeat/scripts/mage/config.go +++ b/winlogbeat/scripts/mage/config.go @@ -28,33 +28,11 @@ func config() error { } func configFileParams() devtools.ConfigFileParams { - beatDir := devtools.OSSBeatDir - switch SelectLogic { - case devtools.OSSProject: - beatDir = devtools.OSSBeatDir - case devtools.XPackProject: - beatDir = devtools.XPackBeatDir - default: - panic(devtools.ErrUnknownProjectType) - } - - return devtools.ConfigFileParams{ - ShortParts: []string{ - devtools.OSSBeatDir("_meta/common.yml.tmpl"), - beatDir("_meta/beat.yml.tmpl"), - devtools.LibbeatDir("_meta/config.yml.tmpl"), - }, - ReferenceParts: []string{ - devtools.OSSBeatDir("_meta/common.yml.tmpl"), - beatDir("_meta/beat.yml.tmpl"), - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - }, - DockerParts: []string{ - devtools.OSSBeatDir("_meta/beat.docker.yml"), - devtools.LibbeatDir("_meta/config.docker.yml"), - }, - ExtraVars: map[string]interface{}{ - "GOOS": "windows", - }, + conf := devtools.DefaultConfigFileParams() + conf.ExtraVars = map[string]interface{}{"GOOS": "windows"} + conf.Templates = append(conf.Templates, devtools.OSSBeatDir("_meta/config/*.tmpl")) + if devtools.XPackProject == SelectLogic { + conf.Templates = append(conf.Templates, devtools.XPackBeatDir("_meta/config/*.tmpl")) } + return conf } diff --git a/winlogbeat/winlogbeat.reference.yml b/winlogbeat/winlogbeat.reference.yml index d0e11d083e8..aee40b39bf9 100644 --- a/winlogbeat/winlogbeat.reference.yml +++ b/winlogbeat/winlogbeat.reference.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/winlogbeat/index.html -#======================= Winlogbeat specific options =========================== +# ======================== Winlogbeat specific options ========================= # The registry file is where Winlogbeat persists its state so that the beat can # resume after shutdown or an outage. The default is .winlogbeat.yml in the @@ -22,26 +22,20 @@ # forwarded, ignore_older, level, event_id, provider, and include_xml. Please # visit the documentation for the complete details of each option. # https://go.es.io/WinlogbeatConfig + winlogbeat.event_logs: - name: Application ignore_older: 72h - # Set to true to publish fields with null values in events. - #keep_null: false - - name: System - # Set to true to publish fields with null values in events. - #keep_null: false - - name: Security - # Set to true to publish fields with null values in events. - #keep_null: false + - name: ForwardedEvents + tags: [forwarded] - -#================================ General ====================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -149,7 +143,7 @@ winlogbeat.event_logs: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -166,153 +160,153 @@ winlogbeat.event_logs: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Winlogbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -325,11 +319,11 @@ winlogbeat.event_logs: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -449,7 +443,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -563,7 +578,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -717,6 +732,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -739,7 +757,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -857,7 +875,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -891,7 +909,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -904,7 +922,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Winlogbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -930,11 +948,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -978,8 +998,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1033,7 +1052,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1047,13 +1066,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'winlogbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "winlogbeat" +#setup.ilm.rollover_alias: 'winlogbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'winlogbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1068,7 +1087,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1123,9 +1142,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1192,8 +1210,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Winlogbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1303,6 +1320,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1314,7 +1352,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1338,12 +1377,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/winlogbeat/winlogbeat.yml b/winlogbeat/winlogbeat.yml index 8887e8d75c7..d1308e2a2ed 100644 --- a/winlogbeat/winlogbeat.yml +++ b/winlogbeat/winlogbeat.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/winlogbeat/index.html -#======================= Winlogbeat specific options =========================== +# ======================== Winlogbeat specific options ========================= # event_logs specifies a list of event logs to monitor as well as any # accompanying options. The YAML data type of event_logs is a list of @@ -17,6 +17,7 @@ # forwarded, ignore_older, level, event_id, provider, and include_xml. Please # visit the documentation for the complete details of each option. # https://go.es.io/WinlogbeatConfig + winlogbeat.event_logs: - name: Application ignore_older: 72h @@ -25,7 +26,10 @@ winlogbeat.event_logs: - name: Security -#==================== Elasticsearch template settings ========================== + - name: ForwardedEvents + tags: [forwarded] + +# ====================== Elasticsearch template settings ======================= setup.template.settings: index.number_of_shards: 1 @@ -33,7 +37,7 @@ setup.template.settings: #_source.enabled: false -#================================ General ===================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -48,8 +52,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -61,7 +64,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -78,7 +81,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Winlogbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -91,11 +94,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -108,7 +111,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -123,16 +126,13 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== - -# Configure processors to enhance or manipulate events generated by the beat. - +# ================================= Processors ================================= processors: - - add_host_metadata: ~ + - add_host_metadata: + when.not.contains.tags: forwarded - add_cloud_metadata: ~ - - add_docker_metadata: ~ -#================================ Logging ===================================== +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -143,8 +143,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# winlogbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Winlogbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -165,7 +165,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/x-pack/auditbeat/Makefile b/x-pack/auditbeat/Makefile index 56633e2b3e5..019d3b9309a 100644 --- a/x-pack/auditbeat/Makefile +++ b/x-pack/auditbeat/Makefile @@ -1,3 +1,3 @@ ES_BEATS ?= ../.. -include $(ES_BEATS)/dev-tools/make/xpack.mk +include $(ES_BEATS)/dev-tools/make/mage.mk diff --git a/x-pack/auditbeat/auditbeat.docker.yml b/x-pack/auditbeat/auditbeat.docker.yml index a012bbb6aad..19c9bd1b477 100644 --- a/x-pack/auditbeat/auditbeat.docker.yml +++ b/x-pack/auditbeat/auditbeat.docker.yml @@ -12,6 +12,7 @@ auditbeat.modules: - /sbin - /usr/sbin - /etc + processors: - add_cloud_metadata: ~ - add_docker_metadata: ~ diff --git a/x-pack/auditbeat/auditbeat.reference.yml b/x-pack/auditbeat/auditbeat.reference.yml index a164895d2d0..2e6f8e955fb 100644 --- a/x-pack/auditbeat/auditbeat.reference.yml +++ b/x-pack/auditbeat/auditbeat.reference.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/auditbeat/index.html -#============================ Config Reloading ================================ +# ============================== Config Reloading ============================== # Config reloading allows to dynamically load modules. Each file which is # monitored must contain one or multiple modules as a list. @@ -26,7 +26,7 @@ auditbeat.config.modules: # disable startup delay. auditbeat.max_start_delay: 10s -#========================== Modules configuration ============================= +# =========================== Modules configuration ============================ auditbeat.modules: # The auditd module collects events from the audit framework in the Linux @@ -174,7 +174,8 @@ auditbeat.modules: login.wtmp_file_pattern: /var/log/wtmp* login.btmp_file_pattern: /var/log/btmp* -#================================ General ====================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -282,7 +283,7 @@ auditbeat.modules: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -299,153 +300,153 @@ auditbeat.modules: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Auditbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -458,11 +459,11 @@ auditbeat.modules: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -582,7 +583,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -696,7 +718,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -850,6 +872,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -872,7 +897,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -990,7 +1015,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -1024,7 +1049,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -1037,7 +1062,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Auditbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -1063,11 +1088,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -1111,8 +1138,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1166,7 +1192,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1180,13 +1206,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'auditbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "auditbeat" +#setup.ilm.rollover_alias: 'auditbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'auditbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1201,7 +1227,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1256,9 +1282,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1325,8 +1350,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Auditbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1436,6 +1460,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1447,7 +1492,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1471,12 +1517,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/x-pack/auditbeat/auditbeat.yml b/x-pack/auditbeat/auditbeat.yml index f174d7a793e..408e52004e6 100644 --- a/x-pack/auditbeat/auditbeat.yml +++ b/x-pack/auditbeat/auditbeat.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/auditbeat/index.html -#========================== Modules configuration ============================= +# =========================== Modules configuration ============================ auditbeat.modules: - module: auditd @@ -75,13 +75,14 @@ auditbeat.modules: login.wtmp_file_pattern: /var/log/wtmp* login.btmp_file_pattern: /var/log/btmp* -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false -#================================ General ===================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -96,8 +97,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -109,7 +109,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -126,7 +126,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Auditbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -139,11 +139,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -156,7 +156,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -171,7 +171,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -180,7 +180,8 @@ processors: - add_cloud_metadata: ~ - add_docker_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -191,8 +192,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# auditbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Auditbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -213,7 +214,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/x-pack/auditbeat/module/system/process/process.go b/x-pack/auditbeat/module/system/process/process.go index 48c6914142f..6c9e5a7db6a 100644 --- a/x-pack/auditbeat/module/system/process/process.go +++ b/x-pack/auditbeat/module/system/process/process.go @@ -66,6 +66,21 @@ func (action eventAction) String() string { } } +func (action eventAction) Type() string { + switch action { + case eventActionExistingProcess: + return "info" + case eventActionProcessStarted: + return "start" + case eventActionProcessStopped: + return "end" + case eventActionProcessError: + return "info" + default: + return "info" + } +} + func init() { mb.Registry.MustAddMetricSet(moduleName, metricsetName, New, mb.DefaultMetricSet(), @@ -319,8 +334,10 @@ func (ms *MetricSet) processEvent(process *Process, eventType string, action eve event := mb.Event{ RootFields: common.MapStr{ "event": common.MapStr{ - "kind": eventType, - "action": action.String(), + "kind": eventType, + "category": []string{"process"}, + "type": []string{action.Type()}, + "action": action.String(), }, "process": process.toMapStr(), "message": processMessage(process, action), diff --git a/x-pack/auditbeat/module/system/process/process_test.go b/x-pack/auditbeat/module/system/process/process_test.go index 18e64998618..2a33022ddef 100644 --- a/x-pack/auditbeat/module/system/process/process_test.go +++ b/x-pack/auditbeat/module/system/process/process_test.go @@ -66,9 +66,11 @@ func TestProcessEvent(t *testing.T) { } expectedRootFields := map[string]interface{}{ - "event.kind": "event", - "event.action": "process_started", - "message": "Process zsh (PID: 9086) by user elastic STARTED", + "event.kind": "event", + "event.category": []string{"process"}, + "event.type": []string{"start"}, + "event.action": "process_started", + "message": "Process zsh (PID: 9086) by user elastic STARTED", "process.pid": 9086, "process.ppid": 9085, diff --git a/x-pack/dockerlogbeat/Makefile b/x-pack/dockerlogbeat/Makefile index 950d9029145..2367bfdeaa4 100644 --- a/x-pack/dockerlogbeat/Makefile +++ b/x-pack/dockerlogbeat/Makefile @@ -9,7 +9,7 @@ ES_BEATS?=../../ GOX_OS=linux GOX_FLAGS=-arch="amd64 386 arm ppc64 ppc64le" -include $(ES_BEATS)/dev-tools/make/xpack.mk +include $(ES_BEATS)/dev-tools/make/mage.mk .PHONY: unit-tests diff --git a/x-pack/elastic-agent/CHANGELOG.asciidoc b/x-pack/elastic-agent/CHANGELOG.asciidoc index b81acebc9e8..a1fccd61728 100644 --- a/x-pack/elastic-agent/CHANGELOG.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.asciidoc @@ -25,6 +25,12 @@ - Fixed injected log path to monitoring beat {pull}17833[17833] - Make sure that the Elastic Agent connect over TLS in cloud. {pull}17843[17843] - Moved stream.* fields to top of event {pull}17858[17858] +- Fix an issue where the checkin_frequency, jitter, and backoff options where not configurable. {pull}17843[17843] +- ECS compliant Elastic agent metadata sent to fleet {pull}18006[18006] +- Use default output by default {pull}18091[18091] +- Use /tmp for default monitoring endpoint location for libbeat {pull}18131[18131] +- Fix panic and flaky tests for the Agent. {pull}18135[18135] +- Fix default configuration after enroll {pull}18232[18232] ==== New features @@ -37,3 +43,12 @@ - Expose stream.* variables in events {pull}17468[17468] - Monitoring configuration reloadable {pull}17855[17855] - Pack ECS metadata to request payload send to fleet {pull}17894[17894] +- Allow CLI overrides of paths {pull}17781[17781] +- Enable Filebeat input: S3, Azureeventhub, cloudfoundry, httpjson, netflow, o365audit. {pull}17909[17909] +- Configurable log level {pull}18083[18083] +- Use data subfolder as default for process logs {pull}17960[17960] +- Do not require unnecessary configuration {pull}18003[18003] +- Enable debug log level for Metricbeat and Filebeat when run under the Elastic Agent. {pull}17935[17935] +- Enable introspecting configuration {pull}18124[18124] +- Follow home path for all config files {pull}18161[18161] +- Use nested objects so fleet can handle metadata correctly {pull}18234[18234] diff --git a/x-pack/elastic-agent/Makefile b/x-pack/elastic-agent/Makefile index 71dd1dea852..50e8845e1b2 100644 --- a/x-pack/elastic-agent/Makefile +++ b/x-pack/elastic-agent/Makefile @@ -7,7 +7,7 @@ ES_BEATS ?= ../.. # # Includes # -include $(ES_BEATS)/dev-tools/make/xpack.mk +include $(ES_BEATS)/dev-tools/make/mage.mk .PHONY: docs docs: ## @build Builds the documentation for the beat diff --git a/x-pack/elastic-agent/_meta/common.p2.yml b/x-pack/elastic-agent/_meta/common.p2.yml deleted file mode 100644 index 651f02282dd..00000000000 --- a/x-pack/elastic-agent/_meta/common.p2.yml +++ /dev/null @@ -1,121 +0,0 @@ -###################################### -# Fleet configuration -###################################### -outputs: - default: - type: elasticsearch - hosts: [127.0.0.1:9200] - username: elastic - password: changeme - -datasources: - - namespace: default - use_output: default - inputs: - - type: system/metrics - streams: - - metricset: cpu - dataset: system.cpu - - metricset: memory - dataset: system.memory - - metricset: network - dataset: system.network - - metricset: filesystem - dataset: system.filesystem - -management: - # Mode of management, the Elastic Agent support two modes of operation: - # - # local: The Elastic Agent will expect to find the inputs configuration in the local file. - # - # Default is local. - mode: "local" - - fleet: - access_token: "" - kibana: - # kibana minimal configuration - host: "localhost:5601" - ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # optional values - #protocol: "https" - #username: "elastic" - #password: "changeme" - #path: "" - #ssl.verification_mode: full - #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] - #ssl.cipher_suites: [] - #ssl.curve_types: [] - - reporting: - log: - # format in which logs will be written, options are json or default. - format: "default" - fleet: - # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. - enabled: false - - # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. - reporting_threshold: 10000 - - # Frequency used to check the queue of events to be sent out to fleet. - reporting_check_frequency_sec: 30 - - # Allow fleet to reload his configuration locally on disk. - # Notes: Only specific process configuration will be reloaded. - reload: - # enabled configure the Elastic Agent to reload or not the local configuration. - # - # Default is true - enabled: true - - # period define how frequent we should look for changes in the configuration. - period: 10s - -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" - -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s - -retry: - # Enabled determines whether retry is possible. Default is false. - enabled: true - # RetriesCount specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retriesCount: 3 - # Delay specifies delay in ms between retries. Default is 30s - delay: 30s - # MaxDelay specifies maximum delay in ms between retries. Default is 300s - maxDelay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false - -settings.monitoring: - # enabled turns on monitoring of running processes - enabled: false - # enables log monitoring - logs: false - # enables metrics monitoring - metrics: false diff --git a/x-pack/elastic-agent/_meta/common.reference.p2.yml b/x-pack/elastic-agent/_meta/common.reference.p2.yml deleted file mode 100644 index eaa97536e0b..00000000000 --- a/x-pack/elastic-agent/_meta/common.reference.p2.yml +++ /dev/null @@ -1,121 +0,0 @@ -###################################### -# Fleet configuration -###################################### -outputs: - default: - type: elasticsearch - hosts: [127.0.0.1:9200] - username: elastic - password: changeme - -datasources: - - namespace: default - use_output: default - inputs: - - type: system/metrics - streams: - - metricset: cpu - dataset: system.cpu - - metricset: memory - dataset: system.memory - - metricset: network - dataset: system.network - - metricset: filesystem - dataset: system.filesystem - -management: - # Mode of management, the Elastic Agent currently only support the following mode: - # - # local: The Elastic Agent will expect to find the inputs configuration in the local file. - # - # Default is local. - mode: local - - fleet: - access_token: "" - kibana: - # kibana minimal configuration - host: "localhost:5601" - ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # optional values - #protocol: "https" - #username: "elastic" - #password: "changeme" - #path: "" - #ssl.verification_mode: full - #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] - #ssl.cipher_suites: [] - #ssl.curve_types: [] - - reporting: - log: - # format in which logs will be written, options are json or default. - format: "default" - fleet: - # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. - enabled: false - - # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. - reporting_threshold: 10000 - - # Frequency used to check the queue of events to be sent out to fleet. - reporting_check_frequency_sec: 30 - - # Allow fleet to reload his configuration locally on disk. - # Notes: Only specific process configuration will be reloaded. - reload: - # enabled configure the Elastic Agent to reload or not the local configuration. - # - # Default is true - enabled: true - - # period define how frequent we should look for changes in the configuration. - period: 10s - -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" - -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s - -retry: - # enabled determines whether retry is possible. Default is false. - enabled: true - # retries_count specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retries_count: 3 - # delay specifies delay in ms between retries. Default is 30s - delay: 30s - # max_delay specifies maximum delay in ms between retries. Default is 300s - max_delay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false - -settings.monitoring: - # enabled turns on monitoring of running processes - enabled: false - # enables log monitoring - logs: false - # enables metrics monitoring - metrics: false diff --git a/x-pack/elastic-agent/_meta/common.p1.yml b/x-pack/elastic-agent/_meta/config/common.p1.yml.tmpl similarity index 100% rename from x-pack/elastic-agent/_meta/common.p1.yml rename to x-pack/elastic-agent/_meta/config/common.p1.yml.tmpl diff --git a/x-pack/elastic-agent/_meta/config/common.p2.yml.tmpl b/x-pack/elastic-agent/_meta/config/common.p2.yml.tmpl new file mode 100644 index 00000000000..0e7c950cdd5 --- /dev/null +++ b/x-pack/elastic-agent/_meta/config/common.p2.yml.tmpl @@ -0,0 +1,125 @@ +###################################### +# Fleet configuration +###################################### +outputs: + default: + type: elasticsearch + hosts: [127.0.0.1:9200] + username: elastic + password: changeme + +datasources: + - namespace: default + use_output: default + inputs: + - type: system/metrics + streams: + - metricset: cpu + dataset: system.cpu + - metricset: memory + dataset: system.memory + - metricset: network + dataset: system.network + - metricset: filesystem + dataset: system.filesystem + +settings.monitoring: + # enabled turns on monitoring of running processes + enabled: true + # enables log monitoring + logs: true + # enables metrics monitoring + metrics: true + +# management: +# # Mode of management, the Elastic Agent support two modes of operation: +# # +# # local: The Elastic Agent will expect to find the inputs configuration in the local file. +# # +# # Default is local. +# mode: "local" + +# fleet: +# access_token: "" +# kibana: +# # kibana minimal configuration +# host: "localhost:5601" +# ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + +# # optional values +# #protocol: "https" +# #username: "elastic" +# #password: "changeme" +# #path: "" +# #ssl.verification_mode: full +# #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] +# #ssl.cipher_suites: [] +# #ssl.curve_types: [] + +# reporting: +# log: +# # format in which logs will be written, options are json or default. +# format: "default" +# fleet: +# # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. +# enabled: false + +# # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. +# reporting_threshold: 10000 + +# # Frequency used to check the queue of events to be sent out to fleet. +# reporting_check_frequency_sec: 30 + +# # Allow fleet to reload his configuration locally on disk. +# # Notes: Only specific process configuration will be reloaded. +# reload: +# # enabled configure the Elastic Agent to reload or not the local configuration. +# # +# # Default is true +# enabled: true + +# # period define how frequent we should look for changes in the configuration. +# period: 10s + +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # Enabled determines whether retry is possible. Default is false. +# enabled: true +# # RetriesCount specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retriesCount: 3 +# # Delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # MaxDelay specifies maximum delay in ms between retries. Default is 300s +# maxDelay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace diff --git a/x-pack/elastic-agent/_meta/common.reference.p1.yml b/x-pack/elastic-agent/_meta/config/common.reference.p1.yml.tmpl similarity index 100% rename from x-pack/elastic-agent/_meta/common.reference.p1.yml rename to x-pack/elastic-agent/_meta/config/common.reference.p1.yml.tmpl diff --git a/x-pack/elastic-agent/_meta/config/common.reference.p2.yml.tmpl b/x-pack/elastic-agent/_meta/config/common.reference.p2.yml.tmpl new file mode 100644 index 00000000000..15582908fe7 --- /dev/null +++ b/x-pack/elastic-agent/_meta/config/common.reference.p2.yml.tmpl @@ -0,0 +1,125 @@ +###################################### +# Fleet configuration +###################################### +outputs: + default: + type: elasticsearch + hosts: [127.0.0.1:9200] + username: elastic + password: changeme + +datasources: + - namespace: default + use_output: default + inputs: + - type: system/metrics + streams: + - metricset: cpu + dataset: system.cpu + - metricset: memory + dataset: system.memory + - metricset: network + dataset: system.network + - metricset: filesystem + dataset: system.filesystem + +# management: +# # Mode of management, the Elastic Agent support two modes of operation: +# # +# # local: The Elastic Agent will expect to find the inputs configuration in the local file. +# # +# # Default is local. +# mode: "local" + +# fleet: +# access_token: "" +# kibana: +# # kibana minimal configuration +# host: "localhost:5601" +# ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + +# # optional values +# #protocol: "https" +# #username: "elastic" +# #password: "changeme" +# #path: "" +# #ssl.verification_mode: full +# #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] +# #ssl.cipher_suites: [] +# #ssl.curve_types: [] + +# reporting: +# log: +# # format in which logs will be written, options are json or default. +# format: "default" +# fleet: +# # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. +# enabled: false + +# # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. +# reporting_threshold: 10000 + +# # Frequency used to check the queue of events to be sent out to fleet. +# reporting_check_frequency_sec: 30 + +# # Allow fleet to reload his configuration locally on disk. +# # Notes: Only specific process configuration will be reloaded. +# reload: +# # enabled configure the Elastic Agent to reload or not the local configuration. +# # +# # Default is true +# enabled: true + +# # period define how frequent we should look for changes in the configuration. +# period: 10s + +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # Enabled determines whether retry is possible. Default is false. +# enabled: true +# # RetriesCount specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retriesCount: 3 +# # Delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # MaxDelay specifies maximum delay in ms between retries. Default is 300s +# maxDelay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false + +# settings.monitoring: +# # enabled turns on monitoring of running processes +# enabled: false +# # enables log monitoring +# logs: false +# # enables metrics monitoring +# metrics: false + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace diff --git a/x-pack/elastic-agent/_meta/config/elastic-agent.docker.yml.tmpl b/x-pack/elastic-agent/_meta/config/elastic-agent.docker.yml.tmpl new file mode 100644 index 00000000000..fc7edf73413 --- /dev/null +++ b/x-pack/elastic-agent/_meta/config/elastic-agent.docker.yml.tmpl @@ -0,0 +1,125 @@ +###################################### +# Fleet configuration +###################################### +outputs: + default: + type: elasticsearch + hosts: '${ELASTICSEARCH_HOSTS:http://elasticsearch:9200}' + username: '${ELASTICSEARCH_USERNAME:elastic}' + password: '${ELASTICSEARCH_PASSWORD:changeme}' + +datasources: + - namespace: default + use_output: default + inputs: + - type: system/metrics + streams: + - metricset: cpu + dataset: system.cpu + - metricset: memory + dataset: system.memory + - metricset: network + dataset: system.network + - metricset: filesystem + dataset: system.filesystem + +# management: +# # Mode of management, the Elastic Agent support two modes of operation: +# # +# # local: The Elastic Agent will expect to find the inputs configuration in the local file. +# # +# # Default is local. +# mode: "local" + +# fleet: +# access_token: "" +# kibana: +# # kibana minimal configuration +# host: "localhost:5601" +# ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + +# # optional values +# #protocol: "https" +# #username: "elastic" +# #password: "changeme" +# #path: "" +# #ssl.verification_mode: full +# #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] +# #ssl.cipher_suites: [] +# #ssl.curve_types: [] + +# reporting: +# log: +# # format in which logs will be written, options are json or default. +# format: "default" +# fleet: +# # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. +# enabled: false + +# # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. +# reporting_threshold: 10000 + +# # Frequency used to check the queue of events to be sent out to fleet. +# reporting_check_frequency_sec: 30 + +# # Allow fleet to reload his configuration locally on disk. +# # Notes: Only specific process configuration will be reloaded. +# reload: +# # enabled configure the Elastic Agent to reload or not the local configuration. +# # +# # Default is true +# enabled: true + +# # period define how frequent we should look for changes in the configuration. +# period: 10s + +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # Enabled determines whether retry is possible. Default is false. +# enabled: true +# # RetriesCount specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retriesCount: 3 +# # Delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # MaxDelay specifies maximum delay in ms between retries. Default is 300s +# maxDelay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false + +# settings.monitoring: +# # enabled turns on monitoring of running processes +# enabled: false +# # enables log monitoring +# logs: false +# # enables metrics monitoring +# metrics: false + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace diff --git a/x-pack/elastic-agent/_meta/config/elastic-agent.reference.yml.tmpl b/x-pack/elastic-agent/_meta/config/elastic-agent.reference.yml.tmpl new file mode 100644 index 00000000000..d3d5225f0ec --- /dev/null +++ b/x-pack/elastic-agent/_meta/config/elastic-agent.reference.yml.tmpl @@ -0,0 +1,2 @@ +{{template "common.reference.p1.yml.tmpl" .}} +{{template "common.reference.p2.yml.tmpl" .}} diff --git a/x-pack/elastic-agent/_meta/config/elastic-agent.yml.tmpl b/x-pack/elastic-agent/_meta/config/elastic-agent.yml.tmpl new file mode 100644 index 00000000000..01634504728 --- /dev/null +++ b/x-pack/elastic-agent/_meta/config/elastic-agent.yml.tmpl @@ -0,0 +1,2 @@ +{{template "common.p1.yml.tmpl" .}} +{{template "common.p2.yml.tmpl" .}} diff --git a/x-pack/elastic-agent/_meta/elastic-agent.docker.yml b/x-pack/elastic-agent/_meta/elastic-agent.docker.yml deleted file mode 100644 index dbf7931b690..00000000000 --- a/x-pack/elastic-agent/_meta/elastic-agent.docker.yml +++ /dev/null @@ -1,95 +0,0 @@ -###################################### -# Fleet configuration -###################################### -outputs: - default: - type: elasticsearch - hosts: '${ELASTICSEARCH_HOSTS:http://elasticsearch:9200}' - username: '${ELASTICSEARCH_USERNAME:elastic}' - password: '${ELASTICSEARCH_PASSWORD:changeme}' - -datasources: - - namespace: default - use_output: default - inputs: - - type: system/metrics - streams: - - metricset: cpu - dataset: system.cpu - - metricset: memory - dataset: system.memory - - metricset: network - dataset: system.network - - metricset: filesystem - dataset: system.filesystem - -management: - # Mode of management, the Elastic Agent support two modes of operation: - # - # local: The Elastic Agent will expect to find the inputs configuration in the local file. - # - # Default is local. - mode: "local" - - reporting: - log: - # format in which logs will be written, options are json or default. - format: "default" - - # Allow the Elastic Agent to reload his configuration locally on disk. - # Notes: Only specific process configuration will be reloaded. - reload: - # enabled configure the Elastic Agent to reload or not the local configuration. - # - # Default is true - enabled: true - - # period define how frequent we should look for changes in the configuration. - period: 10s - -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present Elastic Agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" - -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s - -retry: - # enabled determines whether retry is possible. Default is false. - enabled: true - # retries_count specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retries_count: 3 - # delay specifies delay in ms between retries. Default is 30s - delay: 30s - # max_delay specifies maximum delay in ms between retries. Default is 300s - max_delay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false - -settings.monitoring: - # enabled turns on monitoring of running processes - enabled: false - # enables log monitoring - logs: false - # enables metrics monitoring - metrics: false diff --git a/x-pack/elastic-agent/_meta/elastic-agent.fleet.yml b/x-pack/elastic-agent/_meta/elastic-agent.fleet.yml index 08304cbaa92..30c0a68431b 100644 --- a/x-pack/elastic-agent/_meta/elastic-agent.fleet.yml +++ b/x-pack/elastic-agent/_meta/elastic-agent.fleet.yml @@ -1,45 +1,63 @@ -#================================ General ===================================== +# ================================ General ===================================== # Beats is configured under Fleet, you can define most settings # from the Kibana UI. You can update this file to configure the settings that # are not supported by Fleet. management: mode: "fleet" -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present Elastic Agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" + # Check in frequency configure the time between calls to fleet to retrieve the new configuration. + # + # Default is 30s + #checkin_frequency: 30s -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s + # Add variance between API calls to better distribute the calls. + #jitter: 5s -retry: - # enabled determines whether retry is possible. Default is false. - enabled: true - # retries_count specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retries_count: 3 - # delay specifies delay in ms between retries. Default is 30s - delay: 30s - # max_delay specifies maximum delay in ms between retries. Default is 300s - max_delay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false + # The Elastic Agent does Exponential backoff when an error happen. + # + #backoff: + # + # Initial time to wait before retrying the call. + # init: 1s + # + # Maximum time to wait before retrying the call. + # max: 10s + +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present Elastic Agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # enabled determines whether retry is possible. Default is false. +# enabled: true +# # retries_count specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retries_count: 3 +# # delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # max_delay specifies maximum delay in ms between retries. Default is 300s +# max_delay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false diff --git a/x-pack/elastic-agent/_meta/elastic-agent.yml b/x-pack/elastic-agent/_meta/elastic-agent.yml index 12fbfabc3fe..15582908fe7 100644 --- a/x-pack/elastic-agent/_meta/elastic-agent.yml +++ b/x-pack/elastic-agent/_meta/elastic-agent.yml @@ -23,74 +23,103 @@ datasources: - metricset: filesystem dataset: system.filesystem -management: - # Mode of management, the Elastic Agent support two modes of operation: - # - # local: The Elastic Agent will expect to find the inputs configuration in the local file. - # - # Default is local. - mode: "local" +# management: +# # Mode of management, the Elastic Agent support two modes of operation: +# # +# # local: The Elastic Agent will expect to find the inputs configuration in the local file. +# # +# # Default is local. +# mode: "local" +# fleet: +# access_token: "" +# kibana: +# # kibana minimal configuration +# host: "localhost:5601" +# ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - reporting: - log: - # format in which logs will be written, options are json or default. - format: "default" +# # optional values +# #protocol: "https" +# #username: "elastic" +# #password: "changeme" +# #path: "" +# #ssl.verification_mode: full +# #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] +# #ssl.cipher_suites: [] +# #ssl.curve_types: [] - # Allow the Elastic Agent to reload his configuration locally on disk. - # Notes: Only specific process configuration will be reloaded. - reload: - # enabled configure the Elastic Agent to reload or not the local configuration. - # - # Default is true - enabled: true +# reporting: +# log: +# # format in which logs will be written, options are json or default. +# format: "default" +# fleet: +# # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. +# enabled: false - # period define how frequent we should look for changes in the configuration. - period: 10s +# # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. +# reporting_threshold: 10000 -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present Elastic Agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" +# # Frequency used to check the queue of events to be sent out to fleet. +# reporting_check_frequency_sec: 30 -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s +# # Allow fleet to reload his configuration locally on disk. +# # Notes: Only specific process configuration will be reloaded. +# reload: +# # enabled configure the Elastic Agent to reload or not the local configuration. +# # +# # Default is true +# enabled: true -retry: - # enabled determines whether retry is possible. Default is false. - enabled: true - # retries_count specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retries_count: 3 - # delay specifies delay in ms between retries. Default is 30s - delay: 30s - # max_delay specifies maximum delay in ms between retries. Default is 300s - max_delay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false +# # period define how frequent we should look for changes in the configuration. +# period: 10s -settings.monitoring: - # enabled turns on monitoring of running processes - enabled: false - # enables log monitoring - logs: false - # enables metrics monitoring - metrics: false +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # Enabled determines whether retry is possible. Default is false. +# enabled: true +# # RetriesCount specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retriesCount: 3 +# # Delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # MaxDelay specifies maximum delay in ms between retries. Default is 300s +# maxDelay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false + +# settings.monitoring: +# # enabled turns on monitoring of running processes +# enabled: false +# # enables log monitoring +# logs: false +# # enables metrics monitoring +# metrics: false + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace diff --git a/x-pack/elastic-agent/docs/elastic-agent_configuration_example.yml b/x-pack/elastic-agent/docs/elastic-agent_configuration_example.yml index 08b92363921..e91363998fb 100644 --- a/x-pack/elastic-agent/docs/elastic-agent_configuration_example.yml +++ b/x-pack/elastic-agent/docs/elastic-agent_configuration_example.yml @@ -25,6 +25,10 @@ outputs: settings.monitoring: use_output: monitoring +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace + datasources: # use the nginx package - id?: nginx-x1 diff --git a/x-pack/elastic-agent/elastic-agent.docker.yml b/x-pack/elastic-agent/elastic-agent.docker.yml index dbf7931b690..fc7edf73413 100644 --- a/x-pack/elastic-agent/elastic-agent.docker.yml +++ b/x-pack/elastic-agent/elastic-agent.docker.yml @@ -23,73 +23,103 @@ datasources: - metricset: filesystem dataset: system.filesystem -management: - # Mode of management, the Elastic Agent support two modes of operation: - # - # local: The Elastic Agent will expect to find the inputs configuration in the local file. - # - # Default is local. - mode: "local" +# management: +# # Mode of management, the Elastic Agent support two modes of operation: +# # +# # local: The Elastic Agent will expect to find the inputs configuration in the local file. +# # +# # Default is local. +# mode: "local" - reporting: - log: - # format in which logs will be written, options are json or default. - format: "default" +# fleet: +# access_token: "" +# kibana: +# # kibana minimal configuration +# host: "localhost:5601" +# ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - # Allow the Elastic Agent to reload his configuration locally on disk. - # Notes: Only specific process configuration will be reloaded. - reload: - # enabled configure the Elastic Agent to reload or not the local configuration. - # - # Default is true - enabled: true +# # optional values +# #protocol: "https" +# #username: "elastic" +# #password: "changeme" +# #path: "" +# #ssl.verification_mode: full +# #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] +# #ssl.cipher_suites: [] +# #ssl.curve_types: [] - # period define how frequent we should look for changes in the configuration. - period: 10s +# reporting: +# log: +# # format in which logs will be written, options are json or default. +# format: "default" +# fleet: +# # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. +# enabled: false -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present Elastic Agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" +# # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. +# reporting_threshold: 10000 -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s +# # Frequency used to check the queue of events to be sent out to fleet. +# reporting_check_frequency_sec: 30 -retry: - # enabled determines whether retry is possible. Default is false. - enabled: true - # retries_count specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retries_count: 3 - # delay specifies delay in ms between retries. Default is 30s - delay: 30s - # max_delay specifies maximum delay in ms between retries. Default is 300s - max_delay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false +# # Allow fleet to reload his configuration locally on disk. +# # Notes: Only specific process configuration will be reloaded. +# reload: +# # enabled configure the Elastic Agent to reload or not the local configuration. +# # +# # Default is true +# enabled: true -settings.monitoring: - # enabled turns on monitoring of running processes - enabled: false - # enables log monitoring - logs: false - # enables metrics monitoring - metrics: false +# # period define how frequent we should look for changes in the configuration. +# period: 10s + +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # Enabled determines whether retry is possible. Default is false. +# enabled: true +# # RetriesCount specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retriesCount: 3 +# # Delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # MaxDelay specifies maximum delay in ms between retries. Default is 300s +# maxDelay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false + +# settings.monitoring: +# # enabled turns on monitoring of running processes +# enabled: false +# # enables log monitoring +# logs: false +# # enables metrics monitoring +# metrics: false + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace diff --git a/x-pack/elastic-agent/elastic-agent.reference.yml b/x-pack/elastic-agent/elastic-agent.reference.yml index 2fb40550bc7..ae06f02c816 100644 --- a/x-pack/elastic-agent/elastic-agent.reference.yml +++ b/x-pack/elastic-agent/elastic-agent.reference.yml @@ -3,6 +3,7 @@ # This file is an example configuration file highlighting only the most common # options. The elastic-agent.reference.yml file from the same directory contains all the # supported options with more comments. You can use it as a reference. + ###################################### # Fleet configuration ###################################### @@ -28,99 +29,104 @@ datasources: - metricset: filesystem dataset: system.filesystem -management: - # Mode of management, the Elastic Agent currently only support the following mode: - # - # local: The Elastic Agent will expect to find the inputs configuration in the local file. - # - # Default is local. - mode: local - - fleet: - access_token: "" - kibana: - # kibana minimal configuration - host: "localhost:5601" - ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # optional values - #protocol: "https" - #username: "elastic" - #password: "changeme" - #path: "" - #ssl.verification_mode: full - #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] - #ssl.cipher_suites: [] - #ssl.curve_types: [] - - reporting: - log: - # format in which logs will be written, options are json or default. - format: "default" - fleet: - # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. - enabled: false - - # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. - reporting_threshold: 10000 - - # Frequency used to check the queue of events to be sent out to fleet. - reporting_check_frequency_sec: 30 - - # Allow fleet to reload his configuration locally on disk. - # Notes: Only specific process configuration will be reloaded. - reload: - # enabled configure the Elastic Agent to reload or not the local configuration. - # - # Default is true - enabled: true - - # period define how frequent we should look for changes in the configuration. - period: 10s - -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" - -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s - -retry: - # enabled determines whether retry is possible. Default is false. - enabled: true - # retries_count specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retries_count: 3 - # delay specifies delay in ms between retries. Default is 30s - delay: 30s - # max_delay specifies maximum delay in ms between retries. Default is 300s - max_delay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false - -settings.monitoring: - # enabled turns on monitoring of running processes - enabled: false - # enables log monitoring - logs: false - # enables metrics monitoring - metrics: false +# management: +# # Mode of management, the Elastic Agent support two modes of operation: +# # +# # local: The Elastic Agent will expect to find the inputs configuration in the local file. +# # +# # Default is local. +# mode: "local" + +# fleet: +# access_token: "" +# kibana: +# # kibana minimal configuration +# host: "localhost:5601" +# ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + +# # optional values +# #protocol: "https" +# #username: "elastic" +# #password: "changeme" +# #path: "" +# #ssl.verification_mode: full +# #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] +# #ssl.cipher_suites: [] +# #ssl.curve_types: [] + +# reporting: +# log: +# # format in which logs will be written, options are json or default. +# format: "default" +# fleet: +# # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. +# enabled: false + +# # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. +# reporting_threshold: 10000 + +# # Frequency used to check the queue of events to be sent out to fleet. +# reporting_check_frequency_sec: 30 + +# # Allow fleet to reload his configuration locally on disk. +# # Notes: Only specific process configuration will be reloaded. +# reload: +# # enabled configure the Elastic Agent to reload or not the local configuration. +# # +# # Default is true +# enabled: true + +# # period define how frequent we should look for changes in the configuration. +# period: 10s + +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # Enabled determines whether retry is possible. Default is false. +# enabled: true +# # RetriesCount specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retriesCount: 3 +# # Delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # MaxDelay specifies maximum delay in ms between retries. Default is 300s +# maxDelay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false + +# settings.monitoring: +# # enabled turns on monitoring of running processes +# enabled: false +# # enables log monitoring +# logs: false +# # enables metrics monitoring +# metrics: false + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace + diff --git a/x-pack/elastic-agent/elastic-agent.yml b/x-pack/elastic-agent/elastic-agent.yml index 24331ef7f9f..d28cce65ab5 100644 --- a/x-pack/elastic-agent/elastic-agent.yml +++ b/x-pack/elastic-agent/elastic-agent.yml @@ -3,6 +3,7 @@ # This file is an example configuration file highlighting only the most common # options. The elastic-agent.reference.yml file from the same directory contains all the # supported options with more comments. You can use it as a reference. + ###################################### # Fleet configuration ###################################### @@ -28,99 +29,104 @@ datasources: - metricset: filesystem dataset: system.filesystem -management: - # Mode of management, the Elastic Agent support two modes of operation: - # - # local: The Elastic Agent will expect to find the inputs configuration in the local file. - # - # Default is local. - mode: "local" - - fleet: - access_token: "" - kibana: - # kibana minimal configuration - host: "localhost:5601" - ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] - - # optional values - #protocol: "https" - #username: "elastic" - #password: "changeme" - #path: "" - #ssl.verification_mode: full - #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] - #ssl.cipher_suites: [] - #ssl.curve_types: [] - - reporting: - log: - # format in which logs will be written, options are json or default. - format: "default" - fleet: - # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. - enabled: false - - # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. - reporting_threshold: 10000 - - # Frequency used to check the queue of events to be sent out to fleet. - reporting_check_frequency_sec: 30 - - # Allow fleet to reload his configuration locally on disk. - # Notes: Only specific process configuration will be reloaded. - reload: - # enabled configure the Elastic Agent to reload or not the local configuration. - # - # Default is true - enabled: true - - # period define how frequent we should look for changes in the configuration. - period: 10s - -download: - # source of the artifacts, requires elastic like structure and naming of the binaries - # e.g /windows-x86.zip - sourceURI: "https://artifacts.elastic.co/downloads/beats/" - # path to the directory containing downloaded packages - target_directory: "${path.data}/downloads" - # timeout for downloading package - timeout: 30s - # file path to a public key used for verifying downloaded artifacts - # if not file is present agent will try to load public key from elastic.co website. - pgpfile: "${path.data}/elastic.pgp" - # install_path describes the location of installed packages/programs. It is also used - # for reading program specifications. - install_path: "${path.data}/install" - -process: - # minimal port number for spawned processes - min_port: 10000 - # maximum port number for spawned processes - max_port: 30000 - # timeout for creating new processes. when process is not successfully created by this timeout - # start operation is considered a failure - spawn_timeout: 30s - -retry: - # Enabled determines whether retry is possible. Default is false. - enabled: true - # RetriesCount specifies number of retries. Default is 3. - # Retry count of 1 means it will be retried one time after one failure. - retriesCount: 3 - # Delay specifies delay in ms between retries. Default is 30s - delay: 30s - # MaxDelay specifies maximum delay in ms between retries. Default is 300s - maxDelay: 5m - # Exponential determines whether delay is treated as exponential. - # With 30s delay and 3 retries: 30, 60, 120s - # Default is false - exponential: false - settings.monitoring: # enabled turns on monitoring of running processes - enabled: false + enabled: true # enables log monitoring - logs: false + logs: true # enables metrics monitoring - metrics: false + metrics: true + +# management: +# # Mode of management, the Elastic Agent support two modes of operation: +# # +# # local: The Elastic Agent will expect to find the inputs configuration in the local file. +# # +# # Default is local. +# mode: "local" + +# fleet: +# access_token: "" +# kibana: +# # kibana minimal configuration +# host: "localhost:5601" +# ssl.certificate_authorities: ["/etc/pki/root/ca.pem"] + +# # optional values +# #protocol: "https" +# #username: "elastic" +# #password: "changeme" +# #path: "" +# #ssl.verification_mode: full +# #ssl.supported_protocols: [TLSv1.0, TLSv1.1, TLSv1.2] +# #ssl.cipher_suites: [] +# #ssl.curve_types: [] + +# reporting: +# log: +# # format in which logs will be written, options are json or default. +# format: "default" +# fleet: +# # enables fleet reporter. fleet reporting can be enabled only in fleet management.mode. +# enabled: false + +# # Reporting threshold indicates how many events should be kept in-memory before reporting them to fleet. +# reporting_threshold: 10000 + +# # Frequency used to check the queue of events to be sent out to fleet. +# reporting_check_frequency_sec: 30 + +# # Allow fleet to reload his configuration locally on disk. +# # Notes: Only specific process configuration will be reloaded. +# reload: +# # enabled configure the Elastic Agent to reload or not the local configuration. +# # +# # Default is true +# enabled: true + +# # period define how frequent we should look for changes in the configuration. +# period: 10s + +# download: +# # source of the artifacts, requires elastic like structure and naming of the binaries +# # e.g /windows-x86.zip +# sourceURI: "https://artifacts.elastic.co/downloads/beats/" +# # path to the directory containing downloaded packages +# target_directory: "${path.data}/downloads" +# # timeout for downloading package +# timeout: 30s +# # file path to a public key used for verifying downloaded artifacts +# # if not file is present agent will try to load public key from elastic.co website. +# pgpfile: "${path.data}/elastic.pgp" +# # install_path describes the location of installed packages/programs. It is also used +# # for reading program specifications. +# install_path: "${path.data}/install" + +# process: +# # minimal port number for spawned processes +# min_port: 10000 +# # maximum port number for spawned processes +# max_port: 30000 +# # timeout for creating new processes. when process is not successfully created by this timeout +# # start operation is considered a failure +# spawn_timeout: 30s + +# retry: +# # Enabled determines whether retry is possible. Default is false. +# enabled: true +# # RetriesCount specifies number of retries. Default is 3. +# # Retry count of 1 means it will be retried one time after one failure. +# retriesCount: 3 +# # Delay specifies delay in ms between retries. Default is 30s +# delay: 30s +# # MaxDelay specifies maximum delay in ms between retries. Default is 300s +# maxDelay: 5m +# # Exponential determines whether delay is treated as exponential. +# # With 30s delay and 3 retries: 30, 60, 120s +# # Default is false +# exponential: false + +# Sets log level. The default log level is info. +# Available log levels are: error, warning, info, debug +#logging.level: trace + diff --git a/x-pack/elastic-agent/magefile.go b/x-pack/elastic-agent/magefile.go index 8168890998e..1816dd3b205 100644 --- a/x-pack/elastic-agent/magefile.go +++ b/x-pack/elastic-agent/magefile.go @@ -383,19 +383,12 @@ func configYML() error { // ConfigFileParams returns the parameters for generating OSS config. func ConfigFileParams() devtools.ConfigFileParams { - return devtools.ConfigFileParams{ - ShortParts: []string{ - devtools.XPackBeatDir("_meta/common.p1.yml"), - devtools.XPackBeatDir("_meta/common.p2.yml"), - }, - ReferenceParts: []string{ - devtools.XPackBeatDir("_meta/common.reference.p1.yml"), - devtools.XPackBeatDir("_meta/common.reference.p2.yml"), - }, - DockerParts: []string{ - devtools.XPackBeatDir("_meta/elastic-agent.docker.yml"), - }, - } + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, "_meta/config/*.tmpl") + p.Short.Template = "_meta/config/elastic-agent.yml.tmpl" + p.Reference.Template = "_meta/config/elastic-agent.reference.yml.tmpl" + p.Docker.Template = "_meta/config/elastic-agent.docker.yml.tmpl" + return p } // fieldDocs generates docs/fields.asciidoc containing all fields diff --git a/x-pack/elastic-agent/main_test.go b/x-pack/elastic-agent/main_test.go index cdced7ea677..79e2a9dc719 100644 --- a/x-pack/elastic-agent/main_test.go +++ b/x-pack/elastic-agent/main_test.go @@ -8,17 +8,21 @@ import ( "flag" "testing" - // Just using this a place holder. - "github.com/elastic/beats/v7/x-pack/filebeat/cmd" + "github.com/spf13/cobra" ) var systemTest *bool func init() { testing.Init() + + cmd := &cobra.Command{ + Use: "elastic-agent [subcommand]", + } + systemTest = flag.Bool("systemTest", false, "Set to true when running system tests") - cmd.RootCmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("systemTest")) - cmd.RootCmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("test.coverprofile")) + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("systemTest")) + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("test.coverprofile")) } // Test started when the test binary is started. Only calls main. diff --git a/x-pack/elastic-agent/pkg/agent/application/application.go b/x-pack/elastic-agent/pkg/agent/application/application.go index 78634055989..878809dbd08 100644 --- a/x-pack/elastic-agent/pkg/agent/application/application.go +++ b/x-pack/elastic-agent/pkg/agent/application/application.go @@ -26,28 +26,28 @@ func New(log *logger.Logger, pathConfigFile string) (Application, error) { // Load configuration from disk to understand in which mode of operation // we must start the elastic-agent, the mode of operation cannot be changed without restarting the // elastic-agent. - config, err := config.LoadYAML(pathConfigFile) + rawConfig, err := config.LoadYAML(pathConfigFile) if err != nil { return nil, err } - if err := InjectAgentConfig(config); err != nil { + if err := InjectAgentConfig(rawConfig); err != nil { return nil, err } - return createApplication(log, pathConfigFile, config) + return createApplication(log, pathConfigFile, rawConfig) } func createApplication( log *logger.Logger, pathConfigFile string, - config *config.Config, + rawConfig *config.Config, ) (Application, error) { warn.LogNotGA(log) log.Info("Detecting execution mode") c := localDefaultConfig() - err := config.Unpack(c) + err := rawConfig.Unpack(c) if err != nil { return nil, errors.New(err, "initiating application") } @@ -63,10 +63,10 @@ func createApplication( switch mgmt.Mode { case localMode: log.Info("Agent is managed locally") - return newLocal(ctx, log, pathConfigFile, config) + return newLocal(ctx, log, pathConfigFile, rawConfig) case fleetMode: log.Info("Agent is managed by Fleet") - return newManaged(ctx, log, config) + return newManaged(ctx, log, rawConfig) default: return nil, ErrInvalidMgmtMode } diff --git a/x-pack/elastic-agent/pkg/agent/application/config.go b/x-pack/elastic-agent/pkg/agent/application/config.go index edec6b4c7f8..bca9e615e17 100644 --- a/x-pack/elastic-agent/pkg/agent/application/config.go +++ b/x-pack/elastic-agent/pkg/agent/application/config.go @@ -8,7 +8,6 @@ import ( "fmt" "time" - "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/kibana" @@ -16,23 +15,19 @@ import ( logreporter "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/reporter/log" ) -// TODO(ph) correctly setup global path. -func fleetAgentConfigPath() string { - return info.AgentConfigFile -} - -// TODO(ph) correctly setup with global path. -func fleetActionStoreFile() string { - return info.AgentActionStoreFile -} - // Config define the configuration of the Agent. type Config struct { Management *config.Config `config:"management"` } func localDefaultConfig() *Config { - return &Config{} + localModeCfg, _ := config.NewConfigFrom(map[string]interface{}{ + "mode": "local", + }) + + return &Config{ + Management: localModeCfg, + } } type managementMode int diff --git a/x-pack/elastic-agent/pkg/agent/application/configuration_embed.go b/x-pack/elastic-agent/pkg/agent/application/configuration_embed.go index 8a064cf9268..b6bc827cc82 100644 --- a/x-pack/elastic-agent/pkg/agent/application/configuration_embed.go +++ b/x-pack/elastic-agent/pkg/agent/application/configuration_embed.go @@ -15,7 +15,7 @@ var DefaultAgentFleetConfig []byte func init() { // Packed File // _meta/elastic-agent.fleet.yml - unpacked := packer.MustUnpack("eJyMlk2Xo7oRhvf5GbPPDR/NnCE7CzdCjKFjfFtfmxwkeQS2ZHNigw05+e85wm53z6Rzz130xkh6q956qqr//eWfdnuu/7Y19encyr/Wens4//bDbLfn30Zrvvz9C5q8+B+//7k/9KyMsHhk5KL/7J3H3/q4Q8+5YbSaEDQTguUgLO94iEdO1lqRaM8p0pW9Gk7XJ5SZQW3AjpEnzSG2NYkMgv4gw7WWAfYUND0Pi+9oBFba+Iyy6sg3YFWTvBH2WePXfY/S9zcUBXtGK+PO1bTQ3JoT3wBPjGAngsjWRPnSvmoFG4Oy0qis6oRVkzvP6NrpNO67sLGHstKXGRjkoTI8Ae12A6CA2Kjk6bsgac+IMoLgXi2P31ECziKozEu7aPkttzbR3V6Eqhcwbvh8ZrFDWTkomu/4Bgy8BV4NXzWjuVcT3rCwmlYJGDlN/ZrmRo7ACJhOCpodgteuDl61DKtRkdJTNDcIpj1PQM+I3wkrtQjYLS+Yd8KmY03wlLQLjUZgVlbqVag6YatBhcV5u9TB6tB1cnHUj5gw3mx+P2pkG09lYHppvw0/xWbxidHSq0nRs+DbwGEcCnsdGKmmVXCr+WpE9zxBo6DWCn67x1R10uKdgvG43cy18BgxfU2iA4LVoILoJIJ07xiRMN3VQXrgtHC+eozmB06rHxyakZPSE2EeOZ/z7HxhtDqubNUomMar4P0d+Yij6gTBg6JrzW08ftSatTPQsODc8OB11qqJb0SIvZd2MRVZcXsD8k7AVxeXVyczSw3KgM/stWMj+Bene61o6RixIkRaWTzWhEef5fbRz3ttOt6CXoTrN1Y7OYKLtHjiJPLQ88NzvXa8HdZaBeYkEuDJw372d+YrARdF8pM7Vwc4QpCPIvA0J9dGhlXHxmgnRhBykk81rcxKu7qDg7zl9hUlaL8N55r1HKYeo97w4e6Fh4v2LV5xKD1Grid6O+/6dmJh3jGLp3t/nkRQNgqaQbQz5487v9R3kFk+8DBvBC169Gy828xITzL4dvPU8ZkUt9pluWGkmll/3NuASUK8qwnvWJB6NYl7OT65Wv4a56c51iSaFExPIkHfkwMYRVAaGRZf77meaxJ1gqQnNM+BtRYHfGYWjwjyQbZOOw2FxXuUvd0tjbwcNYJ+J0x8EWHuWLoWy8WluOciSPpUE98XGzB/RzDyBcmNbG/8SBdjEDmeLtLGO07L6d4LZ0abHxLGo1oedTEtLsVy8X84L0dOnB+Owcio8ae3epSpIydPs48swJMcZ+5mDmWId/NvAfcFvEZvb838zsw17uyjV+5eOR9HlYBBQjwyWnUieNJuBrMg7mVg9pzmLqcGQd7U5OrmsMvp5qGJ/6f3kkNuVJZHL/rOAIka5mZrAvacVkZaN/ccc+rIaXV0e0VaPPeF05UwnmRgWgFfe/RcGW5TX2RrfdsLjrHX3sX+eHd51CrLfb6+6c1vWWOkH+9EiHuVvLPGafGBhdg6NlWWd7O3v2gVM4+LHUpv+bidqFxvu3vLZy0IbsSh0DV99HbLN24PVG6G7xGMe7fDnD8IplbBWdP9ZhkxJ7cLbv38FkP5gwWxLw7rr2j5NsMqt0silJUXTsqOu7xGsHc9vt2ATrTgLEfQclqFnOD+D/OZwDy7OHSzae80LjPzd7Zp8KmWY7cTFJ8fsUDTI+hPCDotZWYe3zwfn3QFsWUUn1Ti2FxMxfKm+1HjpV344l6vijYXEURGHKrO/e/CIfY4zV0PO/2whtirZ+8e+m5evbPtuPj5jf7ONq1pdZzzfPesEXati/FRp2nmNrnocrc4oeXz+Ngfn7Pn2DfbDAzC4l5B04jl8fHty3/+8t8AAAD///ycVl0=") + unpacked := packer.MustUnpack("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") raw, ok := unpacked["_meta/elastic-agent.fleet.yml"] if !ok { // ensure we have something loaded. diff --git a/x-pack/elastic-agent/pkg/agent/application/emitter.go b/x-pack/elastic-agent/pkg/agent/application/emitter.go index 2279d78565d..249acdd213f 100644 --- a/x-pack/elastic-agent/pkg/agent/application/emitter.go +++ b/x-pack/elastic-agent/pkg/agent/application/emitter.go @@ -26,7 +26,11 @@ type configModifiers struct { Decorators []decoratorFunc } -func emitter(log *logger.Logger, router *router, modifiers *configModifiers, reloadables ...reloadable) emitterFunc { +type programsDispatcher interface { + Dispatch(id string, grpProg map[routingKey][]program.Program) error +} + +func emitter(log *logger.Logger, router programsDispatcher, modifiers *configModifiers, reloadables ...reloadable) emitterFunc { return func(c *config.Config) error { if err := InjectAgentConfig(c); err != nil { return err diff --git a/x-pack/elastic-agent/pkg/agent/application/enroll_cmd.go b/x-pack/elastic-agent/pkg/agent/application/enroll_cmd.go index 323937b080c..8a8624b1675 100644 --- a/x-pack/elastic-agent/pkg/agent/application/enroll_cmd.go +++ b/x-pack/elastic-agent/pkg/agent/application/enroll_cmd.go @@ -91,7 +91,7 @@ func NewEnrollCmd( store := storage.NewReplaceOnSuccessStore( configPath, DefaultAgentFleetConfig, - storage.NewEncryptedDiskStore(fleetAgentConfigPath(), []byte("")), + storage.NewEncryptedDiskStore(info.AgentConfigFile(), []byte("")), ) return NewEnrollCmdWithStore( diff --git a/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go b/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go index d59cc620e1a..e22b2ec9eb0 100644 --- a/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/enroll_cmd_test.go @@ -13,7 +13,6 @@ import ( "net/http" "net/http/httptest" "os" - "runtime" "strconv" "testing" @@ -44,10 +43,6 @@ func (m *mockStore) Save(in io.Reader) error { } func TestEnroll(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("Disabled under windows: https://github.com/elastic/beats/issues/16860") - } - log, _ := logger.New() t.Run("fail to save is propagated", withTLSServer( diff --git a/x-pack/elastic-agent/pkg/agent/application/filters/constraints_filter.go b/x-pack/elastic-agent/pkg/agent/application/filters/constraints_filter.go index 2cb92cffd97..9159199e794 100644 --- a/x-pack/elastic-agent/pkg/agent/application/filters/constraints_filter.go +++ b/x-pack/elastic-agent/pkg/agent/application/filters/constraints_filter.go @@ -229,7 +229,7 @@ func initVarStore(store *constraintVarStore) error { return err } - meta, err := agentInfo.ECSMetadata() + meta, err := agentInfo.ECSMetadataFlatMap() if err != nil { return errors.New(err, "failed to gather host metadata") } diff --git a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go index e4f021fdaad..ae6f2d21003 100644 --- a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go +++ b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go @@ -11,11 +11,33 @@ import ( "github.com/elastic/beats/v7/libbeat/common/backoff" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/scheduler" ) +// Default Configuration for the Fleet Gateway. +var defaultGatewaySettings = &fleetGatewaySettings{ + Duration: 30 * time.Second, + Jitter: 5 * time.Second, + Backoff: backoffSettings{ + Init: 1 * time.Second, + Max: 10 * time.Second, + }, +} + +type fleetGatewaySettings struct { + Duration time.Duration `config:"checkin_frequency"` + Jitter time.Duration `config:"jitter"` + Backoff backoffSettings `config:"backoff"` +} + +type backoffSettings struct { + Init time.Duration `config:"init"` + Max time.Duration `config:"max"` +} + type dispatcher interface { Dispatch(acker fleetAcker, actions ...action) error } @@ -52,27 +74,22 @@ type fleetGateway struct { acker fleetAcker } -type fleetGatewaySettings struct { - Duration time.Duration - Jitter time.Duration - Backoff backoffSettings -} - -type backoffSettings struct { - Init time.Duration - Max time.Duration -} - func newFleetGateway( ctx context.Context, log *logger.Logger, - settings *fleetGatewaySettings, + rawConfig *config.Config, agentInfo agentInfo, client clienter, d dispatcher, r fleetReporter, acker fleetAcker, ) (*fleetGateway, error) { + + settings := defaultGatewaySettings + if err := rawConfig.Unpack(settings); err != nil { + return nil, errors.New(err, "fail to read gateway configuration") + } + scheduler := scheduler.NewPeriodicJitter(settings.Duration, settings.Jitter) return newFleetGatewayWithScheduler( ctx, @@ -98,6 +115,9 @@ func newFleetGatewayWithScheduler( r fleetReporter, acker fleetAcker, ) (*fleetGateway, error) { + + // Backoff implementation doesn't support the using context as the shutdown mechanism. + // So we keep a done channel that will be closed when the current context is shutdown. done := make(chan struct{}) return &fleetGateway{ @@ -144,10 +164,8 @@ func (f *fleetGateway) worker() { } f.log.Debugf("FleetGateway is sleeping, next update in %s", f.settings.Duration) - case <-f.done: - return case <-f.bgContext.Done(): - f.Stop() + f.stop() return } } @@ -179,10 +197,8 @@ func (f *fleetGateway) execute(ctx context.Context) (*fleetapi.CheckinResponse, // get events ee, ack := f.reporter.Events() - var metaData map[string]interface{} - if m, err := metadata(); err == nil { - metaData = m - } else { + ecsMeta, err := metadata() + if err != nil { f.log.Error(errors.New("failed to load metadata", err)) } @@ -190,7 +206,7 @@ func (f *fleetGateway) execute(ctx context.Context) (*fleetapi.CheckinResponse, cmd := fleetapi.NewCheckinCmd(f.agentInfo, f.client) req := &fleetapi.CheckinRequest{ Events: ee, - Metadata: metaData, + Metadata: ecsMeta, } resp, err := cmd.Execute(ctx, req) @@ -213,7 +229,7 @@ func (f *fleetGateway) Start() { }(&f.wg) } -func (f *fleetGateway) Stop() { +func (f *fleetGateway) stop() { f.log.Info("Fleet gateway is stopping") defer f.scheduler.Stop() close(f.done) diff --git a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway_test.go b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway_test.go index 91cf687aa54..4bc48b8fd98 100644 --- a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway_test.go @@ -114,8 +114,10 @@ func withGateway(agentInfo agentInfo, settings *fleetGatewaySettings, fn withGat log, _ := logger.New() rep := getReporter(agentInfo, log, t) + ctx, cancel := context.WithCancel(context.Background()) + gateway, err := newFleetGatewayWithScheduler( - context.Background(), + ctx, log, settings, agentInfo, @@ -127,7 +129,7 @@ func withGateway(agentInfo agentInfo, settings *fleetGatewaySettings, fn withGat ) go gateway.Start() - defer gateway.Stop() + defer cancel() require.NoError(t, err) @@ -240,9 +242,10 @@ func TestFleetGateway(t *testing.T) { client := newTestingClient() dispatcher := newTestingDispatcher() + ctx, cancel := context.WithCancel(context.Background()) log, _ := logger.New() gateway, err := newFleetGatewayWithScheduler( - context.Background(), + ctx, log, settings, agentInfo, @@ -254,7 +257,7 @@ func TestFleetGateway(t *testing.T) { ) go gateway.Start() - defer gateway.Stop() + defer cancel() require.NoError(t, err) @@ -324,9 +327,10 @@ func TestFleetGateway(t *testing.T) { client := newTestingClient() dispatcher := newTestingDispatcher() + ctx, cancel := context.WithCancel(context.Background()) log, _ := logger.New() gateway, err := newFleetGatewayWithScheduler( - context.Background(), + ctx, log, &fleetGatewaySettings{ Duration: d, @@ -370,8 +374,9 @@ func TestFleetGateway(t *testing.T) { // 1. Gateway will check the API on boot. // 2. WaitTick() will block for 20 minutes. // 3. Stop will should unblock the wait. - gateway.Stop() + cancel() }) + } func TestRetriesOnFailures(t *testing.T) { diff --git a/x-pack/elastic-agent/pkg/agent/application/global_config.go b/x-pack/elastic-agent/pkg/agent/application/global_config.go index 44e9f2772ff..16d5f21639e 100644 --- a/x-pack/elastic-agent/pkg/agent/application/global_config.go +++ b/x-pack/elastic-agent/pkg/agent/application/global_config.go @@ -5,26 +5,15 @@ package application import ( - "os" - "path/filepath" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" ) -var ( - homePath string - dataPath string -) - -func init() { - homePath = retrieveExecutablePath() - dataPath = retrieveDataPath() -} - // InjectAgentConfig injects config to a provided configuration. func InjectAgentConfig(c *config.Config) error { - globalConfig := AgentGlobalConfig() + globalConfig := agentGlobalConfig() if err := c.Merge(globalConfig); err != nil { return errors.New("failed to inject agent global config", err, errors.TypeConfig) } @@ -32,29 +21,13 @@ func InjectAgentConfig(c *config.Config) error { return nil } -// AgentGlobalConfig gets global config used for resolution of variables inside configuration +// agentGlobalConfig gets global config used for resolution of variables inside configuration // such as ${path.data}. -func AgentGlobalConfig() map[string]interface{} { +func agentGlobalConfig() map[string]interface{} { return map[string]interface{}{ "path": map[string]interface{}{ - "data": dataPath, - "home": homePath, + "data": paths.Data(), + "home": paths.Home(), }, } } - -// retrieveExecutablePath returns a directory where binary lives -// Executable is not supported on nacl. -func retrieveExecutablePath() string { - execPath, err := os.Executable() - if err != nil { - panic(err) - } - - return filepath.Dir(execPath) -} - -// retrieveHomePath returns a home directory of current user -func retrieveDataPath() string { - return filepath.Join(retrieveExecutablePath(), "data") -} diff --git a/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go b/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go index c20f60972f9..9e0084bc6f1 100644 --- a/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go +++ b/x-pack/elastic-agent/pkg/agent/application/info/agent_id.go @@ -8,21 +8,23 @@ import ( "bytes" "fmt" "io" + "path/filepath" "github.com/gofrs/uuid" "gopkg.in/yaml.v2" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" ) -// AgentConfigFile is a name of file used to store agent information -const AgentConfigFile = "fleet.yml" +// defaultAgentConfigFile is a name of file used to store agent information +const defaultAgentConfigFile = "fleet.yml" const agentInfoKey = "agent_info" -// AgentActionStoreFile is the file that will contains the action that can be replayed after restart. -const AgentActionStoreFile = "action_store.yml" +// defaultAgentActionStoreFile is the file that will contains the action that can be replayed after restart. +const defaultAgentActionStoreFile = "action_store.yml" type persistentAgentInfo struct { ID string `json:"ID" yaml:"ID" config:"ID"` @@ -33,6 +35,16 @@ type ioStore interface { Load() (io.ReadCloser, error) } +// AgentConfigFile is a name of file used to store agent information +func AgentConfigFile() string { + return filepath.Join(paths.Home(), defaultAgentConfigFile) +} + +// AgentActionStoreFile is the file that will contains the action that can be replayed after restart. +func AgentActionStoreFile() string { + return filepath.Join(paths.Home(), defaultAgentActionStoreFile) +} + func generateAgentID() (string, error) { uid, err := uuid.NewV4() if err != nil { @@ -43,7 +55,8 @@ func generateAgentID() (string, error) { } func loadAgentInfo(forceUpdate bool) (*persistentAgentInfo, error) { - s := storage.NewEncryptedDiskStore(AgentConfigFile, []byte("")) + agentConfigFile := AgentConfigFile() + s := storage.NewEncryptedDiskStore(agentConfigFile, []byte("")) agentinfo, err := getInfoFromStore(s) if err != nil { @@ -67,6 +80,7 @@ func loadAgentInfo(forceUpdate bool) (*persistentAgentInfo, error) { } func getInfoFromStore(s ioStore) (*persistentAgentInfo, error) { + agentConfigFile := AgentConfigFile() reader, err := s.Load() if err != nil { return nil, err @@ -75,9 +89,9 @@ func getInfoFromStore(s ioStore) (*persistentAgentInfo, error) { cfg, err := config.NewConfigFrom(reader) if err != nil { return nil, errors.New(err, - fmt.Sprintf("fail to read configuration %s for the agent", AgentConfigFile), + fmt.Sprintf("fail to read configuration %s for the agent", agentConfigFile), errors.TypeFilesystem, - errors.M(errors.MetaKeyPath, AgentConfigFile)) + errors.M(errors.MetaKeyPath, agentConfigFile)) } if err := reader.Close(); err != nil { @@ -110,6 +124,7 @@ func getInfoFromStore(s ioStore) (*persistentAgentInfo, error) { } func updateAgentInfo(s ioStore, agentInfo *persistentAgentInfo) error { + agentConfigFile := AgentConfigFile() reader, err := s.Load() if err != nil { return err @@ -117,9 +132,9 @@ func updateAgentInfo(s ioStore, agentInfo *persistentAgentInfo) error { cfg, err := config.NewConfigFrom(reader) if err != nil { - return errors.New(err, fmt.Sprintf("fail to read configuration %s for the agent", AgentConfigFile), + return errors.New(err, fmt.Sprintf("fail to read configuration %s for the agent", agentConfigFile), errors.TypeFilesystem, - errors.M(errors.MetaKeyPath, AgentConfigFile)) + errors.M(errors.MetaKeyPath, agentConfigFile)) } if err := reader.Close(); err != nil { diff --git a/x-pack/elastic-agent/pkg/agent/application/info/agent_metadata.go b/x-pack/elastic-agent/pkg/agent/application/info/agent_metadata.go index 79371e76600..424053276b0 100644 --- a/x-pack/elastic-agent/pkg/agent/application/info/agent_metadata.go +++ b/x-pack/elastic-agent/pkg/agent/application/info/agent_metadata.go @@ -15,6 +15,62 @@ import ( "github.com/elastic/go-sysinfo/types" ) +// ECSMeta is a collection of agent related metadata in ECS compliant object form. +type ECSMeta struct { + Elastic *ElasticECSMeta `json:"elastic"` + Host *HostECSMeta `json:"host"` + OS *SystemECSMeta `json:"os"` +} + +// ElasticECSMeta is a collection of elastic vendor metadata in ECS compliant object form. +type ElasticECSMeta struct { + Agent *AgentECSMeta `json:"agent"` +} + +// AgentECSMeta is a collection of agent metadata in ECS compliant object form. +type AgentECSMeta struct { + // ID is a generated (in standalone) or assigned (in fleet) agent identifier. + ID string `json:"id"` + // Version specifies current version of an agent. + Version string `json:"version"` +} + +// SystemECSMeta is a collection of operating system metadata in ECS compliant object form. +type SystemECSMeta struct { + // Family defines a family of underlying operating system (e.g. redhat, debian, freebsd, windows). + Family string `json:"family"` + // Kernel specifies current version of a kernel in a semver format. + Kernel string `json:"kernel"` + // Platform specifies platform agent is running on (e.g. centos, ubuntu, windows). + Platform string `json:"platform"` + // Version specifies version of underlying operating system (e.g. 10.12.6). + Version string `json:"version"` + // Name is a operating system name. + // Currently we just normalize the name (i.e. macOS, Windows, Linux). See https://www.elastic.co/guide/en/ecs/current/ecs-html + Name string `json:"name"` + // Full is an operating system name, including the version or code name. + FullName string `json:"full"` +} + +// HostECSMeta is a collection of host metadata in ECS compliant object form. +type HostECSMeta struct { + // Arch defines architecture of a host (e.g. x86_64, arm, ppc, mips). + Arch string `json:"architecture"` + // Hostname specifies hostname of the host. + Hostname string `json:"hostname"` + // Name specifies hostname of the host. + Name string `json:"name"` + // ID is a Unique host id. + // As hostname is not always unique, use values that are meaningful in your environment. + ID string `json:"id"` + // IP is Host ip addresses. + // Note: this field should contain an array of values. + IP []string `json:"ip"` + // Mac is Host mac addresses. + // Note: this field should contain an array of values. + MAC []string `json:"mac"` +} + // List of variables available to be used in constraint definitions. const ( // `agent.id` is a generated (in standalone) or assigned (in fleet) agent identifier. @@ -54,19 +110,57 @@ const ( ) // ECSMetadata returns an agent ECS compliant metadata. -func (i *AgentInfo) ECSMetadata() (map[string]interface{}, error) { +func (i *AgentInfo) ECSMetadata() (*ECSMeta, error) { hostname, err := os.Hostname() if err != nil { return nil, err } - // TODO: remove these values when kibana migrates to ECS - meta := map[string]interface{}{ - "platform": runtime.GOOS, - "version": release.Version(), - "host": hostname, + sysInfo, err := sysinfo.Host() + if err != nil { + return nil, err + } + + info := sysInfo.Info() + + return &ECSMeta{ + Elastic: &ElasticECSMeta{ + Agent: &AgentECSMeta{ + ID: i.agentID, + Version: release.Version(), + }, + }, + Host: &HostECSMeta{ + Arch: info.Architecture, + Hostname: hostname, + Name: hostname, + ID: info.UniqueID, + IP: info.IPs, + MAC: info.MACs, + }, + + // Operating system + OS: &SystemECSMeta{ + Family: runtime.GOOS, + Kernel: info.KernelVersion, + Platform: info.OS.Family, + Version: info.OS.Version, + Name: info.OS.Name, + FullName: getFullOSName(info), + }, + }, nil +} + +// ECSMetadataFlatMap returns an agent ECS compliant metadata in a form of flattened map. +func (i *AgentInfo) ECSMetadataFlatMap() (map[string]interface{}, error) { + hostname, err := os.Hostname() + if err != nil { + return nil, err } + // TODO: remove these values when kibana migrates to ECS + meta := make(map[string]interface{}) + sysInfo, err := sysinfo.Host() if err != nil { return nil, err diff --git a/x-pack/elastic-agent/pkg/agent/application/introspect_config_cmd.go b/x-pack/elastic-agent/pkg/agent/application/introspect_config_cmd.go new file mode 100644 index 00000000000..f5ab634b84d --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/application/introspect_config_cmd.go @@ -0,0 +1,131 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package application + +import ( + "fmt" + + yaml "gopkg.in/yaml.v2" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" +) + +// IntrospectConfigCmd is an introspect subcommand that shows configurations of the agent. +type IntrospectConfigCmd struct { + cfgPath string +} + +// NewIntrospectConfigCmd creates a new introspect command. +func NewIntrospectConfigCmd(configPath string, +) (*IntrospectConfigCmd, error) { + return &IntrospectConfigCmd{ + cfgPath: configPath, + }, nil +} + +// Execute introspects agent configuration. +func (c *IntrospectConfigCmd) Execute() error { + return c.introspectConfig() +} + +func (c *IntrospectConfigCmd) introspectConfig() error { + cfg, err := loadConfig(c.cfgPath) + if err != nil { + return err + } + + isLocal, err := isLocalMode(cfg) + if err != nil { + return err + } + + if isLocal { + return printConfig(cfg) + } + + fleetConfig, err := loadFleetConfig(cfg) + if err != nil { + return err + } else if fleetConfig == nil { + return errors.New("no fleet config retrieved yet") + } + + return printMapStringConfig(fleetConfig) +} + +func loadConfig(configPath string) (*config.Config, error) { + rawConfig, err := config.LoadYAML(configPath) + if err != nil { + return nil, err + } + + if err := InjectAgentConfig(rawConfig); err != nil { + return nil, err + } + + return rawConfig, nil +} + +func loadFleetConfig(cfg *config.Config) (map[string]interface{}, error) { + log, err := newErrorLogger() + if err != nil { + return nil, err + } + + as, err := newActionStore(log, storage.NewDiskStore(info.AgentActionStoreFile())) + if err != nil { + return nil, err + } + + for _, c := range as.Actions() { + cfgChange, ok := c.(*fleetapi.ActionConfigChange) + if !ok { + continue + } + + fmt.Println("Action ID:", cfgChange.ID()) + return cfgChange.Config, nil + } + return nil, nil +} + +func isLocalMode(rawConfig *config.Config) (bool, error) { + c := localDefaultConfig() + if err := rawConfig.Unpack(&c); err != nil { + return false, errors.New(err, "initiating application") + } + + managementConfig := struct { + Mode string `config:"mode" yaml:"mode"` + }{} + + if err := c.Management.Unpack(&managementConfig); err != nil { + return false, errors.New(err, "initiating application") + } + return managementConfig.Mode == "local", nil +} + +func printMapStringConfig(mapStr map[string]interface{}) error { + data, err := yaml.Marshal(mapStr) + if err != nil { + return errors.New(err, "could not marshal to YAML") + } + + fmt.Println(string(data)) + return nil +} + +func printConfig(cfg *config.Config) error { + mapStr, err := cfg.ToMapStr() + if err != nil { + return err + } + + return printMapStringConfig(mapStr) +} diff --git a/x-pack/elastic-agent/pkg/agent/application/introspect_output_cmd.go b/x-pack/elastic-agent/pkg/agent/application/introspect_output_cmd.go new file mode 100644 index 00000000000..26eb424fe97 --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/application/introspect_output_cmd.go @@ -0,0 +1,211 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package application + +import ( + "fmt" + + "github.com/urso/ecslog" + "github.com/urso/ecslog/backend" + "github.com/urso/ecslog/backend/appender" + "github.com/urso/ecslog/backend/layout" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/filters" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/noop" +) + +// IntrospectOutputCmd is an introspect subcommand that shows configurations of the agent. +type IntrospectOutputCmd struct { + cfgPath string + output string + program string +} + +// NewIntrospectOutputCmd creates a new introspect command. +func NewIntrospectOutputCmd(configPath, output, program string) (*IntrospectOutputCmd, error) { + return &IntrospectOutputCmd{ + cfgPath: configPath, + output: output, + program: program, + }, nil +} + +// Execute tries to enroll the agent into Fleet. +func (c *IntrospectOutputCmd) Execute() error { + if c.output == "" { + return c.introspectOutputs() + } + + return c.introspectOutput() +} + +func (c *IntrospectOutputCmd) introspectOutputs() error { + cfg, err := loadConfig(c.cfgPath) + if err != nil { + return err + } + + isLocal, err := isLocalMode(cfg) + if err != nil { + return err + } + + l, err := newErrorLogger() + if err != nil { + return err + } + + if isLocal { + return listOutputsFromConfig(l, cfg) + } + + fleetConfig, err := loadFleetConfig(cfg) + if err != nil { + return err + } else if fleetConfig == nil { + return errors.New("no fleet config retrieved yet") + } + + return listOutputsFromMap(l, fleetConfig) +} + +func listOutputsFromConfig(log *logger.Logger, cfg *config.Config) error { + programsGroup, err := getProgramsFromConfig(log, cfg) + if err != nil { + return err + + } + + for k := range programsGroup { + fmt.Println(k) + } + + return nil +} + +func listOutputsFromMap(log *logger.Logger, cfg map[string]interface{}) error { + c, err := config.NewConfigFrom(cfg) + if err != nil { + return err + } + + return listOutputsFromConfig(log, c) +} + +func (c *IntrospectOutputCmd) introspectOutput() error { + cfg, err := loadConfig(c.cfgPath) + if err != nil { + return err + } + + l, err := newErrorLogger() + if err != nil { + return err + } + + isLocal, err := isLocalMode(cfg) + if err != nil { + return err + } + + if isLocal { + return printOutputFromConfig(l, c.output, c.program, cfg) + } + + fleetConfig, err := loadFleetConfig(cfg) + if err != nil { + return err + } else if fleetConfig == nil { + return errors.New("no fleet config retrieved yet") + } + + return printOutputFromMap(l, c.output, c.program, fleetConfig) +} + +func printOutputFromConfig(log *logger.Logger, output, programName string, cfg *config.Config) error { + programsGroup, err := getProgramsFromConfig(log, cfg) + if err != nil { + return err + + } + + for k, programs := range programsGroup { + if k != output { + continue + } + + var programFound bool + for _, p := range programs { + if programName != "" && programName != p.Spec.Cmd { + continue + } + + programFound = true + fmt.Printf("[%s] %s:\n", k, p.Spec.Cmd) + printMapStringConfig(p.Configuration()) + fmt.Println("---") + } + + if !programFound { + fmt.Printf("program '%s' is not recognized within output '%s', try running `elastic-agent introspect output` to find available outputs.\n", + programName, + output) + } + return nil + } + + fmt.Printf("output '%s' is not recognized, try running `elastic-agent introspect output` to find available outputs.\n", output) + + return nil +} + +func printOutputFromMap(log *logger.Logger, output, programName string, cfg map[string]interface{}) error { + c, err := config.NewConfigFrom(cfg) + if err != nil { + return err + } + + return printOutputFromConfig(log, output, programName, c) +} + +func getProgramsFromConfig(log *logger.Logger, cfg *config.Config) (map[string][]program.Program, error) { + monitor := noop.NewMonitor() + router := &inmemRouter{} + emit := emitter( + log, + router, + &configModifiers{ + Decorators: []decoratorFunc{injectMonitoring}, + Filters: []filterFunc{filters.ConstraintFilter}, + }, + monitor, + ) + + if err := emit(cfg); err != nil { + return nil, err + } + return router.programs, nil +} + +type inmemRouter struct { + programs map[string][]program.Program +} + +func (r *inmemRouter) Dispatch(id string, grpProg map[routingKey][]program.Program) error { + r.programs = grpProg + return nil +} + +func newErrorLogger() (*logger.Logger, error) { + backend, err := appender.Console(backend.Error, layout.Text(true)) + if err != nil { + return nil, err + } + return ecslog.New(backend), nil +} diff --git a/x-pack/elastic-agent/pkg/agent/application/local_meta.go b/x-pack/elastic-agent/pkg/agent/application/local_meta.go index 3456075baa9..540f74ad924 100644 --- a/x-pack/elastic-agent/pkg/agent/application/local_meta.go +++ b/x-pack/elastic-agent/pkg/agent/application/local_meta.go @@ -9,7 +9,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" ) -func metadata() (map[string]interface{}, error) { +func metadata() (*info.ECSMeta, error) { agentInfo, err := info.NewAgentInfo() if err != nil { return nil, err diff --git a/x-pack/elastic-agent/pkg/agent/application/local_mode.go b/x-pack/elastic-agent/pkg/agent/application/local_mode.go index 65a4927a55d..4ad8f27c7f0 100644 --- a/x-pack/elastic-agent/pkg/agent/application/local_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/local_mode.go @@ -55,7 +55,7 @@ func newLocal( ) (*Local, error) { var err error if log == nil { - log, err = logger.New() + log, err = logger.NewFromConfig(rawConfig) if err != nil { return nil, err } diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go index 6431ec03975..269287446db 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go @@ -11,8 +11,6 @@ import ( "net/http" "net/url" - "time" - "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/filters" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" @@ -26,15 +24,6 @@ import ( logreporter "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/reporter/log" ) -var gatewaySettings = &fleetGatewaySettings{ - Duration: 2 * time.Second, - Jitter: 1 * time.Second, - Backoff: backoffSettings{ - Init: 1 * time.Second, - Max: 10 * time.Second, - }, -} - type apiClient interface { Send( method string, @@ -68,7 +57,7 @@ func newManaged( return nil, err } - path := fleetAgentConfigPath() + path := info.AgentConfigFile() // TODO(ph): Define the encryption password. store := storage.NewEncryptedDiskStore(path, []byte("")) @@ -87,6 +76,7 @@ func newManaged( errors.M(errors.MetaKeyPath, path)) } + // merge local configuration and configuration persisted from fleet. rawConfig.Merge(config) cfg := defaultFleetAgentConfig() @@ -97,6 +87,15 @@ func newManaged( errors.M(errors.MetaKeyPath, path)) } + // Extract only management related configuration. + managementCfg := &Config{} + if err := rawConfig.Unpack(managementCfg); err != nil { + return nil, errors.New(err, + fmt.Sprintf("fail to unpack configuration from %s", path), + errors.TypeFilesystem, + errors.M(errors.MetaKeyPath, path)) + } + client, err := fleetapi.NewAuthWithConfig(log, cfg.API.AccessAPIKey, cfg.API.Kibana) if err != nil { return nil, errors.New(err, @@ -129,7 +128,15 @@ func newManaged( return nil, errors.New(err, "fail to initialize pipeline router") } - emit := emitter(log, router, &configModifiers{Decorators: []decoratorFunc{injectMonitoring}, Filters: []filterFunc{filters.ConstraintFilter}}, monitor) + emit := emitter( + log, + router, + &configModifiers{ + Decorators: []decoratorFunc{injectMonitoring}, + Filters: []filterFunc{filters.ConstraintFilter}, + }, + monitor, + ) acker, err := newActionAcker(log, agentInfo, client) if err != nil { return nil, err @@ -138,9 +145,9 @@ func newManaged( batchedAcker := newLazyAcker(acker) // Create the action store that will persist the last good policy change on disk. - actionStore, err := newActionStore(log, storage.NewDiskStore(fleetActionStoreFile())) + actionStore, err := newActionStore(log, storage.NewDiskStore(info.AgentActionStoreFile())) if err != nil { - return nil, errors.New(err, fmt.Sprintf("fail to read action store '%s'", fleetActionStoreFile())) + return nil, errors.New(err, fmt.Sprintf("fail to read action store '%s'", info.AgentActionStoreFile())) } actionAcker := newActionStoreAcker(batchedAcker, actionStore) @@ -175,7 +182,7 @@ func newManaged( gateway, err := newFleetGateway( managedApplication.bgContext, log, - gatewaySettings, + managementCfg.Management, agentInfo, client, actionDispatcher, @@ -201,7 +208,6 @@ func (m *Managed) Start() error { func (m *Managed) Stop() error { defer m.log.Info("Agent is stopped") m.cancelCtxFn() - m.gateway.Stop() return nil } diff --git a/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go b/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go index bd0b240c65d..89328dd05b2 100644 --- a/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go +++ b/x-pack/elastic-agent/pkg/agent/application/monitoring_decorator.go @@ -19,10 +19,11 @@ const ( monitoringOutputFormatKey = "outputs.%s" outputKey = "output" - enabledKey = "settings.monitoring.enabled" - outputsKey = "outputs" - elasticsearchKey = "elasticsearch" - typeKey = "type" + enabledKey = "settings.monitoring.enabled" + outputsKey = "outputs" + elasticsearchKey = "elasticsearch" + typeKey = "type" + defaultOutputName = "default" ) func injectMonitoring(outputGroup string, rootAst *transpiler.AST, programsToRun []program.Program) ([]program.Program, error) { @@ -40,17 +41,17 @@ func injectMonitoring(outputGroup string, rootAst *transpiler.AST, programsToRun config[enabledKey] = false } else { // get monitoring output name to be used + monitoringOutputName := defaultOutputName useOutputNode, found := transpiler.Lookup(rootAst, monitoringUseOutputKey) - if !found { - return programsToRun, nil - } + if found { - monitoringOutputNameKey, ok := useOutputNode.Value().(*transpiler.StrVal) - if !ok { - return programsToRun, nil - } + monitoringOutputNameKey, ok := useOutputNode.Value().(*transpiler.StrVal) + if !ok { + return programsToRun, nil + } - monitoringOutputName := monitoringOutputNameKey.String() + monitoringOutputName = monitoringOutputNameKey.String() + } ast := rootAst.Clone() if err := getMonitoringRule(monitoringOutputName).Apply(ast); err != nil { diff --git a/x-pack/elastic-agent/pkg/agent/application/paths/paths.go b/x-pack/elastic-agent/pkg/agent/application/paths/paths.go new file mode 100644 index 00000000000..a45000b40ae --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/application/paths/paths.go @@ -0,0 +1,45 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package paths + +import ( + "flag" + "os" + "path/filepath" +) + +var ( + homePath string + dataPath string +) + +func init() { + exePath := retrieveExecutablePath() + + fs := flag.CommandLine + fs.StringVar(&homePath, "path.home", exePath, "Agent root path") + fs.StringVar(&dataPath, "path.data", filepath.Join(exePath, "data"), "Data path contains Agent managed binaries") +} + +// Home returns a directory where binary lives +// Executable is not supported on nacl. +func Home() string { + return homePath +} + +// Data returns a home directory of current user +func Data() string { + return dataPath +} + +func retrieveExecutablePath() string { + + execPath, err := os.Executable() + if err != nil { + panic(err) + } + + return filepath.Dir(execPath) +} diff --git a/x-pack/elastic-agent/pkg/agent/application/stream.go b/x-pack/elastic-agent/pkg/agent/application/stream.go index 1e8a1f10048..5d8880a9e07 100644 --- a/x-pack/elastic-agent/pkg/agent/application/stream.go +++ b/x-pack/elastic-agent/pkg/agent/application/stream.go @@ -72,7 +72,7 @@ func streamFactory(ctx context.Context, cfg *config.Config, client sender, r rep } func newOperator(ctx context.Context, log *logger.Logger, id routingKey, config *config.Config, r reporter, m monitoring.Monitor) (*operation.Operator, error) { - operatorConfig := &operatorCfg.Config{} + operatorConfig := operatorCfg.DefaultConfig() if err := config.Unpack(&operatorConfig); err != nil { return nil, err } diff --git a/x-pack/elastic-agent/pkg/agent/cmd/common.go b/x-pack/elastic-agent/pkg/agent/cmd/common.go index 0189f8e408d..54b51202ef5 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/common.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/common.go @@ -5,30 +5,30 @@ package cmd import ( + "flag" "fmt" "os" "path/filepath" "github.com/spf13/cobra" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/basecmd" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/cli" ) -var defaultConfig = "elastic-agent.yml" +const defaultConfig = "elastic-agent.yml" type globalFlags struct { - PathConfigFile string PathConfig string - PathData string - PathHome string - PathLogs string + PathConfigFile string FlagStrictPerms bool } +// Config returns path which identifies configuration file. func (f *globalFlags) Config() string { - if len(f.PathConfigFile) == 0 { - return filepath.Join(f.PathHome, defaultConfig) + if len(f.PathConfigFile) == 0 || f.PathConfigFile == defaultConfig { + return filepath.Join(paths.Home(), defaultConfig) } return f.PathConfigFile } @@ -50,17 +50,18 @@ func NewCommandWithArgs(args []string, streams *cli.IOStreams) *cobra.Command { flags := &globalFlags{} + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("path.home")) + cmd.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("path.data")) + cmd.PersistentFlags().StringVarP(&flags.PathConfigFile, "", "c", defaultConfig, fmt.Sprintf(`Configuration file, relative to path.config (default "%s")`, defaultConfig)) - cmd.PersistentFlags().StringVarP(&flags.PathHome, "path.home", "", "", "Home path") cmd.PersistentFlags().StringVarP(&flags.PathConfig, "path.config", "", "${path.home}", "Configuration path") - cmd.PersistentFlags().StringVarP(&flags.PathData, "path.data", "", "${path.home}/data", "Data path") - cmd.PersistentFlags().StringVarP(&flags.PathLogs, "path.logs", "", "${path.home}/logs", "Logs path") cmd.PersistentFlags().BoolVarP(&flags.FlagStrictPerms, "strict.perms", "", true, "Strict permission checking on config files") // Add version. cmd.AddCommand(basecmd.NewDefaultCommandsWithArgs(args, streams)...) cmd.AddCommand(newRunCommandWithArgs(flags, args, streams)) cmd.AddCommand(newEnrollCommandWithArgs(flags, args, streams)) + cmd.AddCommand(newIntrospectCommandWithArgs(flags, args, streams)) return cmd } diff --git a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go index a2a7ee48d22..abc5efb3b90 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/enroll.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/enroll.go @@ -46,13 +46,13 @@ func newEnrollCommandWithArgs(flags *globalFlags, _ []string, streams *cli.IOStr func enroll(streams *cli.IOStreams, cmd *cobra.Command, flags *globalFlags, args []string) error { warn.PrintNotGA(streams.Out) - - config, err := config.LoadYAML(flags.PathConfigFile) + pathConfigFile := flags.Config() + config, err := config.LoadYAML(pathConfigFile) if err != nil { return errors.New(err, - fmt.Sprintf("could not read configuration file %s", flags.PathConfigFile), + fmt.Sprintf("could not read configuration file %s", pathConfigFile), errors.TypeFilesystem, - errors.M(errors.MetaKeyPath, flags.PathConfigFile)) + errors.M(errors.MetaKeyPath, pathConfigFile)) } force, _ := cmd.Flags().GetBool("force") @@ -95,7 +95,7 @@ func enroll(streams *cli.IOStreams, cmd *cobra.Command, flags *globalFlags, args c, err := application.NewEnrollCmd( logger, &options, - flags.PathConfigFile, + pathConfigFile, ) if err != nil { diff --git a/x-pack/elastic-agent/pkg/agent/cmd/introspect.go b/x-pack/elastic-agent/pkg/agent/cmd/introspect.go new file mode 100644 index 00000000000..f6cb40e1894 --- /dev/null +++ b/x-pack/elastic-agent/pkg/agent/cmd/introspect.go @@ -0,0 +1,69 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/cli" +) + +func newIntrospectCommandWithArgs(flags *globalFlags, s []string, streams *cli.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "inspect", + Short: "Shows configuration of the agent", + Long: "Shows current configuration of the agent", + Args: cobra.ExactArgs(0), + Run: func(c *cobra.Command, args []string) { + command, err := application.NewIntrospectConfigCmd(flags.Config()) + if err != nil { + fmt.Fprintf(streams.Err, "%v\n", err) + os.Exit(1) + } + + if err := command.Execute(); err != nil { + fmt.Fprintf(streams.Err, "%v\n", err) + os.Exit(1) + } + }, + } + + cmd.AddCommand(newIntrospectOutputCommandWithArgs(flags, s, streams)) + + return cmd +} + +func newIntrospectOutputCommandWithArgs(flags *globalFlags, _ []string, streams *cli.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "output", + Short: "Displays configuration generated for output", + Long: "Displays configuration generated for output.\nIf no output is specified list of output is displayed", + Args: cobra.MaximumNArgs(2), + Run: func(c *cobra.Command, args []string) { + outName, _ := c.Flags().GetString("output") + program, _ := c.Flags().GetString("program") + + command, err := application.NewIntrospectOutputCmd(flags.Config(), outName, program) + if err != nil { + fmt.Fprintf(streams.Err, "%v\n", err) + os.Exit(1) + } + + if err := command.Execute(); err != nil { + fmt.Fprintf(streams.Err, "%v\n", err) + os.Exit(1) + } + }, + } + + cmd.Flags().StringP("output", "o", "", "name of the output to be introspected") + cmd.Flags().StringP("program", "p", "", "type of program to introspect, needs to be combined with output. e.g filebeat") + + return cmd +} diff --git a/x-pack/elastic-agent/pkg/agent/cmd/run.go b/x-pack/elastic-agent/pkg/agent/cmd/run.go index f0196ba7875..db199e2b47d 100644 --- a/x-pack/elastic-agent/pkg/agent/cmd/run.go +++ b/x-pack/elastic-agent/pkg/agent/cmd/run.go @@ -33,12 +33,13 @@ func newRunCommandWithArgs(flags *globalFlags, _ []string, streams *cli.IOStream } func run(flags *globalFlags, streams *cli.IOStreams) error { - config, err := config.LoadYAML(flags.PathConfigFile) + pathConfigFile := flags.Config() + config, err := config.LoadYAML(pathConfigFile) if err != nil { return errors.New(err, - fmt.Sprintf("could not read configuration file %s", flags.PathConfigFile), + fmt.Sprintf("could not read configuration file %s", pathConfigFile), errors.TypeFilesystem, - errors.M(errors.MetaKeyPath, flags.PathConfigFile)) + errors.M(errors.MetaKeyPath, pathConfigFile)) } logger, err := logger.NewFromConfig(config) @@ -46,7 +47,7 @@ func run(flags *globalFlags, streams *cli.IOStreams) error { return err } - app, err := application.New(logger, flags.PathConfigFile) + app, err := application.New(logger, pathConfigFile) if err != nil { return err } diff --git a/x-pack/elastic-agent/pkg/agent/operation/config/config.go b/x-pack/elastic-agent/pkg/agent/operation/config/config.go index f1c15df7007..d96f4c5125e 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/config/config.go +++ b/x-pack/elastic-agent/pkg/agent/operation/config/config.go @@ -6,6 +6,7 @@ package config import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/process" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/retry" ) @@ -16,4 +17,15 @@ type Config struct { RetryConfig *retry.Config `yaml:"retry" config:"retry"` DownloadConfig *artifact.Config `yaml:"download" config:"download"` + LoggingConfig *logger.Config `yaml:"logging,omitempty" config:"logging,omitempty"` +} + +// DefaultConfig creates a config with pre-set default values. +func DefaultConfig() *Config { + return &Config{ + ProcessConfig: process.DefaultConfig(), + RetryConfig: retry.DefaultConfig(), + DownloadConfig: artifact.DefaultConfig(), + LoggingConfig: logger.DefaultLoggingConfig(), + } } diff --git a/x-pack/elastic-agent/pkg/agent/operation/operator.go b/x-pack/elastic-agent/pkg/agent/operation/operator.go index 4a2b2208ee8..89734e38fe7 100644 --- a/x-pack/elastic-agent/pkg/agent/operation/operator.go +++ b/x-pack/elastic-agent/pkg/agent/operation/operator.go @@ -10,7 +10,6 @@ import ( "os" "strings" "sync" - "time" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configrequest" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" @@ -23,7 +22,6 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/app" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/app/monitoring" - "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/retry" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/state" rconfig "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/remoteconfig/grpc" ) @@ -65,7 +63,7 @@ func NewOperator( eventProcessor callbackHooks, monitor monitoring.Monitor) (*Operator, error) { - operatorConfig := defaultOperatorConfig() + operatorConfig := operatorCfg.DefaultConfig() if err := config.Unpack(&operatorConfig); err != nil { return nil, err } @@ -99,18 +97,6 @@ func NewOperator( return operator, nil } -func defaultOperatorConfig() *operatorCfg.Config { - return &operatorCfg.Config{ - RetryConfig: &retry.Config{ - Enabled: false, - RetriesCount: 0, - Delay: 30 * time.Second, - MaxDelay: 5 * time.Minute, - Exponential: false, - }, - } -} - // State describes the current state of the system. // Reports all known beats and theirs states. Whether they are running // or not, and if they are information about process is also present. @@ -253,7 +239,20 @@ func (o *Operator) getApp(p Descriptor) (Application, error) { return nil, fmt.Errorf("descriptor is not an app.Specifier") } - a, err := app.NewApplication(o.bgContext, p.ID(), p.BinaryName(), o.pipelineID, specifier, factory, o.config, o.logger, o.eventProcessor.OnFailing, o.monitor) + // TODO: (michal) join args into more compact options version + a, err := app.NewApplication( + o.bgContext, + p.ID(), + p.BinaryName(), + o.pipelineID, + o.config.LoggingConfig.Level.String(), + specifier, + factory, + o.config, + o.logger, + o.eventProcessor.OnFailing, + o.monitor) + if err != nil { return nil, err } diff --git a/x-pack/elastic-agent/pkg/agent/program/supported.go b/x-pack/elastic-agent/pkg/agent/program/supported.go index fe62d0afc28..ca70abd8771 100644 --- a/x-pack/elastic-agent/pkg/agent/program/supported.go +++ b/x-pack/elastic-agent/pkg/agent/program/supported.go @@ -19,7 +19,7 @@ func init() { // Packed Files // spec/filebeat.yml // spec/metricbeat.yml - unpacked := packer.MustUnpack("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") + unpacked := packer.MustUnpack("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") SupportedMap = make(map[string]bool) for f, v := range unpacked { diff --git a/x-pack/elastic-agent/pkg/agent/warn/warn.go b/x-pack/elastic-agent/pkg/agent/warn/warn.go index d9667f6f284..c3b97079aa1 100644 --- a/x-pack/elastic-agent/pkg/agent/warn/warn.go +++ b/x-pack/elastic-agent/pkg/agent/warn/warn.go @@ -11,7 +11,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" ) -const message = "The Elastic Agent is currently in Alpha and should not be used in production" +const message = "The Elastic Agent is currently in Experimental and should not be used in production" // LogNotGA warns the users in the log that the Elastic Agent is not GA. func LogNotGA(log *logger.Logger) { diff --git a/x-pack/elastic-agent/pkg/artifact/config.go b/x-pack/elastic-agent/pkg/artifact/config.go index 406677cb8aa..aefe35c26e1 100644 --- a/x-pack/elastic-agent/pkg/artifact/config.go +++ b/x-pack/elastic-agent/pkg/artifact/config.go @@ -5,9 +5,12 @@ package artifact import ( + "path/filepath" "runtime" "strings" "time" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths" ) // Config is a configuration used for verifier and downloader @@ -42,6 +45,18 @@ type Config struct { DropPath string `yaml:"dropPath" config:"drop_path"` } +// DefaultConfig creates a config with pre-set default values. +func DefaultConfig() *Config { + dataPath := paths.Data() + return &Config{ + BeatsSourceURI: "https://artifacts.elastic.co/downloads/beats/", + TargetDirectory: filepath.Join(dataPath, "downloads"), + Timeout: 30 * time.Second, + PgpFile: filepath.Join(dataPath, "elastic.pgp"), + InstallPath: filepath.Join(dataPath, "install"), + } +} + // OS return configured operating system or falls back to runtime.GOOS func (c *Config) OS() string { if c.OperatingSystem != "" { diff --git a/x-pack/elastic-agent/pkg/core/logger/logger.go b/x-pack/elastic-agent/pkg/core/logger/logger.go index 3f23feb782e..5c7e2aa9527 100644 --- a/x-pack/elastic-agent/pkg/core/logger/logger.go +++ b/x-pack/elastic-agent/pkg/core/logger/logger.go @@ -5,6 +5,8 @@ package logger import ( + "fmt" + "github.com/urso/ecslog" "github.com/urso/ecslog/backend" "github.com/urso/ecslog/backend/appender" @@ -16,21 +18,78 @@ import ( // Logger alias ecslog.Logger with Logger. type Logger = ecslog.Logger +// Config is a configuration of logging. +type Config struct { + Level loggingLevel `config:"level"` +} + +// DefaultLoggingConfig creates a default logging configuration. +func DefaultLoggingConfig() *Config { + return &Config{ + Level: loggingLevel(backend.Trace), + } +} + // New returns a configured ECS Logger func New() (*Logger, error) { - backend, err := createJSONBackend() + return new(backend.Trace) +} + +func createJSONBackend(lvl backend.Level) (backend.Backend, error) { + return appender.Console(lvl, layout.Text(true)) +} + +//NewFromConfig takes the user configuration and generate the right logger. +// TODO: Finish implementation, need support on the library that we use. +func NewFromConfig(cfg *config.Config) (*Logger, error) { + wrappedConfig := &struct { + Logging *Config `config:"logging"` + }{ + Logging: DefaultLoggingConfig(), + } + + if err := cfg.Unpack(&wrappedConfig); err != nil { + return nil, err + } + + return new(backend.Level(wrappedConfig.Logging.Level)) +} + +func new(lvl backend.Level) (*Logger, error) { + backend, err := createJSONBackend(lvl) if err != nil { return nil, err } return ecslog.New(backend), nil } -func createJSONBackend() (backend.Backend, error) { - return appender.Console(backend.Trace, layout.Text(true)) +type loggingLevel backend.Level + +var loggingLevelMap = map[string]loggingLevel{ + "trace": loggingLevel(backend.Trace), + "debug": loggingLevel(backend.Debug), + "info": loggingLevel(backend.Info), + "error": loggingLevel(backend.Error), } -//NewFromConfig takes the user configuration and generate the right logger. -// TODO: Finish implementation, need support on the library that we use. -func NewFromConfig(_ *config.Config) (*Logger, error) { - return New() +func (m *loggingLevel) Unpack(v string) error { + mgt, ok := loggingLevelMap[v] + if !ok { + return fmt.Errorf( + "unknown logging level mode, received '%s' and valid values are 'trace', 'debug', 'info' or 'error'", + v, + ) + } + *m = mgt + return nil +} + +func (m *loggingLevel) String() string { + for s, v := range loggingLevelMap { + if v == *m { + return s + } + } + + return "unknown" } diff --git a/x-pack/elastic-agent/pkg/core/plugin/app/app.go b/x-pack/elastic-agent/pkg/core/plugin/app/app.go index be361354a5e..0c343793b55 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/app/app.go +++ b/x-pack/elastic-agent/pkg/core/plugin/app/app.go @@ -42,6 +42,7 @@ type Application struct { id string name string pipelineID string + logLevel string spec Specifier state state.State grpcClient remoteconfig.Client @@ -70,7 +71,7 @@ type ArgsDecorator func([]string) []string // the application. func NewApplication( ctx context.Context, - id, appName, pipelineID string, + id, appName, pipelineID, logLevel string, spec Specifier, factory remoteconfig.ConnectionCreator, cfg *config.Config, @@ -90,6 +91,7 @@ func NewApplication( id: id, name: appName, pipelineID: pipelineID, + logLevel: logLevel, spec: spec, clientFactory: factory, processConfig: cfg.ProcessConfig, diff --git a/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/drop_test.go b/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/drop_test.go index a4d06169ca8..5c2f6be7f19 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/drop_test.go +++ b/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/drop_test.go @@ -15,22 +15,22 @@ type testCase struct { func TestMonitoringDrops(t *testing.T) { cases := []testCase{ - testCase{`/var/lib/drop/abc.sock`, "/var/lib/drop"}, - testCase{`npipe://drop`, ""}, - testCase{`http+npipe://drop`, ""}, - testCase{`\\.\pipe\drop`, ""}, - testCase{`unix:///var/lib/drop/abc.sock`, "/var/lib/drop"}, - testCase{`http+unix:///var/lib/drop/abc.sock`, "/var/lib/drop"}, - testCase{`file:///var/lib/drop/abc.sock`, "/var/lib/drop"}, - testCase{`http://localhost/stats`, ""}, - testCase{`localhost/stats`, ""}, - testCase{`http://localhost:8080/stats`, ""}, - testCase{`localhost:8080/stats`, ""}, - testCase{`http://1.2.3.4/stats`, ""}, - testCase{`http://1.2.3.4:5678/stats`, ""}, - testCase{`1.2.3.4:5678/stats`, ""}, - testCase{`http://hithere.com:5678/stats`, ""}, - testCase{`hithere.com:5678/stats`, ""}, + {`/var/lib/drop/abc.sock`, "/var/lib/drop"}, + {`npipe://drop`, ""}, + {`http+npipe://drop`, ""}, + {`\\.\pipe\drop`, ""}, + {`unix:///var/lib/drop/abc.sock`, "/var/lib/drop"}, + {`http+unix:///var/lib/drop/abc.sock`, "/var/lib/drop"}, + {`file:///var/lib/drop/abc.sock`, "/var/lib/drop"}, + {`http://localhost/stats`, ""}, + {`localhost/stats`, ""}, + {`http://localhost:8080/stats`, ""}, + {`localhost:8080/stats`, ""}, + {`http://1.2.3.4/stats`, ""}, + {`http://1.2.3.4:5678/stats`, ""}, + {`1.2.3.4:5678/stats`, ""}, + {`http://hithere.com:5678/stats`, ""}, + {`hithere.com:5678/stats`, ""}, } for _, c := range cases { diff --git a/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/monitoring.go b/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/monitoring.go index c551f5ef18c..265ea4cda82 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/monitoring.go +++ b/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/beats/monitoring.go @@ -6,16 +6,18 @@ package beats import ( "fmt" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths" ) const ( - // args: pipeline name, application name - logFileFormat = "/var/log/elastic-agent/%s/%s" - // args: install path, pipeline name, application name - logFileFormatWin = "%s\\logs\\elastic-agent\\%s\\%s" + // args: data path, pipeline name, application name + logFileFormat = "%s/logs/%s/%s" + // args: data path, install path, pipeline name, application name + logFileFormatWin = "%s\\logs\\%s\\%s" // args: pipeline name, application name - mbEndpointFileFormat = "unix:///var/run/elastic-agent/%s/%s/%s.sock" + mbEndpointFileFormat = "unix:///tmp/elastic-agent/%s/%s/%s.sock" // args: pipeline name, application name mbEndpointFileFormatWin = `npipe:///%s-%s` ) @@ -30,8 +32,8 @@ func getMonitoringEndpoint(program, operatingSystem, pipelineID string) string { func getLoggingFile(program, operatingSystem, installPath, pipelineID string) string { if operatingSystem == "windows" { - return fmt.Sprintf(logFileFormatWin, installPath, pipelineID, program) + return fmt.Sprintf(logFileFormatWin, paths.Data(), pipelineID, program) } - return fmt.Sprintf(logFileFormat, pipelineID, program) + return fmt.Sprintf(logFileFormat, paths.Data(), pipelineID, program) } diff --git a/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/monitor.go b/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/monitor.go index 67fd1a96aee..07750ff5191 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/monitor.go +++ b/x-pack/elastic-agent/pkg/core/plugin/app/monitoring/monitor.go @@ -32,7 +32,10 @@ type wrappedConfig struct { // NewMonitor creates a monitor based on a process configuration. func NewMonitor(config *config.Config) (Monitor, error) { - cfg := &wrappedConfig{} + cfg := &wrappedConfig{ + DownloadConfig: artifact.DefaultConfig(), + } + if err := config.Unpack(&cfg); err != nil { return nil, err } diff --git a/x-pack/elastic-agent/pkg/core/plugin/app/start.go b/x-pack/elastic-agent/pkg/core/plugin/app/start.go index 00684753a0d..e13e137699e 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/app/start.go +++ b/x-pack/elastic-agent/pkg/core/plugin/app/start.go @@ -15,6 +15,7 @@ import ( "gopkg.in/yaml.v2" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/paths" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/authority" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin/process" @@ -80,6 +81,7 @@ func (a *Application) Start(ctx context.Context, cfg map[string]interface{}) (er a.limiter.Add() } + spec.Args = injectLogLevel(a.logLevel, spec.Args) spec.Args = a.monitor.EnrichArgs(a.name, a.pipelineID, spec.Args) // specify beat name to avoid data lock conflicts @@ -113,6 +115,27 @@ func (a *Application) Start(ctx context.Context, cfg map[string]interface{}) (er return nil } +func injectLogLevel(logLevel string, args []string) []string { + var level string + // Translate to level beat understands + switch logLevel { + case "trace": + level = "debug" + case "info": + level = "info" + case "debug": + level = "debug" + case "error": + level = "error" + } + + if args == nil || level == "" { + return args + } + + return append(args, "-E", "logging.level="+level) +} + func (a *Application) waitForGrpc(spec ProcessSpec, ca *authority.CertificateAuthority) error { const ( rounds int = 3 @@ -210,12 +233,7 @@ func (a *Application) checkGrpcHTTP(ctx context.Context, address string, ca *aut } func injectDataPath(args []string, pipelineID, id string) []string { - wd := "" - if w, err := os.Getwd(); err == nil { - wd = w - } - - dataPath := filepath.Join(wd, "data", pipelineID, id) + dataPath := filepath.Join(paths.Data(), "run", pipelineID, id) return append(args, "-E", "path.data="+dataPath) } diff --git a/x-pack/elastic-agent/pkg/core/plugin/process/config.go b/x-pack/elastic-agent/pkg/core/plugin/process/config.go index 0791692aeb4..e8a236b5de4 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/process/config.go +++ b/x-pack/elastic-agent/pkg/core/plugin/process/config.go @@ -19,3 +19,12 @@ type Config struct { // TODO: cgroups and namespaces } + +// DefaultConfig creates a config with pre-set default values. +func DefaultConfig() *Config { + return &Config{ + MinPortNumber: 10000, + MaxPortNumber: 30000, + SpawnTimeout: 30 * time.Second, + } +} diff --git a/x-pack/elastic-agent/pkg/core/plugin/retry/config.go b/x-pack/elastic-agent/pkg/core/plugin/retry/config.go index a9487792a17..11cd1e7b418 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/retry/config.go +++ b/x-pack/elastic-agent/pkg/core/plugin/retry/config.go @@ -21,10 +21,21 @@ type Config struct { RetriesCount int `yaml:"retriesCount" config:"retriesCount"` // Delay specifies delay in ms between retries. Default is 30s Delay time.Duration `yaml:"delay" config:"delay"` - // MaxDelay specifies maximum delay in ms between retries. Default is 300s + // MaxDelay specifies maximum delay in ms between retries. Default is 300s (5min) MaxDelay time.Duration `yaml:"maxDelay" config:"maxDelay"` // Exponential determines whether delay is treated as exponential. // With 30s delay and 3 retries: 30, 60, 120s // Default is false Exponential bool `yaml:"exponential" config:"exponential"` } + +// DefaultConfig creates a config with pre-set default values. +func DefaultConfig() *Config { + return &Config{ + Enabled: false, + RetriesCount: 3, + Delay: 30 * time.Second, + MaxDelay: 5 * time.Minute, + Exponential: false, + } +} diff --git a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go index 80ce76d5b55..7f6eb3d91a2 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go +++ b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd.go @@ -13,6 +13,7 @@ import ( "net/http" "time" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" ) @@ -20,8 +21,8 @@ const checkingPath = "/api/ingest_manager/fleet/agents/%s/checkin" // CheckinRequest consists of multiple events reported to fleet ui. type CheckinRequest struct { - Events []SerializableEvent `json:"events"` - Metadata map[string]interface{} `json:"local_metadata,omitempty"` + Events []SerializableEvent `json:"events"` + Metadata *info.ECSMeta `json:"local_metadata,omitempty"` } // SerializableEvent is a representation of the event to be send to the Fleet API via the checkin diff --git a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go index b9eb9f3aa81..43501c7fac7 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go +++ b/x-pack/elastic-agent/pkg/fleetapi/checkin_cmd_test.go @@ -14,6 +14,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" ) type agentinfo struct{} @@ -28,9 +30,9 @@ func TestCheckin(t *testing.T) { t.Run("Propagate any errors from the server", withServerWithAuthClient( func(t *testing.T) *http.ServeMux { raw := ` -Something went wrong -} -` + Something went wrong + } + ` mux := http.NewServeMux() path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { @@ -52,35 +54,35 @@ Something went wrong t.Run("Checkin receives a PolicyChange", withServerWithAuthClient( func(t *testing.T) *http.ServeMux { raw := ` -{ - "actions": [{ - "type": "CONFIG_CHANGE", - "id": "id1", - "data": { - "config": { - "id": "policy-id", - "outputs": { - "default": { - "hosts": "https://localhost:9200" - } - }, - "datasources": [{ - "id": "string", - "enabled": true, - "use_output": "default", - "inputs": [{ - "type": "logs", - "streams": [{ - "paths": ["/var/log/hello.log"] + { + "actions": [{ + "type": "CONFIG_CHANGE", + "id": "id1", + "data": { + "config": { + "id": "policy-id", + "outputs": { + "default": { + "hosts": "https://localhost:9200" + } + }, + "datasources": [{ + "id": "string", + "enabled": true, + "use_output": "default", + "inputs": [{ + "type": "logs", + "streams": [{ + "paths": ["/var/log/hello.log"] + }] }] }] - }] + } } - } - }], - "success": true -} -` + }], + "success": true + } + ` mux := http.NewServeMux() path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { @@ -109,41 +111,41 @@ Something went wrong t.Run("Checkin receives known and unknown action type", withServerWithAuthClient( func(t *testing.T) *http.ServeMux { raw := ` -{ - "actions": [ - { - "type": "CONFIG_CHANGE", - "id": "id1", - "data": { - "config": { - "id": "policy-id", - "outputs": { - "default": { - "hosts": "https://localhost:9200" - } - }, - "datasources": [{ - "id": "string", - "enabled": true, - "use_output": "default", - "inputs": [{ - "type": "logs", - "streams": [{ - "paths": ["/var/log/hello.log"] + { + "actions": [ + { + "type": "CONFIG_CHANGE", + "id": "id1", + "data": { + "config": { + "id": "policy-id", + "outputs": { + "default": { + "hosts": "https://localhost:9200" + } + }, + "datasources": [{ + "id": "string", + "enabled": true, + "use_output": "default", + "inputs": [{ + "type": "logs", + "streams": [{ + "paths": ["/var/log/hello.log"] + }] }] }] - }] - } - } - }, - { - "type": "WHAT_TO_DO_WITH_IT", - "id": "id2" - } - ], - "success": true -} -` + } + } + }, + { + "type": "WHAT_TO_DO_WITH_IT", + "id": "id2" + } + ], + "success": true + } + ` mux := http.NewServeMux() path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { @@ -177,11 +179,11 @@ Something went wrong t.Run("When we receive no action", withServerWithAuthClient( func(t *testing.T) *http.ServeMux { raw := ` -{ - "actions": [], - "success": true -} -` + { + "actions": [], + "success": true + } + ` mux := http.NewServeMux() path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { @@ -215,21 +217,15 @@ Something went wrong path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { type Request struct { - Metadata map[string]interface{} `json:"local_metadata"` + Metadata *info.ECSMeta `json:"local_metadata"` } - req := &Request{} + + var req *Request content, err := ioutil.ReadAll(r.Body) assert.NoError(t, err) assert.NoError(t, json.Unmarshal(content, &req)) - - assert.Equal(t, 1, len(req.Metadata)) - v, found := req.Metadata["key"] - assert.True(t, found) - - intV, ok := v.(string) - assert.True(t, ok) - assert.Equal(t, "value", intV) + assert.Equal(t, "linux", req.Metadata.OS.Name) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, raw) @@ -237,13 +233,9 @@ Something went wrong return mux }, withAPIKey, func(t *testing.T, client clienter) { - meta := map[string]interface{}{ - "key": "value", - } - cmd := NewCheckinCmd(agentInfo, client) - request := CheckinRequest{Metadata: meta} + request := CheckinRequest{Metadata: testMetadata()} r, err := cmd.Execute(ctx, &request) require.NoError(t, err) @@ -256,22 +248,24 @@ Something went wrong t.Run("No meta are sent when not provided", withServerWithAuthClient( func(t *testing.T) *http.ServeMux { raw := ` -{ - "actions": [], - "success": true -} -` + { + "actions": [], + "success": true + } + ` mux := http.NewServeMux() path := fmt.Sprintf("/api/ingest_manager/fleet/agents/%s/checkin", agentInfo.AgentID()) mux.HandleFunc(path, authHandler(func(w http.ResponseWriter, r *http.Request) { - req := make(map[string]interface{}) + type Request struct { + Metadata *info.ECSMeta `json:"local_metadata"` + } + + var req *Request content, err := ioutil.ReadAll(r.Body) assert.NoError(t, err) assert.NoError(t, json.Unmarshal(content, &req)) - - _, found := req["key"] - assert.False(t, found) + assert.Nil(t, req.Metadata) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, raw) diff --git a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go index c7410baa0e7..0d2784ef741 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go +++ b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/go-multierror" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" ) @@ -84,7 +85,7 @@ type EnrollRequest struct { // Metadata is a all the metadata send or received from the elastic-agent. type Metadata struct { - Local map[string]interface{} `json:"local"` + Local *info.ECSMeta `json:"local"` UserProvided map[string]interface{} `json:"user_provided"` } diff --git a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go index c4f79c7cb38..df341b0110e 100644 --- a/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go +++ b/x-pack/elastic-agent/pkg/fleetapi/enroll_cmd_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/kibana" ) @@ -38,10 +39,8 @@ func TestEnroll(t *testing.T) { require.Equal(t, PermanentEnroll, req.Type) require.Equal(t, "im-a-beat", req.SharedID) - require.Equal(t, Metadata{ - Local: map[string]interface{}{"os": "linux"}, - UserProvided: make(map[string]interface{}), - }, req.Metadata) + require.Equal(t, make(map[string]interface{}), req.Metadata.UserProvided) + require.Equal(t, "linux", req.Metadata.Local.OS.Name) response := &EnrollResponse{ Action: "created", @@ -77,9 +76,7 @@ func TestEnroll(t *testing.T) { EnrollAPIKey: "my-enrollment-api-key", SharedID: "im-a-beat", Metadata: Metadata{ - Local: map[string]interface{}{ - "os": "linux", - }, + Local: testMetadata(), UserProvided: make(map[string]interface{}), }, } @@ -116,9 +113,7 @@ func TestEnroll(t *testing.T) { EnrollAPIKey: "my-enrollment-api-key", SharedID: "im-a-beat", Metadata: Metadata{ - Local: map[string]interface{}{ - "os": "linux", - }, + Local: testMetadata(), UserProvided: make(map[string]interface{}), }, } @@ -132,3 +127,11 @@ func TestEnroll(t *testing.T) { }, )) } + +func testMetadata() *info.ECSMeta { + return &info.ECSMeta{ + OS: &info.SystemECSMeta{ + Name: "linux", + }, + } +} diff --git a/x-pack/elastic-agent/spec/filebeat.yml b/x-pack/elastic-agent/spec/filebeat.yml index 232ac9b12bb..0ed7bd422d4 100644 --- a/x-pack/elastic-agent/spec/filebeat.yml +++ b/x-pack/elastic-agent/spec/filebeat.yml @@ -1,6 +1,6 @@ name: Filebeat cmd: filebeat -args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true"] +args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true", "-E", "logging.level=debug"] configurable: grpc rules: - inject_index: @@ -60,6 +60,14 @@ rules: - docker - redis - syslog + - s3 + - netflow + - httpjson + - o365audit + - azureeventhub + - cloudfoundry + - googlepubsub + - kafka - filter_values: selector: inputs diff --git a/x-pack/elastic-agent/spec/metricbeat.yml b/x-pack/elastic-agent/spec/metricbeat.yml index b9085c8fbb6..3dc7f6507d5 100644 --- a/x-pack/elastic-agent/spec/metricbeat.yml +++ b/x-pack/elastic-agent/spec/metricbeat.yml @@ -1,6 +1,6 @@ name: Metricbeat cmd: metricbeat -args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true"] +args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true", "-E", "logging.level=debug"] configurable: grpc post_install: - move_file: diff --git a/x-pack/filebeat/Makefile b/x-pack/filebeat/Makefile index 56633e2b3e5..019d3b9309a 100644 --- a/x-pack/filebeat/Makefile +++ b/x-pack/filebeat/Makefile @@ -1,3 +1,3 @@ ES_BEATS ?= ../.. -include $(ES_BEATS)/dev-tools/make/xpack.mk +include $(ES_BEATS)/dev-tools/make/mage.mk diff --git a/x-pack/filebeat/_meta/config/beat.reference.yml.tmpl b/x-pack/filebeat/_meta/config/beat.reference.yml.tmpl new file mode 100644 index 00000000000..d90bd36c7b9 --- /dev/null +++ b/x-pack/filebeat/_meta/config/beat.reference.yml.tmpl @@ -0,0 +1,6 @@ +{{template "header.reference.yml.tmpl" .}} +{{template "config.modules.yml.tmpl" .}} +{{template "filebeat.inputs.reference.yml.tmpl" .}} +{{template "filebeat.inputs.reference.xpack.yml.tmpl" .}} +{{template "filebeat.autodiscover.reference.yml.tmpl" .}} +{{template "filebeat.global.reference.yml.tmpl" .}} diff --git a/x-pack/filebeat/_meta/common.reference.inputs.yml b/x-pack/filebeat/_meta/config/filebeat.inputs.reference.xpack.yml.tmpl similarity index 100% rename from x-pack/filebeat/_meta/common.reference.inputs.yml rename to x-pack/filebeat/_meta/config/filebeat.inputs.reference.xpack.yml.tmpl diff --git a/x-pack/filebeat/filebeat.reference.yml b/x-pack/filebeat/filebeat.reference.yml index 3f88fa42976..056b574abe5 100644 --- a/x-pack/filebeat/filebeat.reference.yml +++ b/x-pack/filebeat/filebeat.reference.yml @@ -1047,6 +1047,7 @@ filebeat.modules: #var.paths: + #=========================== Filebeat inputs ============================= # List of inputs to fetch data. @@ -1114,6 +1115,11 @@ filebeat.inputs: # Set to true to publish fields with null values in events. #keep_null: false + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + # Ignore files which were modified more then the defined timespan in the past. # ignore_older is disabled by default, so no files are ignored by setting it to 0. # Time strings like 2h (2 hours), 5m (5 minutes) can be used. @@ -1423,6 +1429,7 @@ filebeat.inputs: # Configure stream to filter to a specific stream: stdout, stderr or all (default) #stream: all + #------------------------------ NetFlow input -------------------------------- # Experimental: Config options for the Netflow/IPFIX collector over UDP input #- type: netflow @@ -1498,7 +1505,8 @@ filebeat.inputs: # The duration (in seconds) that the received messages are hidden from subsequent # retrieve requests after being retrieved by a ReceiveMessage request. #visibility_timeout: 300 -#========================== Filebeat autodiscover ============================== + +# =========================== Filebeat autodiscover ============================ # Autodiscover allows you to detect changes in the system and spawn new modules # or inputs as they happen. @@ -1515,7 +1523,7 @@ filebeat.inputs: # paths: # - /var/lib/docker/containers/${data.docker.container.id}/*.log -#========================= Filebeat global options ============================ +# ========================== Filebeat global options =========================== # Registry data path. If a relative path is used, it is considered relative to the # data path. @@ -1562,7 +1570,8 @@ filebeat.inputs: #reload.enabled: true #reload.period: 10s -#================================ General ====================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -1670,7 +1679,7 @@ filebeat.inputs: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -1687,153 +1696,153 @@ filebeat.inputs: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Filebeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -1846,11 +1855,11 @@ filebeat.inputs: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -1970,7 +1979,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -2084,7 +2114,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -2238,6 +2268,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -2260,7 +2293,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -2378,7 +2411,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -2412,7 +2445,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -2425,7 +2458,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Filebeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -2451,11 +2484,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -2499,8 +2534,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -2554,7 +2588,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -2568,13 +2602,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'filebeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "filebeat" +#setup.ilm.rollover_alias: 'filebeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'filebeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -2589,7 +2623,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -2644,9 +2678,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -2713,8 +2746,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Filebeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -2824,6 +2856,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -2835,7 +2888,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -2859,12 +2913,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/x-pack/filebeat/filebeat.yml b/x-pack/filebeat/filebeat.yml index 581e1a43f23..51a0d40224e 100644 --- a/x-pack/filebeat/filebeat.yml +++ b/x-pack/filebeat/filebeat.yml @@ -10,7 +10,7 @@ # For more available modules and options, please see the filebeat.reference.yml sample # configuration file. -#=========================== Filebeat inputs ============================= +# ============================== Filebeat inputs =============================== filebeat.inputs: @@ -62,8 +62,7 @@ filebeat.inputs: # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after - -#============================= Filebeat modules =============================== +# ============================== Filebeat modules ============================== filebeat.config.modules: # Glob pattern for configuration loading @@ -75,14 +74,15 @@ filebeat.config.modules: # Period on which files under path should be checked for changes #reload.period: 10s -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 #index.codec: best_compression #_source.enabled: false -#================================ General ===================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -97,8 +97,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -110,7 +109,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -127,7 +126,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Filebeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -140,11 +139,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -157,7 +156,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -172,7 +171,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -182,7 +181,8 @@ processors: - add_docker_metadata: ~ - add_kubernetes_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -193,8 +193,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# filebeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Filebeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -215,7 +215,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/x-pack/filebeat/input/googlepubsub/pubsub_test.go b/x-pack/filebeat/input/googlepubsub/pubsub_test.go index 17863862c8a..58d4db9331c 100644 --- a/x-pack/filebeat/input/googlepubsub/pubsub_test.go +++ b/x-pack/filebeat/input/googlepubsub/pubsub_test.go @@ -208,7 +208,7 @@ func defaultTestConfig() *common.Config { } func isInDockerIntegTestEnv() bool { - return os.Getenv("BEATS_DOCKER_INTEGRATION_TEST_ENV") != "" + return os.Getenv("BEATS_INSIDE_INTEGRATION_TEST_ENV") != "" } func runTest(t *testing.T, cfg *common.Config, run func(client *pubsub.Client, input *pubsubInput, out *stubOutleter, t *testing.T)) { diff --git a/x-pack/filebeat/input/netflow/convert.go b/x-pack/filebeat/input/netflow/convert.go index cfc3fd1736a..69d7a2ea6cd 100644 --- a/x-pack/filebeat/input/netflow/convert.go +++ b/x-pack/filebeat/input/netflow/convert.go @@ -66,9 +66,12 @@ func toBeatEventCommon(flow record.Record) (event beat.Event) { ecsEvent := common.MapStr{ "created": flow.Timestamp, "kind": "event", - "category": "network_traffic", + "category": []string{"network_traffic", "network"}, "action": flow.Fields["type"], } + if ecsEvent["action"] == "netflow_flow" { + ecsEvent["type"] = []string{"connection"} + } // ECS Fields -- device ecsDevice := common.MapStr{} if exporter, ok := getKeyString(flow.Exporter, "address"); ok { @@ -155,9 +158,10 @@ func flowToBeatEvent(flow record.Record) (event beat.Event) { } flowDirection, hasFlowDirection := getKeyUint64(flow.Fields, "flowDirection") - // ECS Fields -- source and destination + // ECS Fields -- source, destination & related.ip ecsSource := common.MapStr{} ecsDest := common.MapStr{} + var relatedIP []net.IP // Populate first with WLAN fields if hasFlowDirection { @@ -189,6 +193,7 @@ func flowToBeatEvent(flow record.Record) (event beat.Event) { // Regular IPv4 fields if ip, found := getKeyIP(flow.Fields, "sourceIPv4Address"); found { ecsSource["ip"] = ip + relatedIP = append(relatedIP, ip) ecsSource["locality"] = getIPLocality(ip).String() } if sourcePort, found := getKeyUint64(flow.Fields, "sourceTransportPort"); found { @@ -201,6 +206,7 @@ func flowToBeatEvent(flow record.Record) (event beat.Event) { // ECS Fields -- destination if ip, found := getKeyIP(flow.Fields, "destinationIPv4Address"); found { ecsDest["ip"] = ip + relatedIP = append(relatedIP, ip) ecsDest["locality"] = getIPLocality(ip).String() } if destPort, found := getKeyUint64(flow.Fields, "destinationTransportPort"); found { @@ -313,6 +319,9 @@ func flowToBeatEvent(flow record.Record) (event beat.Event) { if len(ecsNetwork) > 0 { event.Fields["network"] = ecsNetwork } + if len(relatedIP) > 0 { + event.Fields["related"] = common.MapStr{"ip": relatedIP} + } return } diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-extended-uniflow-template-256.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-extended-uniflow-template-256.golden.json index 37d62175d9b..3bdc7c5d159 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-extended-uniflow-template-256.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-extended-uniflow-template-256.golden.json @@ -12,10 +12,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-18T08:16:47Z", "duration": 0, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "kSpZ1WuBhjc", @@ -70,6 +76,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.236.5.4", + "64.235.151.76" + ] + }, "source": { "bytes": 0, "ip": "10.236.5.4", @@ -93,10 +105,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-18T08:16:47Z", "duration": 0, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "kSpZ1WuBhjc", @@ -151,6 +169,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "64.235.151.76", + "10.236.5.4" + ] + }, "source": { "bytes": 0, "ip": "64.235.151.76", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-firewall.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-firewall.golden.json index ad5333cfc04..3814fa8c843 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-firewall.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Barracuda-firewall.golden.json @@ -12,10 +12,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20269000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "2vFIarATx_4", @@ -58,6 +64,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.99.130.239", + "10.99.252.50" + ] + }, "source": { "bytes": 0, "ip": "10.99.130.239", @@ -81,10 +93,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20269000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "2vFIarATx_4", @@ -127,6 +145,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.99.252.50", + "10.99.130.239" + ] + }, "source": { "bytes": 81, "ip": "10.99.252.50", @@ -150,10 +174,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20306000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "wU3G8idsscw", @@ -196,6 +226,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.99.130.239", + "10.98.243.20" + ] + }, "source": { "bytes": 0, "ip": "10.99.130.239", @@ -219,10 +255,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20306000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "wU3G8idsscw", @@ -265,6 +307,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.98.243.20", + "10.99.130.239" + ] + }, "source": { "bytes": 81, "ip": "10.98.243.20", @@ -288,10 +336,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20317000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "rOmj8EdZ2dc", @@ -334,6 +388,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.99.168.140", + "10.98.243.20" + ] + }, "source": { "bytes": 0, "ip": "10.99.168.140", @@ -357,10 +417,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20317000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "rOmj8EdZ2dc", @@ -403,6 +469,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.98.243.20", + "10.99.168.140" + ] + }, "source": { "bytes": 113, "ip": "10.98.243.20", @@ -426,10 +498,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20368000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "JE7pThaMwJY", @@ -472,6 +550,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.99.168.140", + "10.98.243.20" + ] + }, "source": { "bytes": 0, "ip": "10.99.168.140", @@ -495,10 +579,16 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-29T13:58:28Z", "duration": 20368000000, - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "JE7pThaMwJY", @@ -541,6 +631,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.98.243.20", + "10.99.168.140" + ] + }, "source": { "bytes": 113, "ip": "10.98.243.20", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Mikrotik-RouterOS-6.39.2.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Mikrotik-RouterOS-6.39.2.golden.json index 1f990e524ab..5b7004c43f3 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Mikrotik-RouterOS-6.39.2.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Mikrotik-RouterOS-6.39.2.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "1SREAwMSn_Y", @@ -57,6 +63,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.8.197", + "192.168.128.17" + ] + }, "source": { "bytes": 152, "ip": "10.10.8.197", @@ -79,9 +91,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "-1ecQ0Y-YzY", @@ -124,6 +142,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.35.143", + "192.168.230.216" + ] + }, "source": { "bytes": 502, "ip": "192.168.35.143", @@ -146,9 +170,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_ztnBsqvzw4", @@ -191,6 +221,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.6.11", + "192.168.35.143" + ] + }, "source": { "bytes": 2233, "ip": "10.10.6.11", @@ -213,9 +249,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "83jerlRbQig", @@ -258,6 +300,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.128.17", + "192.168.230.216" + ] + }, "source": { "bytes": 152, "ip": "192.168.128.17", @@ -280,9 +328,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "r6DcuKSlKG8", @@ -325,6 +379,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.8.220", + "172.20.5.191" + ] + }, "source": { "bytes": 79724, "ip": "10.10.8.220", @@ -347,9 +407,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "MJV4se1d1EY", @@ -392,6 +458,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.20.4.199", + "172.20.4.1" + ] + }, "source": { "bytes": 161, "ip": "172.20.4.199", @@ -414,9 +486,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "MJV4se1d1EY", @@ -459,6 +537,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.20.4.1", + "172.20.4.199" + ] + }, "source": { "bytes": 245, "ip": "172.20.4.1", @@ -481,9 +565,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Md4y9RxWsu0", @@ -526,6 +616,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.20.4.30", + "10.10.8.34" + ] + }, "source": { "bytes": 504, "ip": "172.20.4.30", @@ -548,9 +644,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_XZysP4InTc", @@ -593,6 +695,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.8.105", + "172.20.4.30" + ] + }, "source": { "bytes": 784, "ip": "10.10.8.105", @@ -615,9 +723,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_XZysP4InTc", @@ -660,6 +774,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.20.4.30", + "10.10.8.105" + ] + }, "source": { "bytes": 433, "ip": "172.20.4.30", @@ -682,9 +802,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "5stvUzTWY8c", @@ -727,6 +853,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.7.11", + "192.168.183.199" + ] + }, "source": { "bytes": 196, "ip": "10.10.7.11", @@ -749,9 +881,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "VdPCBSYnnS0", @@ -794,6 +932,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.183.199", + "192.168.230.216" + ] + }, "source": { "bytes": 206, "ip": "192.168.183.199", @@ -816,9 +960,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "asoP1PL3Pao", @@ -861,6 +1011,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.8.34", + "172.20.4.30" + ] + }, "source": { "bytes": 504, "ip": "10.10.8.34", @@ -883,9 +1039,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "r6DcuKSlKG8", @@ -928,6 +1090,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.20.5.191", + "10.10.8.220" + ] + }, "source": { "bytes": 3539, "ip": "172.20.5.191", @@ -950,9 +1118,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "4AA5ETLDkm0", @@ -995,6 +1169,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.20.4.1", + "255.255.255.255" + ] + }, "source": { "bytes": 495, "ip": "172.20.4.1", @@ -1017,9 +1197,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "4AA5ETLDkm0", @@ -1062,6 +1248,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.20.4.1", + "255.255.255.255" + ] + }, "source": { "bytes": 330, "ip": "172.20.4.1", @@ -1084,9 +1276,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "BaTGW6h8V9s", @@ -1129,6 +1327,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.30.0.1", + "255.255.255.255" + ] + }, "source": { "bytes": 435, "ip": "172.30.0.1", @@ -1151,9 +1355,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "BaTGW6h8V9s", @@ -1196,6 +1406,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.30.0.1", + "255.255.255.255" + ] + }, "source": { "bytes": 290, "ip": "172.30.0.1", @@ -1218,9 +1434,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "a0peNOTOYXA", @@ -1263,6 +1485,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.6.1", + "255.255.255.255" + ] + }, "source": { "bytes": 495, "ip": "10.10.6.1", @@ -1285,9 +1513,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "a0peNOTOYXA", @@ -1330,6 +1564,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.6.1", + "255.255.255.255" + ] + }, "source": { "bytes": 330, "ip": "10.10.6.1", @@ -1352,9 +1592,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "rX81_0wnl4c", @@ -1397,6 +1643,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.7.1", + "255.255.255.255" + ] + }, "source": { "bytes": 495, "ip": "10.10.7.1", @@ -1419,9 +1671,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "rX81_0wnl4c", @@ -1464,6 +1722,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.7.1", + "255.255.255.255" + ] + }, "source": { "bytes": 330, "ip": "10.10.7.1", @@ -1486,9 +1750,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "7EW3D8kjT4Q", @@ -1531,6 +1801,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.8.1", + "255.255.255.255" + ] + }, "source": { "bytes": 495, "ip": "10.10.8.1", @@ -1553,9 +1829,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "7EW3D8kjT4Q", @@ -1598,6 +1880,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.8.1", + "255.255.255.255" + ] + }, "source": { "bytes": 330, "ip": "10.10.8.1", @@ -1620,9 +1908,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "JacJ1_FgpYg", @@ -1665,6 +1959,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.20.0.1", + "255.255.255.255" + ] + }, "source": { "bytes": 495, "ip": "10.20.0.1", @@ -1687,9 +1987,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "JacJ1_FgpYg", @@ -1732,6 +2038,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.20.0.1", + "255.255.255.255" + ] + }, "source": { "bytes": 330, "ip": "10.20.0.1", @@ -1754,9 +2066,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "38frmBtEgfI", @@ -1799,6 +2117,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.10.1", + "255.255.255.255" + ] + }, "source": { "bytes": 495, "ip": "10.10.10.1", @@ -1821,9 +2145,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "38frmBtEgfI", @@ -1866,6 +2196,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.10.1", + "255.255.255.255" + ] + }, "source": { "bytes": 330, "ip": "10.10.10.1", @@ -1886,9 +2222,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -1947,9 +2289,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2008,9 +2356,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2069,9 +2423,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2130,9 +2490,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2191,9 +2557,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2252,9 +2624,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2313,9 +2691,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2374,9 +2758,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2435,9 +2825,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2496,9 +2892,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2557,9 +2959,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2618,9 +3026,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2679,9 +3093,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2740,9 +3160,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2801,9 +3227,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2862,9 +3294,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", @@ -2923,9 +3361,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-19T16:18:08Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "RlrAo_U1Y14", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Netscaler-with-variable-length-fields.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Netscaler-with-variable-length-fields.golden.json index 5037ae27ecb..d1ccaac0791 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Netscaler-with-variable-length-fields.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Netscaler-with-variable-length-fields.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-11-11T12:09:19Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "8wXIKNz6u_8", @@ -80,6 +86,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "10.0.0.1" + ] + }, "source": { "bytes": 40, "ip": "192.168.0.1", @@ -102,9 +114,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-11-11T12:09:19Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "8wXIKNz6u_8", @@ -158,6 +176,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.0.1", + "192.168.0.1" + ] + }, "source": { "bytes": 1525, "ip": "10.0.0.1", @@ -180,9 +204,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-11-11T12:09:19Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "8wXIKNz6u_8", @@ -248,6 +278,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "10.0.0.1" + ] + }, "source": { "bytes": 1541, "ip": "192.168.0.1", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Nokia-BRAS.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Nokia-BRAS.golden.json index 3c4a77c41b6..b77d79ff35c 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Nokia-BRAS.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Nokia-BRAS.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-12-14T07:23:45Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "aVnWxMM8qxI", @@ -50,6 +56,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.1.228", + "10.0.0.34" + ] + }, "source": { "ip": "10.0.1.228", "locality": "private", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-OpenBSD-pflow.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-OpenBSD-pflow.golden.json index e8331bf0f97..41fb9b9c48d 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-OpenBSD-pflow.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-OpenBSD-pflow.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_dzJqQAoWYk", @@ -53,6 +59,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 373, "ip": "192.168.0.17", @@ -75,9 +87,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_dzJqQAoWYk", @@ -116,6 +134,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 6634, "ip": "192.168.0.1", @@ -138,9 +162,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "iSYE82PBcbQ", @@ -179,6 +209,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 453, "ip": "192.168.0.17", @@ -201,9 +237,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "iSYE82PBcbQ", @@ -242,6 +284,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 10893, "ip": "192.168.0.1", @@ -264,9 +312,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "iSYE82PBcbQ", @@ -305,6 +359,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 453, "ip": "192.168.0.17", @@ -327,9 +387,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "iSYE82PBcbQ", @@ -368,6 +434,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 10893, "ip": "192.168.0.1", @@ -390,9 +462,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "L_N7tNeOZwc", @@ -431,6 +509,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 373, "ip": "192.168.0.17", @@ -453,9 +537,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "L_N7tNeOZwc", @@ -494,6 +584,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 6780, "ip": "192.168.0.1", @@ -516,9 +612,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "L_N7tNeOZwc", @@ -557,6 +659,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 373, "ip": "192.168.0.17", @@ -579,9 +687,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "L_N7tNeOZwc", @@ -620,6 +734,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 6780, "ip": "192.168.0.1", @@ -642,9 +762,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Dsp4RZAzcPQ", @@ -683,6 +809,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 373, "ip": "192.168.0.17", @@ -705,9 +837,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Dsp4RZAzcPQ", @@ -746,6 +884,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 7319, "ip": "192.168.0.1", @@ -768,9 +912,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Dsp4RZAzcPQ", @@ -809,6 +959,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 373, "ip": "192.168.0.17", @@ -831,9 +987,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Dsp4RZAzcPQ", @@ -872,6 +1034,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 7319, "ip": "192.168.0.1", @@ -894,9 +1062,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B9Jsqhany8Q", @@ -935,6 +1109,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 333, "ip": "192.168.0.17", @@ -957,9 +1137,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B9Jsqhany8Q", @@ -998,6 +1184,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 1833, "ip": "192.168.0.1", @@ -1020,9 +1212,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B9Jsqhany8Q", @@ -1061,6 +1259,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 333, "ip": "192.168.0.17", @@ -1083,9 +1287,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B9Jsqhany8Q", @@ -1124,6 +1334,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 1833, "ip": "192.168.0.1", @@ -1146,9 +1362,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "O7k79Py4ef0", @@ -1187,6 +1409,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 453, "ip": "192.168.0.17", @@ -1209,9 +1437,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "O7k79Py4ef0", @@ -1250,6 +1484,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 10550, "ip": "192.168.0.1", @@ -1272,9 +1512,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "O7k79Py4ef0", @@ -1313,6 +1559,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 453, "ip": "192.168.0.17", @@ -1335,9 +1587,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "O7k79Py4ef0", @@ -1376,6 +1634,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 10550, "ip": "192.168.0.1", @@ -1398,9 +1662,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "T1etbJ4WSI0", @@ -1439,6 +1709,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 373, "ip": "192.168.0.17", @@ -1461,9 +1737,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "T1etbJ4WSI0", @@ -1502,6 +1784,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 6425, "ip": "192.168.0.1", @@ -1524,9 +1812,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "T1etbJ4WSI0", @@ -1565,6 +1859,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.17", + "192.168.0.1" + ] + }, "source": { "bytes": 373, "ip": "192.168.0.17", @@ -1587,9 +1887,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:30:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "T1etbJ4WSI0", @@ -1628,6 +1934,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 6425, "ip": "192.168.0.1", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Procera.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Procera.golden.json index 8295166e4c0..1ec8673c346 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Procera.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-Procera.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "gEodlN50y4w", @@ -62,6 +68,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "181.214.87.71", + "138.44.161.14" + ] + }, "source": { "ip": "181.214.87.71", "locality": "public", @@ -82,9 +94,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "GYmhjYyvaAI", @@ -132,6 +150,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "0.0.0.0", + "0.0.0.0" + ] + }, "source": { "ip": "0.0.0.0", "locality": "private", @@ -152,9 +176,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "qSSNfC38l0c", @@ -202,6 +232,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "5.188.11.35", + "138.44.161.14" + ] + }, "source": { "ip": "5.188.11.35", "locality": "public", @@ -222,9 +258,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Tv1jmZy2vn4", @@ -272,6 +314,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "206.117.25.89", + "138.44.161.14" + ] + }, "source": { "ip": "206.117.25.89", "locality": "public", @@ -292,9 +340,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "GYmhjYyvaAI", @@ -342,6 +396,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "0.0.0.0", + "0.0.0.0" + ] + }, "source": { "ip": "0.0.0.0", "locality": "private", @@ -362,9 +422,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "JhEHWMX5XwI", @@ -412,6 +478,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "185.232.29.199", + "138.44.161.14" + ] + }, "source": { "ip": "185.232.29.199", "locality": "public", @@ -432,9 +504,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q_zyIhDZuIo", @@ -482,6 +560,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "177.188.228.137", + "138.44.161.14" + ] + }, "source": { "ip": "177.188.228.137", "locality": "public", @@ -502,9 +586,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-04-15T03:30:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "pNMKY7O9aVc", @@ -552,6 +642,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "138.44.161.14", + "138.44.161.13" + ] + }, "source": { "ip": "138.44.161.14", "locality": "public", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-VMware-virtual-distributed-switch.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-VMware-virtual-distributed-switch.golden.json index c77bc562aad..7f97270bb03 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-VMware-virtual-distributed-switch.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-VMware-virtual-distributed-switch.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-22T12:17:52Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "-Sv1di8xiKE", @@ -62,6 +68,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.18.65.21", + "172.18.65.211" + ] + }, "source": { "bytes": 100, "ip": "172.18.65.21", @@ -84,9 +96,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-22T12:17:56Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "OQCLJ5IN83c", @@ -134,6 +152,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.18.65.91", + "172.18.65.255" + ] + }, "source": { "bytes": 229, "ip": "172.18.65.91", @@ -156,9 +180,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-22T12:17:56Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "OQCLJ5IN83c", @@ -206,6 +236,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.18.65.91", + "172.18.65.255" + ] + }, "source": { "bytes": 229, "ip": "172.18.65.91", @@ -228,9 +264,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-22T12:26:04Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "xcyYrM-QBl0", @@ -278,6 +320,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.18.65.21", + "224.0.0.252" + ] + }, "source": { "bytes": 104, "ip": "172.18.65.21", @@ -298,9 +346,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-22T12:26:04Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "y_Vml2vPNtw", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-YAF-basic-with-applabel.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-YAF-basic-with-applabel.golden.json index 95c1c37fb42..fa7eed00986 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-YAF-basic-with-applabel.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-YAF-basic-with-applabel.golden.json @@ -14,9 +14,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-25T13:03:38Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "QMH_S2K9KdI", @@ -63,6 +69,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.100" + ] + }, "source": { "bytes": 132, "ip": "172.16.32.201", @@ -87,9 +99,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-25T12:58:38Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "YlvEOsG0NHc", @@ -142,6 +160,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.215" + ] + }, "source": { "bytes": 172, "ip": "172.16.32.100", @@ -159,7 +183,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-25T13:03:33Z", "kind": "event" }, diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-configured-with-include_flowset_id.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-configured-with-include_flowset_id.golden.json index 50892931663..1eda2ee228b 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-configured-with-include_flowset_id.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-configured-with-include_flowset_id.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-11-11T12:09:19Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "8wXIKNz6u_8", @@ -80,6 +86,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "10.0.0.1" + ] + }, "source": { "bytes": 40, "ip": "192.168.0.1", @@ -102,9 +114,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-11-11T12:09:19Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "8wXIKNz6u_8", @@ -158,6 +176,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.0.1", + "192.168.0.1" + ] + }, "source": { "bytes": 1525, "ip": "10.0.0.1", @@ -180,9 +204,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-11-11T12:09:19Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "8wXIKNz6u_8", @@ -248,6 +278,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "10.0.0.1" + ] + }, "source": { "bytes": 1541, "ip": "192.168.0.1", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-options-template-from-Juniper-MX240-JunOS-15.1-R6-S3.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-options-template-from-Juniper-MX240-JunOS-15.1-R6-S3.golden.json index 763e20e774e..d4aa929699b 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-options-template-from-Juniper-MX240-JunOS-15.1-R6-S3.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-options-template-from-Juniper-MX240-JunOS-15.1-R6-S3.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-06-01T15:11:53Z", "kind": "event" }, diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-vIPtela-with-VPN-id.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-vIPtela-with-VPN-id.golden.json index b43b3a4f6b5..f477e8c3d37 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-vIPtela-with-VPN-id.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX-vIPtela-with-VPN-id.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-21T14:32:15Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "dO-Anbp9xpw", @@ -65,6 +71,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.113.7.54", + "172.16.21.27" + ] + }, "source": { "bytes": 775, "ip": "10.113.7.54", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX.golden.json index c458b21dd9e..1fdb6707c2e 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/IPFIX.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/IPFIX.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", "kind": "event" }, @@ -48,9 +51,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ofdVXz7_x6E", @@ -93,6 +102,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.1", + "192.168.253.128" + ] + }, "source": { "bytes": 260, "ip": "192.168.253.1", @@ -115,9 +130,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ofdVXz7_x6E", @@ -160,6 +181,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.128", + "192.168.253.1" + ] + }, "source": { "bytes": 1000, "ip": "192.168.253.128", @@ -182,9 +209,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ztL93_3GZNs", @@ -227,6 +260,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.2", + "192.168.253.132" + ] + }, "source": { "bytes": 601, "ip": "192.168.253.2", @@ -249,9 +288,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ztL93_3GZNs", @@ -294,6 +339,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.132", + "192.168.253.2" + ] + }, "source": { "bytes": 148, "ip": "192.168.253.132", @@ -316,9 +367,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "VANFUe1rklc", @@ -361,6 +418,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "54.214.9.161", + "192.168.253.132" + ] + }, "source": { "bytes": 5946, "ip": "54.214.9.161", @@ -383,9 +446,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "VANFUe1rklc", @@ -428,6 +497,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.132", + "54.214.9.161" + ] + }, "source": { "bytes": 2608, "ip": "192.168.253.132", @@ -450,9 +525,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:26Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "iDHwMSG6faQ", @@ -495,6 +576,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.130", + "10.4.36.64" + ] + }, "source": { "bytes": 60, "ip": "192.168.253.130", @@ -517,9 +604,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:28Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ofdVXz7_x6E", @@ -562,6 +655,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.1", + "192.168.253.128" + ] + }, "source": { "bytes": 256, "ip": "192.168.253.1", @@ -584,9 +683,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:28Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ofdVXz7_x6E", @@ -629,6 +734,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.128", + "192.168.253.1" + ] + }, "source": { "bytes": 1916, "ip": "192.168.253.128", @@ -651,9 +762,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:28Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "WgPN9s2D0jg", @@ -696,6 +813,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.1", + "192.168.253.128" + ] + }, "source": { "bytes": 168, "ip": "192.168.253.1", @@ -718,9 +841,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:28Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "WgPN9s2D0jg", @@ -763,6 +892,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.128", + "192.168.253.1" + ] + }, "source": { "bytes": 84, "ip": "192.168.253.128", @@ -785,9 +920,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-05-13T11:20:28Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "PSMPOofjjVU", @@ -830,6 +971,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.253.1", + "224.0.0.251" + ] + }, "source": { "bytes": 232, "ip": "192.168.253.1", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-1941-K9-release-15.1.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-1941-K9-release-15.1.golden.json index b3b9bec5c1c..ad5bbec160f 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-1941-K9-release-15.1.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-1941-K9-release-15.1.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "BPlkuHwo9sU", @@ -56,6 +62,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.111", + "62.217.193.1" + ] + }, "source": { "bytes": 75, "ip": "192.168.0.111", @@ -79,9 +91,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "-PhJhHv5gvE", @@ -123,6 +141,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.111", + "62.217.193.65" + ] + }, "source": { "bytes": 75, "ip": "192.168.0.111", @@ -146,9 +170,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "zTrEnrxMnjo", @@ -190,6 +220,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.111", + "62.217.193.1" + ] + }, "source": { "bytes": 75, "ip": "192.168.0.111", @@ -213,9 +249,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "G4AVpSxBAVo", @@ -257,6 +299,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.111", + "62.217.193.65" + ] + }, "source": { "bytes": 75, "ip": "192.168.0.111", @@ -280,9 +328,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "2nQmjOOzSH0", @@ -324,6 +378,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "158.85.58.115", + "192.168.3.142" + ] + }, "source": { "bytes": 964, "ip": "158.85.58.115", @@ -347,9 +407,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "z7uHiA5SrD0", @@ -391,6 +457,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.88", + "216.58.212.195" + ] + }, "source": { "bytes": 2748, "ip": "192.168.0.88", @@ -414,9 +486,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "z7uHiA5SrD0", @@ -458,6 +536,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "216.58.212.195", + "192.168.0.88" + ] + }, "source": { "bytes": 2023, "ip": "216.58.212.195", @@ -481,9 +565,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "eyNcUtWu34I", @@ -525,6 +615,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.201", + "216.58.201.106" + ] + }, "source": { "bytes": 2180, "ip": "192.168.1.201", @@ -548,9 +644,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "eyNcUtWu34I", @@ -592,6 +694,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "216.58.201.106", + "192.168.1.201" + ] + }, "source": { "bytes": 700, "ip": "216.58.201.106", @@ -615,9 +723,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "i7e4W23LBGg", @@ -659,6 +773,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "52.236.33.163", + "192.168.2.118" + ] + }, "source": { "bytes": 161, "ip": "52.236.33.163", @@ -682,9 +802,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ALOJ32qLh_s", @@ -726,6 +852,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.3.34", + "52.216.130.237" + ] + }, "source": { "bytes": 1764, "ip": "192.168.3.34", @@ -749,9 +881,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "h9s7TXaoMZw", @@ -793,6 +931,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "209.197.3.19", + "192.168.3.34" + ] + }, "source": { "bytes": 13811, "ip": "209.197.3.19", @@ -816,9 +960,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ALOJ32qLh_s", @@ -860,6 +1010,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "52.216.130.237", + "192.168.3.34" + ] + }, "source": { "bytes": 4717, "ip": "52.216.130.237", @@ -883,9 +1039,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "2GPS5gJiF8g", @@ -927,6 +1089,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.157", + "172.217.23.232" + ] + }, "source": { "bytes": 2419, "ip": "192.168.0.157", @@ -950,9 +1118,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "2GPS5gJiF8g", @@ -994,6 +1168,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.217.23.232", + "192.168.0.157" + ] + }, "source": { "bytes": 5551, "ip": "172.217.23.232", @@ -1017,9 +1197,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ughO0a0lrBw", @@ -1061,6 +1247,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "107.21.232.174", + "192.168.3.178" + ] + }, "source": { "bytes": 187, "ip": "107.21.232.174", @@ -1084,9 +1276,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ughO0a0lrBw", @@ -1128,6 +1326,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.3.178", + "107.21.232.174" + ] + }, "source": { "bytes": 104, "ip": "192.168.3.178", @@ -1151,9 +1355,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Ie4W_7Snl8w", @@ -1195,6 +1405,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.2.118", + "95.0.145.242" + ] + }, "source": { "bytes": 4050, "ip": "192.168.2.118", @@ -1218,9 +1434,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Ie4W_7Snl8w", @@ -1262,6 +1484,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "95.0.145.242", + "192.168.2.118" + ] + }, "source": { "bytes": 3719, "ip": "95.0.145.242", @@ -1285,9 +1513,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "yokq763qB0U", @@ -1329,6 +1563,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.79", + "23.5.100.66" + ] + }, "source": { "bytes": 1402, "ip": "192.168.0.79", @@ -1352,9 +1592,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "DCY-5ocv9ik", @@ -1396,6 +1642,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.79", + "23.5.100.66" + ] + }, "source": { "bytes": 1538, "ip": "192.168.0.79", @@ -1419,9 +1671,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "DCY-5ocv9ik", @@ -1463,6 +1721,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "23.5.100.66", + "192.168.0.79" + ] + }, "source": { "bytes": 13002, "ip": "23.5.100.66", @@ -1486,9 +1750,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B7rjR_940zU", @@ -1530,6 +1800,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "170.251.180.15", + "192.168.0.61" + ] + }, "source": { "bytes": 1194, "ip": "170.251.180.15", @@ -1553,9 +1829,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B7rjR_940zU", @@ -1597,6 +1879,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.61", + "170.251.180.15" + ] + }, "source": { "bytes": 682, "ip": "192.168.0.61", @@ -1620,9 +1908,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "0RrmR_QtH34", @@ -1664,6 +1958,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.3.34", + "74.119.119.84" + ] + }, "source": { "bytes": 1804, "ip": "192.168.3.34", @@ -1687,9 +1987,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "O1-Y9rjVH2A", @@ -1731,6 +2037,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "185.60.218.19", + "192.168.3.142" + ] + }, "source": { "bytes": 4774, "ip": "185.60.218.19", @@ -1754,9 +2066,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "CtFBGbTcLpg", @@ -1798,6 +2116,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.3.200", + "185.60.218.15" + ] + }, "source": { "bytes": 135, "ip": "192.168.3.200", @@ -1821,9 +2145,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "CtFBGbTcLpg", @@ -1865,6 +2195,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "185.60.218.15", + "192.168.3.200" + ] + }, "source": { "bytes": 135, "ip": "185.60.218.15", @@ -1888,9 +2224,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-03T17:03:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lT_guTKc7y4", @@ -1932,6 +2274,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.95", + "169.45.214.246" + ] + }, "source": { "bytes": 194, "ip": "192.168.0.95", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA-2.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA-2.golden.json index dc73be6acf3..68ca3bdb60e 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA-2.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA-2.golden.json @@ -13,9 +13,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "UTkRrDbrhnI", @@ -61,6 +67,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "bytes": 81, "ip": "192.168.0.2", @@ -83,9 +95,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "WQVc0v7217I", @@ -131,6 +149,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "bytes": 81, "ip": "192.168.0.2", @@ -153,9 +177,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "WQVc0v7217I", @@ -201,6 +231,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "bytes": 81, "ip": "192.168.0.2", @@ -223,9 +259,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Nle5z0FLBjA", @@ -271,6 +313,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "bytes": 81, "ip": "192.168.0.1", @@ -293,9 +341,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Nle5z0FLBjA", @@ -341,6 +395,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "bytes": 81, "ip": "192.168.0.1", @@ -363,9 +423,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lfYzCmoZgqo", @@ -411,6 +477,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "bytes": 81, "ip": "192.168.0.2", @@ -433,9 +505,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lfYzCmoZgqo", @@ -481,6 +559,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "bytes": 81, "ip": "192.168.0.2", @@ -502,9 +586,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_9ahEyFsD94", @@ -550,6 +640,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "ip": "192.168.0.1", "locality": "private", @@ -571,9 +667,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_9ahEyFsD94", @@ -619,6 +721,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "bytes": 69, "ip": "192.168.0.1", @@ -641,9 +749,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_9ahEyFsD94", @@ -689,6 +803,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "bytes": 69, "ip": "192.168.0.1", @@ -710,9 +830,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "bnG6S7DUlEE", @@ -758,6 +884,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "ip": "192.168.0.2", "locality": "private", @@ -779,9 +911,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "bnG6S7DUlEE", @@ -827,6 +965,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "bytes": 69, "ip": "192.168.0.2", @@ -849,9 +993,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "bnG6S7DUlEE", @@ -897,6 +1047,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.2", + "192.168.0.17" + ] + }, "source": { "bytes": 69, "ip": "192.168.0.2", @@ -918,9 +1074,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "wuMbsS0oTj4", @@ -966,6 +1128,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "ip": "192.168.0.1", "locality": "private", @@ -987,9 +1155,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "wuMbsS0oTj4", @@ -1035,6 +1209,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 75, "ip": "192.168.0.1", @@ -1057,9 +1237,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "wuMbsS0oTj4", @@ -1105,6 +1291,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.17" + ] + }, "source": { "bytes": 75, "ip": "192.168.0.1", @@ -1126,9 +1318,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "geQD5O-NWw8", @@ -1174,6 +1372,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "ip": "192.168.0.1", "locality": "private", @@ -1195,9 +1399,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "geQD5O-NWw8", @@ -1243,6 +1453,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "bytes": 69, "ip": "192.168.0.1", @@ -1265,9 +1481,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-07-21T13:50:37Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "geQD5O-NWw8", @@ -1313,6 +1535,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.1", + "192.168.0.18" + ] + }, "source": { "bytes": 69, "ip": "192.168.0.1", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA.golden.json index df4f450fbc3..e250cce2afa 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASA.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "5JpExP8VeSU", @@ -62,6 +68,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.14.1", + "2.2.2.11" + ] + }, "source": { "bytes": 56, "ip": "192.168.14.1", @@ -83,9 +95,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "MSQgezzAYh0", @@ -133,6 +151,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.23.22", + "164.164.37.11" + ] + }, "source": { "bytes": 56, "ip": "192.168.23.22", @@ -154,9 +178,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "MSQgezzAYh0", @@ -204,6 +234,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "164.164.37.11", + "192.168.23.22" + ] + }, "source": { "bytes": 56, "ip": "164.164.37.11", @@ -225,9 +261,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ioGVEAJtaEQ", @@ -275,6 +317,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.23.20", + "164.164.37.11" + ] + }, "source": { "bytes": 56, "ip": "192.168.23.20", @@ -296,9 +344,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "ioGVEAJtaEQ", @@ -346,6 +400,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "164.164.37.11", + "192.168.23.20" + ] + }, "source": { "bytes": 56, "ip": "164.164.37.11", @@ -367,9 +427,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "0xqELVtMeog", @@ -417,6 +483,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.14.11", + "2.2.2.11" + ] + }, "source": { "bytes": 56, "ip": "192.168.14.11", @@ -438,9 +510,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "0xqELVtMeog", @@ -488,6 +566,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "2.2.2.11", + "192.168.14.11" + ] + }, "source": { "bytes": 56, "ip": "2.2.2.11", @@ -509,9 +593,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "LA3WpK17LAw", @@ -559,6 +649,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "2.2.2.11", + "192.168.14.1" + ] + }, "source": { "bytes": 56, "ip": "2.2.2.11", @@ -580,9 +676,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "LA3WpK17LAw", @@ -630,6 +732,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.14.1", + "2.2.2.11" + ] + }, "source": { "bytes": 56, "ip": "192.168.14.1", @@ -651,9 +759,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "tBFZO1WrQyk", @@ -701,6 +815,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "164.164.37.11", + "192.168.23.1" + ] + }, "source": { "bytes": 160, "ip": "164.164.37.11", @@ -722,9 +842,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "oil2JqFPSyE", @@ -772,6 +898,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.23.22", + "164.164.37.11" + ] + }, "source": { "bytes": 56, "ip": "192.168.23.22", @@ -793,9 +925,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "oil2JqFPSyE", @@ -843,6 +981,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "164.164.37.11", + "192.168.23.22" + ] + }, "source": { "bytes": 56, "ip": "164.164.37.11", @@ -864,9 +1008,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Pbk_o-xetL4", @@ -914,6 +1064,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.23.20", + "164.164.37.11" + ] + }, "source": { "bytes": 56, "ip": "192.168.23.20", @@ -935,9 +1091,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-09T09:47:51Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Pbk_o-xetL4", @@ -985,6 +1147,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "164.164.37.11", + "192.168.23.20" + ] + }, "source": { "bytes": 56, "ip": "164.164.37.11", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-options-template-256.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-options-template-256.golden.json index bc346d8c98e..625225c213b 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-options-template-256.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-options-template-256.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -41,7 +44,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -75,7 +81,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -109,7 +118,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -143,7 +155,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -177,7 +192,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -211,7 +229,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -245,7 +266,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -279,7 +303,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -313,7 +340,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -347,7 +377,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -381,7 +414,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -415,7 +451,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -449,7 +488,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -483,7 +525,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -517,7 +562,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -551,7 +599,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -585,7 +636,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, @@ -619,7 +673,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:48Z", "kind": "event" }, diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-template-260.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-template-260.golden.json index cf1ad940af8..fa2e4920cce 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-template-260.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR-9000-series-template-260.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.94Z", "kind": "event", - "start": "2016-12-06T10:08:53.94Z" + "start": "2016-12-06T10:08:53.94Z", + "type": [ + "connection" + ] }, "flow": { "id": "kkhtKjgAywQ", @@ -66,6 +72,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.9.146", + "10.0.31.81" + ] + }, "source": { "bytes": 40, "ip": "10.0.9.146", @@ -88,12 +100,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 641000000, "end": "2016-12-06T10:08:54.583Z", "kind": "event", - "start": "2016-12-06T10:08:53.942Z" + "start": "2016-12-06T10:08:53.942Z", + "type": [ + "connection" + ] }, "flow": { "id": "4su7p2nlyno", @@ -142,6 +160,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.17.42", + "10.0.35.4" + ] + }, "source": { "bytes": 104, "ip": "10.0.17.42", @@ -164,12 +188,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.945Z", "kind": "event", - "start": "2016-12-06T10:08:53.945Z" + "start": "2016-12-06T10:08:53.945Z", + "type": [ + "connection" + ] }, "flow": { "id": "mfb1_zWayo4", @@ -218,6 +248,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.22.111", + "10.0.34.141" + ] + }, "source": { "bytes": 52, "ip": "10.0.22.111", @@ -240,12 +276,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.947Z", "kind": "event", - "start": "2016-12-06T10:08:53.947Z" + "start": "2016-12-06T10:08:53.947Z", + "type": [ + "connection" + ] }, "flow": { "id": "jKhffDbQq0o", @@ -294,6 +336,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.23.59", + "10.0.36.170" + ] + }, "source": { "bytes": 435, "ip": "10.0.23.59", @@ -316,12 +364,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.948Z", "kind": "event", - "start": "2016-12-06T10:08:53.948Z" + "start": "2016-12-06T10:08:53.948Z", + "type": [ + "connection" + ] }, "flow": { "id": "5siGD7iCzo4", @@ -370,6 +424,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.34.71", + "10.0.20.242" + ] + }, "source": { "bytes": 969, "ip": "10.0.34.71", @@ -392,12 +452,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 83000000, "end": "2016-12-06T10:08:53.948Z", "kind": "event", - "start": "2016-12-06T10:08:53.865Z" + "start": "2016-12-06T10:08:53.865Z", + "type": [ + "connection" + ] }, "flow": { "id": "IyuegsSri_U", @@ -446,6 +512,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.10.133", + "10.0.30.102" + ] + }, "source": { "bytes": 104, "ip": "10.0.10.133", @@ -468,12 +540,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.951Z", "kind": "event", - "start": "2016-12-06T10:08:53.951Z" + "start": "2016-12-06T10:08:53.951Z", + "type": [ + "connection" + ] }, "flow": { "id": "9JGzjsOdNi4", @@ -522,6 +600,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.37.29", + "10.0.6.24" + ] + }, "source": { "bytes": 52, "ip": "10.0.37.29", @@ -544,12 +628,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.951Z", "kind": "event", - "start": "2016-12-06T10:08:53.951Z" + "start": "2016-12-06T10:08:53.951Z", + "type": [ + "connection" + ] }, "flow": { "id": "Y3aiAEAjjys", @@ -598,6 +688,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.32.176", + "10.0.11.113" + ] + }, "source": { "bytes": 614, "ip": "10.0.32.176", @@ -620,12 +716,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 5418000000, "end": "2016-12-06T10:08:53.952Z", "kind": "event", - "start": "2016-12-06T10:08:48.534Z" + "start": "2016-12-06T10:08:48.534Z", + "type": [ + "connection" + ] }, "flow": { "id": "sC3kzwxISec", @@ -674,6 +776,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.12.21", + "10.0.15.38" + ] + }, "source": { "bytes": 4350, "ip": "10.0.12.21", @@ -696,12 +804,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 3317000000, "end": "2016-12-06T10:08:57.27Z", "kind": "event", - "start": "2016-12-06T10:08:53.953Z" + "start": "2016-12-06T10:08:53.953Z", + "type": [ + "connection" + ] }, "flow": { "id": "dTmlxL48EoA", @@ -750,6 +864,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.4.212", + "10.0.3.110" + ] + }, "source": { "bytes": 533, "ip": "10.0.4.212", @@ -772,12 +892,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 19894000000, "end": "2016-12-06T10:09:04.383Z", "kind": "event", - "start": "2016-12-06T10:08:44.489Z" + "start": "2016-12-06T10:08:44.489Z", + "type": [ + "connection" + ] }, "flow": { "id": "oMLDxCSgNuA", @@ -826,6 +952,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.33.122", + "10.0.1.136" + ] + }, "source": { "bytes": 13660, "ip": "10.0.33.122", @@ -848,12 +980,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.955Z", "kind": "event", - "start": "2016-12-06T10:08:53.955Z" + "start": "2016-12-06T10:08:53.955Z", + "type": [ + "connection" + ] }, "flow": { "id": "5siGD7iCzo4", @@ -902,6 +1040,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.20.242", + "10.0.34.71" + ] + }, "source": { "bytes": 89, "ip": "10.0.20.242", @@ -924,12 +1068,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.957Z", "kind": "event", - "start": "2016-12-06T10:08:53.957Z" + "start": "2016-12-06T10:08:53.957Z", + "type": [ + "connection" + ] }, "flow": { "id": "-IcTJfcRi8w", @@ -978,6 +1128,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.13.25", + "10.0.15.38" + ] + }, "source": { "bytes": 833, "ip": "10.0.13.25", @@ -1000,12 +1156,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 89000000, "end": "2016-12-06T10:08:53.959Z", "kind": "event", - "start": "2016-12-06T10:08:53.87Z" + "start": "2016-12-06T10:08:53.87Z", + "type": [ + "connection" + ] }, "flow": { "id": "tyf0jfEIDwM", @@ -1054,6 +1216,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.25.59", + "10.0.2.18" + ] + }, "source": { "bytes": 1625, "ip": "10.0.25.59", @@ -1076,12 +1244,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 17325000000, "end": "2016-12-06T10:09:05.882Z", "kind": "event", - "start": "2016-12-06T10:08:48.557Z" + "start": "2016-12-06T10:08:48.557Z", + "type": [ + "connection" + ] }, "flow": { "id": "OYKOBQNKdF4", @@ -1130,6 +1304,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.7.73", + "10.0.27.168" + ] + }, "source": { "bytes": 142184, "ip": "10.0.7.73", @@ -1152,12 +1332,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 2705000000, "end": "2016-12-06T10:08:56.186Z", "kind": "event", - "start": "2016-12-06T10:08:53.481Z" + "start": "2016-12-06T10:08:53.481Z", + "type": [ + "connection" + ] }, "flow": { "id": "fC6tFjsdK54", @@ -1206,6 +1392,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.19.50", + "10.0.27.169" + ] + }, "source": { "bytes": 3016, "ip": "10.0.19.50", @@ -1228,12 +1420,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 361000000, "end": "2016-12-06T10:08:54.28Z", "kind": "event", - "start": "2016-12-06T10:08:53.919Z" + "start": "2016-12-06T10:08:53.919Z", + "type": [ + "connection" + ] }, "flow": { "id": "Kk4bVU4hDRk", @@ -1282,6 +1480,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.28.150", + "10.0.24.13" + ] + }, "source": { "bytes": 31500, "ip": "10.0.28.150", @@ -1304,12 +1508,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 378000000, "end": "2016-12-06T10:08:54.037Z", "kind": "event", - "start": "2016-12-06T10:08:53.659Z" + "start": "2016-12-06T10:08:53.659Z", + "type": [ + "connection" + ] }, "flow": { "id": "_Fk2ywvptGE", @@ -1358,6 +1568,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.26.188", + "10.0.21.200" + ] + }, "source": { "bytes": 2919, "ip": "10.0.26.188", @@ -1380,12 +1596,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 11106000000, "end": "2016-12-06T10:09:03.759Z", "kind": "event", - "start": "2016-12-06T10:08:52.653Z" + "start": "2016-12-06T10:08:52.653Z", + "type": [ + "connection" + ] }, "flow": { "id": "MrTF7IZhOrg", @@ -1434,6 +1656,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.29.34", + "10.0.15.38" + ] + }, "source": { "bytes": 4514, "ip": "10.0.29.34", @@ -1456,12 +1684,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 0, "end": "2016-12-06T10:08:53.964Z", "kind": "event", - "start": "2016-12-06T10:08:53.964Z" + "start": "2016-12-06T10:08:53.964Z", + "type": [ + "connection" + ] }, "flow": { "id": "hUKUTbBVmIY", @@ -1510,6 +1744,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.8.200", + "10.0.5.224" + ] + }, "source": { "bytes": 326, "ip": "10.0.8.200", @@ -1532,12 +1772,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-06T10:09:24Z", "duration": 1587000000, "end": "2016-12-06T10:08:53.964Z", "kind": "event", - "start": "2016-12-06T10:08:52.377Z" + "start": "2016-12-06T10:08:52.377Z", + "type": [ + "connection" + ] }, "flow": { "id": "IoEUbnBqGXE", @@ -1586,6 +1832,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.0.29.46", + "10.0.15.38" + ] + }, "source": { "bytes": 112, "ip": "10.0.29.46", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR1001--X.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR1001--X.golden.json index 2484a8a7fa9..05ac80b86c9 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR1001--X.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-ASR1001--X.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_qSyv-Xe8IM", @@ -56,6 +62,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.111.111.242", + "10.12.100.13" + ] + }, "source": { "bytes": 965, "ip": "10.111.111.242", @@ -78,9 +90,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "7s_4xBb69Y0", @@ -122,6 +140,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.4.29", + "10.100.105.85" + ] + }, "source": { "bytes": 284, "ip": "10.10.4.29", @@ -144,9 +168,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "_qSyv-Xe8IM", @@ -188,6 +218,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.100.13", + "10.111.111.242" + ] + }, "source": { "bytes": 670, "ip": "10.12.100.13", @@ -210,9 +246,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "jk1T8-P2OHM", @@ -254,6 +296,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.104.239", + "10.10.11.21" + ] + }, "source": { "bytes": 80, "ip": "10.12.104.239", @@ -276,9 +324,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "jk1T8-P2OHM", @@ -320,6 +374,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.11.21", + "10.12.104.239" + ] + }, "source": { "bytes": 80, "ip": "10.10.11.21", @@ -342,9 +402,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "6AEj_wlzQm4", @@ -386,6 +452,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.100.101.45", + "10.15.131.98" + ] + }, "source": { "bytes": 101, "ip": "10.100.101.45", @@ -408,9 +480,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "MtCuD-nvBTY", @@ -452,6 +530,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.100.101.43", + "10.12.105.23" + ] + }, "source": { "bytes": 1134, "ip": "10.100.101.43", @@ -474,9 +558,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "8zAXung0YbA", @@ -518,6 +608,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "31.13.71.7", + "10.11.31.108" + ] + }, "source": { "bytes": 237, "ip": "31.13.71.7", @@ -540,9 +636,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "5LxKkXX5FfM", @@ -584,6 +686,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.11.21.60", + "10.100.105.86" + ] + }, "source": { "bytes": 91, "ip": "10.11.21.60", @@ -606,9 +714,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "MnDMft-qZjs", @@ -650,6 +764,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.92.102", + "172.217.11.5" + ] + }, "source": { "bytes": 41, "ip": "10.12.92.102", @@ -672,9 +792,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Ddy-Ii-ZDDI", @@ -716,6 +842,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.100.105.86", + "10.11.21.60" + ] + }, "source": { "bytes": 111, "ip": "10.100.105.86", @@ -738,9 +870,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Hiy-Ti0eVlY", @@ -782,6 +920,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.4.234", + "10.100.105.85" + ] + }, "source": { "bytes": 1164, "ip": "10.10.4.234", @@ -804,9 +948,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "7iMintjCsaw", @@ -848,6 +998,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.106.83", + "10.10.11.21" + ] + }, "source": { "bytes": 80, "ip": "10.12.106.83", @@ -870,9 +1026,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "MnDMft-qZjs", @@ -914,6 +1076,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.217.11.5", + "10.12.92.102" + ] + }, "source": { "bytes": 52, "ip": "172.217.11.5", @@ -936,9 +1104,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "7iMintjCsaw", @@ -980,6 +1154,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.11.21", + "10.12.106.83" + ] + }, "source": { "bytes": 80, "ip": "10.10.11.21", @@ -1002,9 +1182,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "hphBugBrKPY", @@ -1046,6 +1232,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.81.86", + "74.201.129.29" + ] + }, "source": { "bytes": 3088, "ip": "10.12.81.86", @@ -1068,9 +1260,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "gJ7Z20zGGk8", @@ -1112,6 +1310,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.14.121.98", + "10.12.100.13" + ] + }, "source": { "bytes": 5306, "ip": "10.14.121.98", @@ -1134,9 +1338,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Ddy-Ii-ZDDI", @@ -1178,6 +1388,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.11.21.60", + "10.100.105.86" + ] + }, "source": { "bytes": 116, "ip": "10.11.21.60", @@ -1200,9 +1416,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "gJ7Z20zGGk8", @@ -1244,6 +1466,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.100.13", + "10.14.121.98" + ] + }, "source": { "bytes": 22764, "ip": "10.12.100.13", @@ -1266,9 +1494,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "LZaFrMI9jg0", @@ -1310,6 +1544,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.102.125", + "10.10.11.21" + ] + }, "source": { "bytes": 80, "ip": "10.12.102.125", @@ -1332,9 +1572,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "f6pXcQQIzpU", @@ -1376,6 +1622,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.100.105.86", + "10.11.21.60" + ] + }, "source": { "bytes": 75, "ip": "10.100.105.86", @@ -1398,9 +1650,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "LZaFrMI9jg0", @@ -1442,6 +1700,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.11.21", + "10.12.102.125" + ] + }, "source": { "bytes": 80, "ip": "10.10.11.21", @@ -1464,9 +1728,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "gQGJtHjUcB8", @@ -1508,6 +1778,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.100.105.85", + "10.10.4.151" + ] + }, "source": { "bytes": 160, "ip": "10.100.105.85", @@ -1530,9 +1806,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "UHiF_w4I6zM", @@ -1574,6 +1856,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.14.25.80", + "17.253.24.253" + ] + }, "source": { "bytes": 76, "ip": "10.14.25.80", @@ -1596,9 +1884,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-10-09T20:22:35Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "czsFrOKrayM", @@ -1640,6 +1934,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.12.150.13", + "10.100.101.43" + ] + }, "source": { "bytes": 1340, "ip": "10.12.150.13", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-flowset-262.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-flowset-262.golden.json index c667fa408ad..8f1a0a6b951 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-flowset-262.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-flowset-262.golden.json @@ -13,12 +13,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:10:36Z", "duration": 0, "end": "2017-02-14T11:10:19.368Z", "kind": "event", - "start": "2017-02-14T11:10:19.368Z" + "start": "2017-02-14T11:10:19.368Z", + "type": [ + "connection" + ] }, "flow": { "id": "Bk-2FcuOyCU", @@ -70,6 +76,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.30.18.62", + "10.30.19.180" + ] + }, "source": { "bytes": 44, "ip": "10.30.18.62", @@ -94,12 +106,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:10:36Z", "duration": 0, "end": "2017-02-14T11:10:19.368Z", "kind": "event", - "start": "2017-02-14T11:10:19.368Z" + "start": "2017-02-14T11:10:19.368Z", + "type": [ + "connection" + ] }, "flow": { "id": "4Xk8GtQfUAo", @@ -151,6 +169,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.30.18.62", + "10.30.19.180" + ] + }, "source": { "bytes": 106, "ip": "10.30.18.62", @@ -175,12 +199,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:10:36Z", "duration": 0, "end": "2017-02-14T11:10:19.924Z", "kind": "event", - "start": "2017-02-14T11:10:19.924Z" + "start": "2017-02-14T11:10:19.924Z", + "type": [ + "connection" + ] }, "flow": { "id": "tfLRXnB6AOA", @@ -232,6 +262,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.172.60", + "10.30.19.180" + ] + }, "source": { "bytes": 44, "ip": "10.10.172.60", @@ -256,12 +292,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:10:36Z", "duration": 0, "end": "2017-02-14T11:10:19.996Z", "kind": "event", - "start": "2017-02-14T11:10:19.996Z" + "start": "2017-02-14T11:10:19.996Z", + "type": [ + "connection" + ] }, "flow": { "id": "1mfP23NPuB8", @@ -313,6 +355,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.172.60", + "10.30.19.180" + ] + }, "source": { "bytes": 76, "ip": "10.10.172.60", @@ -337,12 +385,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:10:36Z", "duration": 72000000, "end": "2017-02-14T11:10:20.008Z", "kind": "event", - "start": "2017-02-14T11:10:19.936Z" + "start": "2017-02-14T11:10:19.936Z", + "type": [ + "connection" + ] }, "flow": { "id": "g6a7KlISbtM", @@ -394,6 +448,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.10.172.60", + "10.30.19.180" + ] + }, "source": { "bytes": 2794, "ip": "10.10.172.60", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-options-template-260.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-options-template-260.golden.json index 3be5e2844f6..8dd560a8d64 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-options-template-260.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-NBAR-options-template-260.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -42,7 +45,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -77,7 +83,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -112,7 +121,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -147,7 +159,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -182,7 +197,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -217,7 +235,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -252,7 +273,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -287,7 +311,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -322,7 +349,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -357,7 +387,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -392,7 +425,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -427,7 +463,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -462,7 +501,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, @@ -497,7 +539,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-02-14T11:09:59Z", "kind": "event" }, diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-WLC.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-WLC.golden.json index 9782ab2e9f2..c55814ca02b 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-WLC.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Cisco-WLC.golden.json @@ -10,9 +10,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -70,9 +76,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -126,9 +138,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -186,9 +204,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -242,9 +266,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -300,9 +330,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -360,9 +396,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -416,9 +458,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -476,9 +524,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -532,9 +586,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -592,9 +652,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -648,9 +714,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -708,9 +780,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -764,9 +842,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -824,9 +908,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -880,9 +970,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -940,9 +1036,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", @@ -996,9 +1098,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "lTcFptYSabQ", @@ -1056,9 +1164,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-06-22T06:31:14Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Q1JIGzkHw0I", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-5.2.1.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-5.2.1.golden.json index 197212e152c..4fca04b8016 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-5.2.1.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-5.2.1.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-18T05:42:14Z", "kind": "event" }, @@ -51,9 +54,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-07-18T05:41:59Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "SKsZNpZob60", @@ -93,6 +102,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.99.7", + "31.13.87.36" + ] + }, "source": { "bytes": 152, "ip": "192.168.99.7", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-54x-appid.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-54x-appid.golden.json index 7e2cf662d47..bb4b82133d3 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-54x-appid.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Fortigate-FortiOS-54x-appid.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 410000000, "end": "2018-05-11T00:54:09.99Z", "kind": "event", - "start": "2018-05-11T00:54:09.58Z" + "start": "2018-05-11T00:54:09.58Z", + "type": [ + "connection" + ] }, "flow": { "id": "FfT-8jRRvok", @@ -64,6 +70,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.151", + "182.50.136.239" + ] + }, "source": { "bytes": 748, "ip": "192.168.100.151", @@ -86,12 +98,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 1130000000, "end": "2018-05-11T00:54:09.74Z", "kind": "event", - "start": "2018-05-11T00:54:08.61Z" + "start": "2018-05-11T00:54:08.61Z", + "type": [ + "connection" + ] }, "flow": { "id": "bZjTG4EkhLs", @@ -138,6 +156,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "208.100.17.187", + "192.168.100.151" + ] + }, "source": { "bytes": 6948, "ip": "208.100.17.187", @@ -160,12 +184,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 1130000000, "end": "2018-05-11T00:54:09.74Z", "kind": "event", - "start": "2018-05-11T00:54:08.61Z" + "start": "2018-05-11T00:54:08.61Z", + "type": [ + "connection" + ] }, "flow": { "id": "bZjTG4EkhLs", @@ -212,6 +242,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.151", + "208.100.17.187" + ] + }, "source": { "bytes": 1584, "ip": "192.168.100.151", @@ -234,12 +270,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 1040000000, "end": "2018-05-11T00:54:09.74Z", "kind": "event", - "start": "2018-05-11T00:54:08.7Z" + "start": "2018-05-11T00:54:08.7Z", + "type": [ + "connection" + ] }, "flow": { "id": "kZjCeMUhjqE", @@ -286,6 +328,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "208.100.17.189", + "192.168.100.151" + ] + }, "source": { "bytes": 8201, "ip": "208.100.17.189", @@ -308,12 +356,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 1040000000, "end": "2018-05-11T00:54:09.74Z", "kind": "event", - "start": "2018-05-11T00:54:08.7Z" + "start": "2018-05-11T00:54:08.7Z", + "type": [ + "connection" + ] }, "flow": { "id": "kZjCeMUhjqE", @@ -360,6 +414,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.151", + "208.100.17.189" + ] + }, "source": { "bytes": 1729, "ip": "192.168.100.151", @@ -382,12 +442,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 410000000, "end": "2018-05-11T00:54:09.11Z", "kind": "event", - "start": "2018-05-11T00:54:08.7Z" + "start": "2018-05-11T00:54:08.7Z", + "type": [ + "connection" + ] }, "flow": { "id": "8PR91KFjFKw", @@ -434,6 +500,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "178.255.83.1", + "192.168.100.151" + ] + }, "source": { "bytes": 1122, "ip": "178.255.83.1", @@ -456,12 +528,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 410000000, "end": "2018-05-11T00:54:09.11Z", "kind": "event", - "start": "2018-05-11T00:54:08.7Z" + "start": "2018-05-11T00:54:08.7Z", + "type": [ + "connection" + ] }, "flow": { "id": "8PR91KFjFKw", @@ -508,6 +586,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.151", + "178.255.83.1" + ] + }, "source": { "bytes": 705, "ip": "192.168.100.151", @@ -530,12 +614,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 370000000, "end": "2018-05-11T00:54:08.53Z", "kind": "event", - "start": "2018-05-11T00:54:08.16Z" + "start": "2018-05-11T00:54:08.16Z", + "type": [ + "connection" + ] }, "flow": { "id": "O5vacJG8mLQ", @@ -582,6 +672,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "178.255.83.1", + "192.168.100.151" + ] + }, "source": { "bytes": 1123, "ip": "178.255.83.1", @@ -604,12 +700,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 370000000, "end": "2018-05-11T00:54:08.53Z", "kind": "event", - "start": "2018-05-11T00:54:08.16Z" + "start": "2018-05-11T00:54:08.16Z", + "type": [ + "connection" + ] }, "flow": { "id": "O5vacJG8mLQ", @@ -656,6 +758,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.151", + "178.255.83.1" + ] + }, "source": { "bytes": 706, "ip": "192.168.100.151", @@ -678,12 +786,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 80000000, "end": "2018-05-11T00:51:08.63Z", "kind": "event", - "start": "2018-05-11T00:51:08.55Z" + "start": "2018-05-11T00:51:08.55Z", + "type": [ + "connection" + ] }, "flow": { "id": "wdz94oax40U", @@ -726,6 +840,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.111", + "192.168.100.150" + ] + }, "source": { "bytes": 74, "ip": "192.168.100.111", @@ -748,12 +868,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 80000000, "end": "2018-05-11T00:51:08.63Z", "kind": "event", - "start": "2018-05-11T00:51:08.55Z" + "start": "2018-05-11T00:51:08.55Z", + "type": [ + "connection" + ] }, "flow": { "id": "wdz94oax40U", @@ -796,6 +922,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.150", + "192.168.100.111" + ] + }, "source": { "bytes": 58, "ip": "192.168.100.150", @@ -818,12 +950,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 80000000, "end": "2018-05-11T00:51:08.63Z", "kind": "event", - "start": "2018-05-11T00:51:08.55Z" + "start": "2018-05-11T00:51:08.55Z", + "type": [ + "connection" + ] }, "flow": { "id": "KvZZ7LW-Qdc", @@ -866,6 +1004,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.111", + "192.168.100.150" + ] + }, "source": { "bytes": 74, "ip": "192.168.100.111", @@ -888,12 +1032,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 80000000, "end": "2018-05-11T00:51:08.63Z", "kind": "event", - "start": "2018-05-11T00:51:08.55Z" + "start": "2018-05-11T00:51:08.55Z", + "type": [ + "connection" + ] }, "flow": { "id": "KvZZ7LW-Qdc", @@ -936,6 +1086,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.150", + "192.168.100.111" + ] + }, "source": { "bytes": 58, "ip": "192.168.100.150", @@ -958,12 +1114,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 2020000000, "end": "2018-05-11T00:54:06.21Z", "kind": "event", - "start": "2018-05-11T00:54:04.19Z" + "start": "2018-05-11T00:54:04.19Z", + "type": [ + "connection" + ] }, "flow": { "id": "PC3a5T13Dpw", @@ -1006,6 +1168,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.111", + "192.168.100.150" + ] + }, "source": { "bytes": 1071, "ip": "192.168.100.111", @@ -1028,12 +1196,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 2020000000, "end": "2018-05-11T00:54:06.21Z", "kind": "event", - "start": "2018-05-11T00:54:04.19Z" + "start": "2018-05-11T00:54:04.19Z", + "type": [ + "connection" + ] }, "flow": { "id": "PC3a5T13Dpw", @@ -1076,6 +1250,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.150", + "192.168.100.111" + ] + }, "source": { "bytes": 1147, "ip": "192.168.100.150", @@ -1098,12 +1278,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 4000000000, "end": "2018-05-11T00:54:00.19Z", "kind": "event", - "start": "2018-05-11T00:53:56.19Z" + "start": "2018-05-11T00:53:56.19Z", + "type": [ + "connection" + ] }, "flow": { "id": "zdGWMwGlfsg", @@ -1146,6 +1332,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.111", + "192.168.100.150" + ] + }, "source": { "bytes": 1980, "ip": "192.168.100.111", @@ -1168,12 +1360,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-11T00:54:11Z", "duration": 4000000000, "end": "2018-05-11T00:54:00.19Z", "kind": "event", - "start": "2018-05-11T00:53:56.19Z" + "start": "2018-05-11T00:53:56.19Z", + "type": [ + "connection" + ] }, "flow": { "id": "zdGWMwGlfsg", @@ -1216,6 +1414,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.100.150", + "192.168.100.111" + ] + }, "source": { "bytes": 2164, "ip": "192.168.100.150", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C-Netstream-with-varstring.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C-Netstream-with-varstring.golden.json index d40a60441f9..93ea138cbac 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C-Netstream-with-varstring.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C-Netstream-with-varstring.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-07-18T01:35:35Z", "duration": 29695000000, "end": "2018-07-18T01:35:02.969Z", "kind": "event", - "start": "2018-07-18T01:34:33.274Z" + "start": "2018-07-18T01:34:33.274Z", + "type": [ + "connection" + ] }, "flow": { "id": "dK1E5m-O-ns", @@ -70,6 +76,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "20.20.20.20", + "20.20.255.255" + ] + }, "source": { "bytes": 702, "ip": "20.20.20.20", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C.golden.json index fee2e5d4e03..5884e5c3a85 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-H3C.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 89519000000, "end": "2018-05-21T09:25:03.677Z", "kind": "event", - "start": "2018-05-21T09:23:34.158Z" + "start": "2018-05-21T09:23:34.158Z", + "type": [ + "connection" + ] }, "flow": { "id": "6gDDasxO-4o", @@ -69,6 +75,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.30", + "10.22.163.21" + ] + }, "source": { "bytes": 1027087, "ip": "10.22.166.30", @@ -91,12 +103,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 60005000000, "end": "2018-05-21T09:25:03.662Z", "kind": "event", - "start": "2018-05-21T09:24:03.657Z" + "start": "2018-05-21T09:24:03.657Z", + "type": [ + "connection" + ] }, "flow": { "id": "RJbWY0zxttI", @@ -148,6 +166,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.12", + "10.21.3.172" + ] + }, "source": { "bytes": 6200, "ip": "10.22.166.12", @@ -170,12 +194,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 60016000000, "end": "2018-05-21T09:25:03.656Z", "kind": "event", - "start": "2018-05-21T09:24:03.64Z" + "start": "2018-05-21T09:24:03.64Z", + "type": [ + "connection" + ] }, "flow": { "id": "MfdYhUDA3Y4", @@ -227,6 +257,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.33", + "10.22.178.37" + ] + }, "source": { "bytes": 11896, "ip": "10.22.166.33", @@ -249,12 +285,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 90011000000, "end": "2018-05-21T09:25:03.643Z", "kind": "event", - "start": "2018-05-21T09:23:33.632Z" + "start": "2018-05-21T09:23:33.632Z", + "type": [ + "connection" + ] }, "flow": { "id": "_QFogYw9xiY", @@ -306,6 +348,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.35", + "10.20.100.253" + ] + }, "source": { "bytes": 1041, "ip": "10.22.166.35", @@ -328,12 +376,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 30000000000, "end": "2018-05-21T09:24:03.629Z", "kind": "event", - "start": "2018-05-21T09:23:33.629Z" + "start": "2018-05-21T09:23:33.629Z", + "type": [ + "connection" + ] }, "flow": { "id": "-O7eEnuq5LI", @@ -385,6 +439,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.36", + "10.20.136.36" + ] + }, "source": { "bytes": 1740, "ip": "10.22.166.36", @@ -407,12 +467,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 29467000000, "end": "2018-05-21T09:24:03.669Z", "kind": "event", - "start": "2018-05-21T09:23:34.202Z" + "start": "2018-05-21T09:23:34.202Z", + "type": [ + "connection" + ] }, "flow": { "id": "pcgnaJ3iCvI", @@ -464,6 +530,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.36", + "10.20.147.28" + ] + }, "source": { "bytes": 2998, "ip": "10.22.166.36", @@ -486,12 +558,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 29452000000, "end": "2018-05-21T09:24:03.67Z", "kind": "event", - "start": "2018-05-21T09:23:34.218Z" + "start": "2018-05-21T09:23:34.218Z", + "type": [ + "connection" + ] }, "flow": { "id": "_gbuwRW4AVE", @@ -543,6 +621,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.28", + "10.20.141.16" + ] + }, "source": { "bytes": 55773, "ip": "10.22.166.28", @@ -565,12 +649,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 29449000000, "end": "2018-05-21T09:24:03.684Z", "kind": "event", - "start": "2018-05-21T09:23:34.235Z" + "start": "2018-05-21T09:23:34.235Z", + "type": [ + "connection" + ] }, "flow": { "id": "VOe0rUor-cg", @@ -622,6 +712,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.35", + "10.20.162.17" + ] + }, "source": { "bytes": 3239438, "ip": "10.22.166.35", @@ -644,12 +740,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 30000000000, "end": "2018-05-21T09:24:03.685Z", "kind": "event", - "start": "2018-05-21T09:23:33.685Z" + "start": "2018-05-21T09:23:33.685Z", + "type": [ + "connection" + ] }, "flow": { "id": "nkp7tr2MVcs", @@ -701,6 +803,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.15", + "10.20.171.36" + ] + }, "source": { "bytes": 5701, "ip": "10.22.166.15", @@ -723,12 +831,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 29391000000, "end": "2018-05-21T09:24:03.691Z", "kind": "event", - "start": "2018-05-21T09:23:34.3Z" + "start": "2018-05-21T09:23:34.3Z", + "type": [ + "connection" + ] }, "flow": { "id": "WxCFEmsTIh0", @@ -780,6 +894,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.2", + "10.22.208.12" + ] + }, "source": { "bytes": 4255012, "ip": "10.22.166.2", @@ -802,12 +922,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 29196000000, "end": "2018-05-21T09:24:03.699Z", "kind": "event", - "start": "2018-05-21T09:23:34.503Z" + "start": "2018-05-21T09:23:34.503Z", + "type": [ + "connection" + ] }, "flow": { "id": "rAIv2psXy74", @@ -859,6 +985,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.28", + "10.22.196.21" + ] + }, "source": { "bytes": 37557, "ip": "10.22.166.28", @@ -881,12 +1013,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 30000000000, "end": "2018-05-21T09:24:03.753Z", "kind": "event", - "start": "2018-05-21T09:23:33.753Z" + "start": "2018-05-21T09:23:33.753Z", + "type": [ + "connection" + ] }, "flow": { "id": "lR18K-eSVNM", @@ -938,6 +1076,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.25", + "10.22.202.15" + ] + }, "source": { "bytes": 23676, "ip": "10.22.166.25", @@ -960,12 +1104,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 89282000000, "end": "2018-05-21T09:25:03.971Z", "kind": "event", - "start": "2018-05-21T09:23:34.689Z" + "start": "2018-05-21T09:23:34.689Z", + "type": [ + "connection" + ] }, "flow": { "id": "1XCFo-Jv19g", @@ -1017,6 +1167,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.25", + "10.20.166.26" + ] + }, "source": { "bytes": 22821, "ip": "10.22.166.25", @@ -1039,12 +1195,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 90012000000, "end": "2018-05-21T09:25:03.95Z", "kind": "event", - "start": "2018-05-21T09:23:33.938Z" + "start": "2018-05-21T09:23:33.938Z", + "type": [ + "connection" + ] }, "flow": { "id": "DkV-9Meb8W8", @@ -1096,6 +1258,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.12", + "10.21.3.117" + ] + }, "source": { "bytes": 526, "ip": "10.22.166.12", @@ -1118,12 +1286,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 60005000000, "end": "2018-05-21T09:25:03.938Z", "kind": "event", - "start": "2018-05-21T09:24:03.933Z" + "start": "2018-05-21T09:24:03.933Z", + "type": [ + "connection" + ] }, "flow": { "id": "v1m_MeAqdL4", @@ -1175,6 +1349,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.17", + "10.22.145.26" + ] + }, "source": { "bytes": 33129, "ip": "10.22.166.17", @@ -1197,12 +1377,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-05-21T09:25:04Z", "duration": 60006000000, "end": "2018-05-21T09:25:03.928Z", "kind": "event", - "start": "2018-05-21T09:24:03.922Z" + "start": "2018-05-21T09:24:03.922Z", + "type": [ + "connection" + ] }, "flow": { "id": "ru0mPvG-tKw", @@ -1254,6 +1440,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.22.166.36", + "10.21.75.38" + ] + }, "source": { "bytes": 5092, "ip": "10.22.166.36", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Huawei-Netstream.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Huawei-Netstream.golden.json index 99db67a6ed8..231fd5e8797 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Huawei-Netstream.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Huawei-Netstream.golden.json @@ -13,12 +13,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-01-29T03:02:20Z", "duration": 327060000000, "end": "2018-01-29T03:02:19Z", "kind": "event", - "start": "2018-01-29T02:56:51.94Z" + "start": "2018-01-29T02:56:51.94Z", + "type": [ + "connection" + ] }, "flow": { "id": "d-FUjj8eKi8", @@ -70,6 +76,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.108.219.53", + "10.111.112.204" + ] + }, "source": { "bytes": 200, "ip": "10.108.219.53", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-IE150-IE151.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-IE150-IE151.golden.json index 55e02042c28..326828e8304 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-IE150-IE151.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-IE150-IE151.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-12-01T17:04:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "X6k2SQeAX5c", @@ -56,6 +62,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.3", + "192.168.0.2" + ] + }, "source": { "bytes": 78, "ip": "192.168.0.3", @@ -78,9 +90,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-12-01T17:04:39Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "XEzNKvE_H1k", @@ -122,6 +140,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.0.4", + "192.168.0.5" + ] + }, "source": { "bytes": 232, "ip": "192.168.0.4", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-1-flowset-in-large-zero-filled-packet.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-1-flowset-in-large-zero-filled-packet.golden.json index 35439694199..c5b53c195be 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-1-flowset-in-large-zero-filled-packet.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-1-flowset-in-large-zero-filled-packet.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-06-06T13:20:17Z", "duration": 0, "end": "2018-06-06T13:20:02Z", "kind": "event", - "start": "2018-06-06T13:20:02Z" + "start": "2018-06-06T13:20:02Z", + "type": [ + "connection" + ] }, "flow": { "id": "A-NpGXd6eh4", @@ -62,6 +68,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "134.220.2.6", + "134.220.1.156" + ] + }, "source": { "bytes": 363, "ip": "134.220.2.6", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-PAN--OS-with-app--id.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-PAN--OS-with-app--id.golden.json index 78e7d67a489..754241fb7e0 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-PAN--OS-with-app--id.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Palo-Alto-PAN--OS-with-app--id.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 0, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:39:31Z" + "start": "2017-11-13T14:39:31Z", + "type": [ + "connection" + ] }, "flow": { "id": "0HZ2F4aNlps", @@ -62,6 +68,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "23.35.171.27", + "10.32.91.205" + ] + }, "source": { "bytes": 70, "ip": "23.35.171.27", @@ -84,12 +96,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 339000000000, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:33:52Z" + "start": "2017-11-13T14:33:52Z", + "type": [ + "connection" + ] }, "flow": { "id": "GTu1zsDt3yw", @@ -134,6 +152,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.32.105.103", + "162.115.24.30" + ] + }, "source": { "bytes": 111, "ip": "10.32.105.103", @@ -156,12 +180,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 0, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:39:31Z" + "start": "2017-11-13T14:39:31Z", + "type": [ + "connection" + ] }, "flow": { "id": "nUCuFEB8z_c", @@ -206,6 +236,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.32.144.145", + "34.202.173.126" + ] + }, "source": { "bytes": 70, "ip": "10.32.144.145", @@ -228,12 +264,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 0, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:39:31Z" + "start": "2017-11-13T14:39:31Z", + "type": [ + "connection" + ] }, "flow": { "id": "inYZm0Y9EVM", @@ -278,6 +320,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "23.209.52.99", + "10.130.145.44" + ] + }, "source": { "bytes": 70, "ip": "23.209.52.99", @@ -300,12 +348,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 0, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:39:31Z" + "start": "2017-11-13T14:39:31Z", + "type": [ + "connection" + ] }, "flow": { "id": "6vds_sLxXqE", @@ -350,6 +404,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.50.97.57", + "10.50.96.20" + ] + }, "source": { "bytes": 78, "ip": "10.50.97.57", @@ -372,12 +432,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 0, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:39:31Z" + "start": "2017-11-13T14:39:31Z", + "type": [ + "connection" + ] }, "flow": { "id": "6vds_sLxXqE", @@ -422,6 +488,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.50.96.20", + "10.50.97.57" + ] + }, "source": { "bytes": 78, "ip": "10.50.96.20", @@ -444,12 +516,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 0, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:39:31Z" + "start": "2017-11-13T14:39:31Z", + "type": [ + "connection" + ] }, "flow": { "id": "v3XVGdLaIe4", @@ -494,6 +572,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "34.234.173.147", + "10.48.208.209" + ] + }, "source": { "bytes": 70, "ip": "34.234.173.147", @@ -516,12 +600,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-11-13T14:39:31Z", "duration": 0, "end": "2017-11-13T14:39:31Z", "kind": "event", - "start": "2017-11-13T14:39:31Z" + "start": "2017-11-13T14:39:31Z", + "type": [ + "connection" + ] }, "flow": { "id": "aenMB9Z5Tzc", @@ -566,6 +656,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.130.167.43", + "65.52.108.254" + ] + }, "source": { "bytes": 70, "ip": "10.130.167.43", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Streamcore.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Streamcore.golden.json index 1a8a189fb03..0fd9b02e864 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Streamcore.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Streamcore.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-01-11T11:48:15Z", "duration": 6012000000, "end": "2017-01-11T11:47:28.879Z", "kind": "event", - "start": "2017-01-11T11:47:22.867Z" + "start": "2017-01-11T11:47:22.867Z", + "type": [ + "connection" + ] }, "flow": { "id": "wdxUeEaOBho", @@ -57,6 +63,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "100.78.40.201", + "10.231.128.150" + ] + }, "source": { "bytes": 128, "ip": "100.78.40.201", @@ -79,12 +91,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-01-11T11:48:15Z", "duration": 6020000000, "end": "2017-01-11T11:47:28.886Z", "kind": "event", - "start": "2017-01-11T11:47:22.866Z" + "start": "2017-01-11T11:47:22.866Z", + "type": [ + "connection" + ] }, "flow": { "id": "wdxUeEaOBho", @@ -124,6 +142,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.231.128.150", + "100.78.40.201" + ] + }, "source": { "bytes": 172, "ip": "10.231.128.150", @@ -146,12 +170,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-01-11T11:23:51Z", "duration": 50997000000, "end": "2017-01-11T11:23:34.936Z", "kind": "event", - "start": "2017-01-11T11:22:43.939Z" + "start": "2017-01-11T11:22:43.939Z", + "type": [ + "connection" + ] }, "flow": { "id": "6_Ia6lqx2cg", @@ -191,6 +221,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "100.78.40.201", + "10.27.8.20" + ] + }, "source": { "bytes": 3943, "ip": "100.78.40.201", @@ -213,12 +249,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2017-01-11T11:23:51Z", "duration": 51015000000, "end": "2017-01-11T11:23:34.954Z", "kind": "event", - "start": "2017-01-11T11:22:43.939Z" + "start": "2017-01-11T11:22:43.939Z", + "type": [ + "connection" + ] }, "flow": { "id": "6_Ia6lqx2cg", @@ -258,6 +300,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.27.8.20", + "100.78.40.201" + ] + }, "source": { "bytes": 3052, "ip": "10.27.8.20", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Ubiquiti-Edgerouter-with-MPLS-labels.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Ubiquiti-Edgerouter-with-MPLS-labels.golden.json index 06dc8e8c35c..cbfc95d8e22 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Ubiquiti-Edgerouter-with-MPLS-labels.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-Ubiquiti-Edgerouter-with-MPLS-labels.golden.json @@ -13,12 +13,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 0, "end": "2016-09-10T16:17:25.825Z", "kind": "event", - "start": "2016-09-10T16:17:25.825Z" + "start": "2016-09-10T16:17:25.825Z", + "type": [ + "connection" + ] }, "flow": { "id": "KYJ6RiyA5YM", @@ -65,6 +71,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.1.0.135", + "10.4.0.251" + ] + }, "source": { "bytes": 174, "ip": "10.1.0.135", @@ -89,12 +101,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 0, "end": "2016-09-10T16:17:25.825Z", "kind": "event", - "start": "2016-09-10T16:17:25.825Z" + "start": "2016-09-10T16:17:25.825Z", + "type": [ + "connection" + ] }, "flow": { "id": "4GHcyowN7sg", @@ -141,6 +159,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.1.0.136", + "10.4.0.251" + ] + }, "source": { "bytes": 87, "ip": "10.1.0.136", @@ -165,12 +189,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 140227000000, "end": "2016-09-10T15:22:30.891Z", "kind": "event", - "start": "2016-09-10T15:20:10.664Z" + "start": "2016-09-10T15:20:10.664Z", + "type": [ + "connection" + ] }, "flow": { "id": "GRn2z1Rao3c", @@ -217,6 +247,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.1.0.232", + "10.4.0.251" + ] + }, "source": { "bytes": 1920, "ip": "10.1.0.232", @@ -241,12 +277,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 140227000000, "end": "2016-09-10T15:22:30.891Z", "kind": "event", - "start": "2016-09-10T15:20:10.664Z" + "start": "2016-09-10T15:20:10.664Z", + "type": [ + "connection" + ] }, "flow": { "id": "iHA6jdIkqjA", @@ -293,6 +335,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.1.0.232", + "10.4.0.251" + ] + }, "source": { "bytes": 610, "ip": "10.1.0.232", @@ -317,12 +365,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 177102000000, "end": "2016-09-10T16:20:32.763Z", "kind": "event", - "start": "2016-09-10T16:17:35.661Z" + "start": "2016-09-10T16:17:35.661Z", + "type": [ + "connection" + ] }, "flow": { "id": "cBjtKefzGos", @@ -369,6 +423,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.5.0.91", + "10.4.0.251" + ] + }, "source": { "bytes": 2420, "ip": "10.5.0.91", @@ -393,12 +453,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 176903000000, "end": "2016-09-10T16:20:32.666Z", "kind": "event", - "start": "2016-09-10T16:17:35.763Z" + "start": "2016-09-10T16:17:35.763Z", + "type": [ + "connection" + ] }, "flow": { "id": "EzT0lQWYBRw", @@ -445,6 +511,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.1.0.30", + "10.4.0.251" + ] + }, "source": { "bytes": 10204, "ip": "10.1.0.30", @@ -469,12 +541,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 0, "end": "2016-09-10T15:22:36.207Z", "kind": "event", - "start": "2016-09-10T15:22:36.207Z" + "start": "2016-09-10T15:22:36.207Z", + "type": [ + "connection" + ] }, "flow": { "id": "TROGwofkmJA", @@ -521,6 +599,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.3.0.100", + "10.4.0.251" + ] + }, "source": { "bytes": 216, "ip": "10.3.0.100", @@ -545,12 +629,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:23:30Z", "duration": 0, "end": "2016-09-10T16:17:35.661Z", "kind": "event", - "start": "2016-09-10T16:17:35.661Z" + "start": "2016-09-10T16:17:35.661Z", + "type": [ + "connection" + ] }, "flow": { "id": "wLclDbADA9s", @@ -597,6 +687,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.1.0.135", + "10.4.0.251" + ] + }, "source": { "bytes": 152, "ip": "10.1.0.135", @@ -620,12 +716,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 116000000, "end": "2016-09-10T15:23:38.951Z", "kind": "event", - "start": "2016-09-10T15:23:38.835Z" + "start": "2016-09-10T15:23:38.835Z", + "type": [ + "connection" + ] }, "flow": { "id": "LpdyE0SSB-o", @@ -672,6 +774,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.98", + "10.0.0.73" + ] + }, "source": { "bytes": 260, "ip": "192.168.1.98", @@ -694,12 +802,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 0, "end": "2016-09-10T16:18:39.443Z", "kind": "event", - "start": "2016-09-10T16:18:39.443Z" + "start": "2016-09-10T16:18:39.443Z", + "type": [ + "connection" + ] }, "flow": { "id": "32P6av-L8P0", @@ -746,6 +860,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.4.0.251", + "255.255.255.255" + ] + }, "source": { "bytes": 32, "ip": "10.4.0.251", @@ -768,12 +888,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 0, "end": "2016-09-10T16:18:39.443Z", "kind": "event", - "start": "2016-09-10T16:18:39.443Z" + "start": "2016-09-10T16:18:39.443Z", + "type": [ + "connection" + ] }, "flow": { "id": "ft_m5C7Hgpo", @@ -820,6 +946,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.4.0.251", + "255.255.255.255" + ] + }, "source": { "bytes": 135, "ip": "10.4.0.251", @@ -842,12 +974,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 0, "end": "2016-09-10T16:18:39.443Z", "kind": "event", - "start": "2016-09-10T16:18:39.443Z" + "start": "2016-09-10T16:18:39.443Z", + "type": [ + "connection" + ] }, "flow": { "id": "bVX88Ii80AQ", @@ -894,6 +1032,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.4.0.251", + "255.255.255.255" + ] + }, "source": { "bytes": 135, "ip": "10.4.0.251", @@ -916,12 +1060,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 0, "end": "2016-09-10T16:18:39.443Z", "kind": "event", - "start": "2016-09-10T16:18:39.443Z" + "start": "2016-09-10T16:18:39.443Z", + "type": [ + "connection" + ] }, "flow": { "id": "bA4nBN4veuI", @@ -968,6 +1118,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.4.0.251", + "255.255.255.255" + ] + }, "source": { "bytes": 135, "ip": "10.4.0.251", @@ -990,12 +1146,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 0, "end": "2016-09-10T16:18:39.443Z", "kind": "event", - "start": "2016-09-10T16:18:39.443Z" + "start": "2016-09-10T16:18:39.443Z", + "type": [ + "connection" + ] }, "flow": { "id": "lY5yfRKXE3s", @@ -1042,6 +1204,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.4.0.251", + "255.255.255.255" + ] + }, "source": { "bytes": 135, "ip": "10.4.0.251", @@ -1064,12 +1232,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 0, "end": "2016-09-10T16:18:39.443Z", "kind": "event", - "start": "2016-09-10T16:18:39.443Z" + "start": "2016-09-10T16:18:39.443Z", + "type": [ + "connection" + ] }, "flow": { "id": "x3GfEtY3zCQ", @@ -1116,6 +1290,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.4.0.251", + "255.255.255.255" + ] + }, "source": { "bytes": 135, "ip": "10.4.0.251", @@ -1138,12 +1318,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-09-10T16:24:08Z", "duration": 1250988000000, "end": "2016-09-10T15:23:44.363Z", "kind": "event", - "start": "2016-09-10T15:02:53.375Z" + "start": "2016-09-10T15:02:53.375Z", + "type": [ + "connection" + ] }, "flow": { "id": "bfT831bq5AI", @@ -1190,6 +1376,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.102", + "10.2.0.95" + ] + }, "source": { "bytes": 3668, "ip": "192.168.1.102", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-field-layer2segmentid.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-field-layer2segmentid.golden.json index c8e2f1e00f5..a3f5a4e3cc1 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-field-layer2segmentid.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-field-layer2segmentid.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-01-16T09:45:02Z", "duration": 0, "end": "2018-01-16T09:44:47Z", "kind": "event", - "start": "2018-01-16T09:44:47Z" + "start": "2018-01-16T09:44:47Z", + "type": [ + "connection" + ] }, "flow": { "id": "tS3zN7t_rFg", @@ -61,6 +67,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.200.136", + "80.82.237.40" + ] + }, "source": { "bytes": 52, "ip": "192.168.200.136", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-ipt_netflow-reduced-size-encoding.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-ipt_netflow-reduced-size-encoding.golden.json index ec7c0c9f8a7..106d624530a 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-ipt_netflow-reduced-size-encoding.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-ipt_netflow-reduced-size-encoding.golden.json @@ -13,12 +13,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 8996000000, "end": "2018-02-18T05:46:53.996Z", "kind": "event", - "start": "2018-02-18T05:46:45Z" + "start": "2018-02-18T05:46:45Z", + "type": [ + "connection" + ] }, "flow": { "id": "XLC-7u3wi0U", @@ -65,6 +71,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "37.122.1.226", + "193.151.198.166" + ] + }, "source": { "bytes": 156, "ip": "37.122.1.226", @@ -89,12 +101,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 0, "end": "2018-02-18T05:46:53.992Z", "kind": "event", - "start": "2018-02-18T05:46:53.992Z" + "start": "2018-02-18T05:46:53.992Z", + "type": [ + "connection" + ] }, "flow": { "id": "2mdiEm9z6pA", @@ -141,6 +159,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "5.141.231.166", + "193.151.199.69" + ] + }, "source": { "bytes": 48, "ip": "5.141.231.166", @@ -165,12 +189,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 7652000000, "end": "2018-02-18T05:46:53.988Z", "kind": "event", - "start": "2018-02-18T05:46:46.336Z" + "start": "2018-02-18T05:46:46.336Z", + "type": [ + "connection" + ] }, "flow": { "id": "IKsDJxZK5UA", @@ -217,6 +247,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.233.128.4", + "212.224.113.74" + ] + }, "source": { "bytes": 584, "ip": "10.233.128.4", @@ -241,12 +277,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 16000000, "end": "2018-02-18T05:46:53.992Z", "kind": "event", - "start": "2018-02-18T05:46:53.976Z" + "start": "2018-02-18T05:46:53.976Z", + "type": [ + "connection" + ] }, "flow": { "id": "lfpS1KL7LwI", @@ -293,6 +335,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "193.151.192.46", + "10.236.8.4" + ] + }, "source": { "bytes": 577, "ip": "193.151.192.46", @@ -317,12 +365,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 1168000000, "end": "2018-02-18T05:46:53.988Z", "kind": "event", - "start": "2018-02-18T05:46:52.82Z" + "start": "2018-02-18T05:46:52.82Z", + "type": [ + "connection" + ] }, "flow": { "id": "HRyho8QOr5M", @@ -369,6 +423,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.235.197.6", + "62.221.115.205" + ] + }, "source": { "bytes": 152, "ip": "10.235.197.6", @@ -393,12 +453,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 8992000000, "end": "2018-02-18T05:46:53.992Z", "kind": "event", - "start": "2018-02-18T05:46:45Z" + "start": "2018-02-18T05:46:45Z", + "type": [ + "connection" + ] }, "flow": { "id": "jbL3H_oK7ok", @@ -445,6 +511,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.236.31.7", + "37.146.125.64" + ] + }, "source": { "bytes": 152, "ip": "10.236.31.7", @@ -469,12 +541,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 4432000000, "end": "2018-02-18T05:46:53.992Z", "kind": "event", - "start": "2018-02-18T05:46:49.56Z" + "start": "2018-02-18T05:46:49.56Z", + "type": [ + "connection" + ] }, "flow": { "id": "ayKjfr1z0QU", @@ -521,6 +599,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.233.151.8", + "52.198.214.72" + ] + }, "source": { "bytes": 1809, "ip": "10.233.151.8", @@ -545,12 +629,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 80000000, "end": "2018-02-18T05:46:53.996Z", "kind": "event", - "start": "2018-02-18T05:46:53.916Z" + "start": "2018-02-18T05:46:53.916Z", + "type": [ + "connection" + ] }, "flow": { "id": "B15R8wv_tVI", @@ -597,6 +687,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.234.22.4", + "64.233.161.188" + ] + }, "source": { "bytes": 234, "ip": "10.234.22.4", @@ -621,12 +717,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 400000000, "end": "2018-02-18T05:46:53.992Z", "kind": "event", - "start": "2018-02-18T05:46:53.592Z" + "start": "2018-02-18T05:46:53.592Z", + "type": [ + "connection" + ] }, "flow": { "id": "oYN-uwp504w", @@ -673,6 +775,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.233.36.7", + "185.209.20.240" + ] + }, "source": { "bytes": 1681, "ip": "10.233.36.7", @@ -697,12 +805,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 9024000000, "end": "2018-02-18T05:46:53.988Z", "kind": "event", - "start": "2018-02-18T05:46:44.964Z" + "start": "2018-02-18T05:46:44.964Z", + "type": [ + "connection" + ] }, "flow": { "id": "MUPum_LUoxk", @@ -749,6 +863,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "10.233.200.7", + "84.39.245.175" + ] + }, "source": { "bytes": 152, "ip": "10.233.200.7", @@ -773,12 +893,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 60000000, "end": "2018-02-18T05:46:53.992Z", "kind": "event", - "start": "2018-02-18T05:46:53.932Z" + "start": "2018-02-18T05:46:53.932Z", + "type": [ + "connection" + ] }, "flow": { "id": "YStkNP0pV1E", @@ -825,6 +951,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "23.43.139.27", + "10.232.8.45" + ] + }, "source": { "bytes": 1866, "ip": "23.43.139.27", @@ -849,12 +981,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-02-18T05:47:09Z", "duration": 192000000, "end": "2018-02-18T05:46:53.992Z", "kind": "event", - "start": "2018-02-18T05:46:53.8Z" + "start": "2018-02-18T05:46:53.8Z", + "type": [ + "connection" + ] }, "flow": { "id": "nkastJ_vPI4", @@ -901,6 +1039,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "2.17.140.47", + "10.233.150.21" + ] + }, "source": { "bytes": 187, "ip": "2.17.140.47", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-macaddress.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-macaddress.golden.json index 0115c023fb6..c1857f85210 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-macaddress.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-macaddress.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:46:56Z", "kind": "event" }, @@ -47,9 +50,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "zQfsdfKgh-o", @@ -81,6 +90,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -103,9 +118,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Tw1iOKJ-dfE", @@ -137,6 +158,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.100" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -159,9 +186,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "NF1W3jyrHAA", @@ -193,6 +226,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.100", "locality": "private", @@ -215,9 +254,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B-_-kE8PEgA", @@ -249,6 +294,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -271,9 +322,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "B-_-kE8PEgA", @@ -305,6 +362,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -327,9 +390,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "q6jss8DvXWE", @@ -361,6 +430,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -383,9 +458,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "q6jss8DvXWE", @@ -417,6 +498,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -439,9 +526,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "3TmuMjQR8Mk", @@ -473,6 +566,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -495,9 +594,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "3TmuMjQR8Mk", @@ -529,6 +634,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -551,9 +662,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "2KDgFVtVKGg", @@ -585,6 +702,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -607,9 +730,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "2KDgFVtVKGg", @@ -641,6 +770,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -663,9 +798,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "vwr6dNcr6FE", @@ -697,6 +838,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -719,9 +866,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "vwr6dNcr6FE", @@ -753,6 +906,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -775,9 +934,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "tmgCubSF_CU", @@ -809,6 +974,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -831,9 +1002,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "tmgCubSF_CU", @@ -865,6 +1042,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -887,9 +1070,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Agzgga7RAr0", @@ -921,6 +1110,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -943,9 +1138,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Agzgga7RAr0", @@ -977,6 +1178,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -999,9 +1206,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "-cqFlm16mLc", @@ -1033,6 +1246,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -1055,9 +1274,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "-cqFlm16mLc", @@ -1089,6 +1314,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -1111,9 +1342,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Txfldw7-948", @@ -1145,6 +1382,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -1167,9 +1410,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Txfldw7-948", @@ -1201,6 +1450,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -1223,9 +1478,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "iaXg6w051Ho", @@ -1257,6 +1518,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -1279,9 +1546,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "iaXg6w051Ho", @@ -1313,6 +1586,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -1335,9 +1614,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "cEvEMCFhKJk", @@ -1369,6 +1654,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -1391,9 +1682,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "cEvEMCFhKJk", @@ -1425,6 +1722,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -1447,9 +1750,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "DnN0kX-gR3Q", @@ -1481,6 +1790,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -1503,9 +1818,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "DnN0kX-gR3Q", @@ -1537,6 +1858,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", @@ -1559,9 +1886,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "-kLcuxmRzgk", @@ -1593,6 +1926,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.1", + "172.16.32.201" + ] + }, "source": { "ip": "172.16.32.1", "locality": "private", @@ -1615,9 +1954,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-10T08:47:01Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "-kLcuxmRzgk", @@ -1649,6 +1994,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "ip": "172.16.32.201", "locality": "private", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-multiple-netflow-exporters.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-multiple-netflow-exporters.golden.json index e408d9f7488..cfec457a4f0 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-multiple-netflow-exporters.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-multiple-netflow-exporters.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:06:29Z", "kind": "event" }, @@ -46,12 +49,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:46.141Z", "kind": "event", - "start": "2015-10-08T19:03:46.14Z" + "start": "2015-10-08T19:03:46.14Z", + "type": [ + "connection" + ] }, "flow": { "id": "1E-M5OJg_go", @@ -92,6 +101,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.248" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.100", @@ -114,12 +129,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:46.141Z", "kind": "event", - "start": "2015-10-08T19:03:46.14Z" + "start": "2015-10-08T19:03:46.14Z", + "type": [ + "connection" + ] }, "flow": { "id": "yMxFd8CW_Ok", @@ -160,6 +181,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.248", + "172.16.32.100" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.248", @@ -182,12 +209,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:51.814Z", "kind": "event", - "start": "2015-10-08T19:03:51.813Z" + "start": "2015-10-08T19:03:51.813Z", + "type": [ + "connection" + ] }, "flow": { "id": "NF1W3jyrHAA", @@ -228,6 +261,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.201" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.100", @@ -250,12 +289,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:51.814Z", "kind": "event", - "start": "2015-10-08T19:03:51.813Z" + "start": "2015-10-08T19:03:51.813Z", + "type": [ + "connection" + ] }, "flow": { "id": "Tw1iOKJ-dfE", @@ -296,6 +341,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.100" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.201", @@ -318,12 +369,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 0, "end": "2015-10-08T19:03:55.958Z", "kind": "event", - "start": "2015-10-08T19:03:55.958Z" + "start": "2015-10-08T19:03:55.958Z", + "type": [ + "connection" + ] }, "flow": { "id": "sNF38-obC7k", @@ -364,6 +421,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.202" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.100", @@ -386,12 +449,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 0, "end": "2015-10-08T19:03:55.958Z", "kind": "event", - "start": "2015-10-08T19:03:55.958Z" + "start": "2015-10-08T19:03:55.958Z", + "type": [ + "connection" + ] }, "flow": { "id": "458D6voFu3E", @@ -432,6 +501,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.202", + "172.16.32.100" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.202", @@ -452,12 +527,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 38081000000, "end": "2015-10-08T19:04:25.9Z", "kind": "event", - "start": "2015-10-08T19:03:47.819Z" + "start": "2015-10-08T19:03:47.819Z", + "type": [ + "connection" + ] }, "flow": { "id": "tYpw8DU5u10", @@ -518,12 +599,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:06:29Z", "duration": 5000000, "end": "2015-10-08T19:05:55.015Z", "kind": "event", - "start": "2015-10-08T19:05:55.01Z" + "start": "2015-10-08T19:05:55.01Z", + "type": [ + "connection" + ] }, "flow": { "id": "zQfsdfKgh-o", @@ -568,6 +655,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.1" + ] + }, "source": { "bytes": 200, "ip": "172.16.32.201", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-nprobe-DPI-L7.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-nprobe-DPI-L7.golden.json index 69e0a14a66c..abf9608ce15 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-nprobe-DPI-L7.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-nprobe-DPI-L7.golden.json @@ -12,9 +12,15 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "1970-01-01T00:08:22Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "oFN7CMNpOLQ", @@ -52,6 +58,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "0.0.0.0", + "0.0.0.0" + ] + }, "source": { "bytes": 82, "ip": "0.0.0.0", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-options-template-with-scope-fields.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-options-template-with-scope-fields.golden.json index 49732550717..ee3ffd12ab2 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-options-template-with-scope-fields.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-options-template-with-scope-fields.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:06:29Z", "kind": "event" }, diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-template-with-0-length-fields.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-template-with-0-length-fields.golden.json index cf8c141f5c3..a1fa471443b 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-template-with-0-length-fields.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-template-with-0-length-fields.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:48.299Z", "kind": "event", - "start": "2016-12-23T01:34:48.299Z" + "start": "2016-12-23T01:34:48.299Z", + "type": [ + "connection" + ] }, "flow": { "id": "BSsjrf_TZnk", @@ -62,6 +68,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "239.255.255.250", + "192.168.1.80" + ] + }, "source": { "bytes": 0, "ip": "239.255.255.250", @@ -84,12 +96,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:48.299Z", "kind": "event", - "start": "2016-12-23T01:34:48.299Z" + "start": "2016-12-23T01:34:48.299Z", + "type": [ + "connection" + ] }, "flow": { "id": "R1Sjz_ITbgo", @@ -134,6 +152,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.80", + "239.255.255.250" + ] + }, "source": { "bytes": 0, "ip": "192.168.1.80", @@ -156,12 +180,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.469Z", "kind": "event", - "start": "2016-12-23T01:34:51.469Z" + "start": "2016-12-23T01:34:51.469Z", + "type": [ + "connection" + ] }, "flow": { "id": "FpUgB2PIhjQ", @@ -206,6 +236,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "239.255.255.250", + "192.168.1.95" + ] + }, "source": { "bytes": 0, "ip": "239.255.255.250", @@ -228,12 +264,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.469Z", "kind": "event", - "start": "2016-12-23T01:34:51.469Z" + "start": "2016-12-23T01:34:51.469Z", + "type": [ + "connection" + ] }, "flow": { "id": "qN8iQExOvkc", @@ -278,6 +320,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.95", + "239.255.255.250" + ] + }, "source": { "bytes": 32, "ip": "192.168.1.95", @@ -300,12 +348,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.469Z", "kind": "event", - "start": "2016-12-23T01:34:51.469Z" + "start": "2016-12-23T01:34:51.469Z", + "type": [ + "connection" + ] }, "flow": { "id": "FpUgB2PIhjQ", @@ -350,6 +404,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "239.255.255.250", + "192.168.1.95" + ] + }, "source": { "bytes": 0, "ip": "239.255.255.250", @@ -372,12 +432,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.469Z", "kind": "event", - "start": "2016-12-23T01:34:51.469Z" + "start": "2016-12-23T01:34:51.469Z", + "type": [ + "connection" + ] }, "flow": { "id": "qN8iQExOvkc", @@ -422,6 +488,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.95", + "239.255.255.250" + ] + }, "source": { "bytes": 0, "ip": "192.168.1.95", @@ -444,12 +516,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.569Z", "kind": "event", - "start": "2016-12-23T01:34:51.569Z" + "start": "2016-12-23T01:34:51.569Z", + "type": [ + "connection" + ] }, "flow": { "id": "WuFpyBG1Gt0", @@ -494,6 +572,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "239.255.255.250", + "192.168.1.33" + ] + }, "source": { "bytes": 0, "ip": "239.255.255.250", @@ -516,12 +600,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.569Z", "kind": "event", - "start": "2016-12-23T01:34:51.569Z" + "start": "2016-12-23T01:34:51.569Z", + "type": [ + "connection" + ] }, "flow": { "id": "1aysHUs7BpA", @@ -566,6 +656,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.33", + "239.255.255.250" + ] + }, "source": { "bytes": 32, "ip": "192.168.1.33", @@ -588,12 +684,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.569Z", "kind": "event", - "start": "2016-12-23T01:34:51.569Z" + "start": "2016-12-23T01:34:51.569Z", + "type": [ + "connection" + ] }, "flow": { "id": "WuFpyBG1Gt0", @@ -638,6 +740,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "239.255.255.250", + "192.168.1.33" + ] + }, "source": { "bytes": 0, "ip": "239.255.255.250", @@ -660,12 +768,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-12-23T01:35:31Z", "duration": 0, "end": "2016-12-23T01:34:51.569Z", "kind": "event", - "start": "2016-12-23T01:34:51.569Z" + "start": "2016-12-23T01:34:51.569Z", + "type": [ + "connection" + ] }, "flow": { "id": "1aysHUs7BpA", @@ -710,6 +824,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "192.168.1.33", + "239.255.255.250" + ] + }, "source": { "bytes": 0, "ip": "192.168.1.33", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-valid-01.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-valid-01.golden.json index bcea73e1dfa..2138b0b63fd 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-valid-01.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow-9-valid-01.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:46.141Z", "kind": "event", - "start": "2015-10-08T19:03:46.14Z" + "start": "2015-10-08T19:03:46.14Z", + "type": [ + "connection" + ] }, "flow": { "id": "1E-M5OJg_go", @@ -58,6 +64,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.248" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.100", @@ -80,12 +92,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:46.141Z", "kind": "event", - "start": "2015-10-08T19:03:46.14Z" + "start": "2015-10-08T19:03:46.14Z", + "type": [ + "connection" + ] }, "flow": { "id": "yMxFd8CW_Ok", @@ -126,6 +144,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.248", + "172.16.32.100" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.248", @@ -148,12 +172,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:51.814Z", "kind": "event", - "start": "2015-10-08T19:03:51.813Z" + "start": "2015-10-08T19:03:51.813Z", + "type": [ + "connection" + ] }, "flow": { "id": "NF1W3jyrHAA", @@ -194,6 +224,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.201" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.100", @@ -216,12 +252,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 1000000, "end": "2015-10-08T19:03:51.814Z", "kind": "event", - "start": "2015-10-08T19:03:51.813Z" + "start": "2015-10-08T19:03:51.813Z", + "type": [ + "connection" + ] }, "flow": { "id": "Tw1iOKJ-dfE", @@ -262,6 +304,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.201", + "172.16.32.100" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.201", @@ -284,12 +332,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 0, "end": "2015-10-08T19:03:55.958Z", "kind": "event", - "start": "2015-10-08T19:03:55.958Z" + "start": "2015-10-08T19:03:55.958Z", + "type": [ + "connection" + ] }, "flow": { "id": "sNF38-obC7k", @@ -330,6 +384,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.100", + "172.16.32.202" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.100", @@ -352,12 +412,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 0, "end": "2015-10-08T19:03:55.958Z", "kind": "event", - "start": "2015-10-08T19:03:55.958Z" + "start": "2015-10-08T19:03:55.958Z", + "type": [ + "connection" + ] }, "flow": { "id": "458D6voFu3E", @@ -398,6 +464,12 @@ "observer": { "ip": "192.0.2.1" }, + "related": { + "ip": [ + "172.16.32.202", + "172.16.32.100" + ] + }, "source": { "bytes": 76, "ip": "172.16.32.202", @@ -418,12 +490,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2015-10-08T19:04:30Z", "duration": 38081000000, "end": "2015-10-08T19:04:25.9Z", "kind": "event", - "start": "2015-10-08T19:03:47.819Z" + "start": "2015-10-08T19:03:47.819Z", + "type": [ + "connection" + ] }, "flow": { "id": "tYpw8DU5u10", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/Netflow9-Juniper-SRX-options-template-with-0-scope-field-length.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/Netflow9-Juniper-SRX-options-template-with-0-scope-field-length.golden.json index 23563687e00..1dc8829da2e 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/Netflow9-Juniper-SRX-options-template-with-0-scope-field-length.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/Netflow9-Juniper-SRX-options-template-with-0-scope-field-length.golden.json @@ -7,7 +7,10 @@ "Fields": { "event": { "action": "netflow_options", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2016-11-29T00:21:56Z", "kind": "event" }, diff --git a/x-pack/filebeat/input/netflow/testdata/golden/ipfix_cisco.pcap.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/ipfix_cisco.pcap.golden.json index 2ff196e7950..da28639ceae 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/ipfix_cisco.pcap.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/ipfix_cisco.pcap.golden.json @@ -17,7 +17,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -102,7 +105,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -187,7 +193,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -272,7 +281,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -357,7 +369,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -442,7 +457,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -527,7 +545,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -612,7 +633,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -697,7 +721,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -782,7 +809,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -867,7 +897,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -952,7 +985,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1037,7 +1073,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1122,7 +1161,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1207,7 +1249,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1292,7 +1337,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1377,7 +1425,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1462,7 +1513,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1547,7 +1601,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1632,7 +1689,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1717,7 +1777,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1802,7 +1865,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1887,7 +1953,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -1972,7 +2041,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -2057,7 +2129,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -2142,7 +2217,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -2227,7 +2305,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -2312,7 +2393,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", @@ -2397,7 +2481,10 @@ "action": "netflow_flow", "category": "network_session", "created": "2018-07-03T10:47:00Z", - "kind": "event" + "kind": "event", + "type": [ + "connection" + ] }, "flow": { "id": "Vhs9T5k296w", diff --git a/x-pack/filebeat/input/netflow/testdata/golden/netflow9_ubiquiti_edgerouter.pcap.golden.json b/x-pack/filebeat/input/netflow/testdata/golden/netflow9_ubiquiti_edgerouter.pcap.golden.json index 3bd1907cc87..0f198716213 100644 --- a/x-pack/filebeat/input/netflow/testdata/golden/netflow9_ubiquiti_edgerouter.pcap.golden.json +++ b/x-pack/filebeat/input/netflow/testdata/golden/netflow9_ubiquiti_edgerouter.pcap.golden.json @@ -12,12 +12,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 287000000, "end": "2018-08-09T16:43:00.307Z", "kind": "event", - "start": "2018-08-09T16:43:00.02Z" + "start": "2018-08-09T16:43:00.02Z", + "type": [ + "connection" + ] }, "flow": { "id": "NPZRWU1oZKQ", @@ -64,6 +70,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.5.2", + "159.65.125.168" + ] + }, "source": { "bytes": 421, "ip": "10.100.5.2", @@ -86,12 +98,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 30209000000, "end": "2018-08-09T16:43:01.317Z", "kind": "event", - "start": "2018-08-09T16:42:31.108Z" + "start": "2018-08-09T16:42:31.108Z", + "type": [ + "connection" + ] }, "flow": { "id": "wMmxEUF-2Sk", @@ -138,6 +156,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.6.93", + "13.32.251.125" + ] + }, "source": { "bytes": 7621, "ip": "10.100.6.93", @@ -160,12 +184,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 0, "end": "2018-08-09T16:43:01.41Z", "kind": "event", - "start": "2018-08-09T16:43:01.41Z" + "start": "2018-08-09T16:43:01.41Z", + "type": [ + "connection" + ] }, "flow": { "id": "2NG48p7EGpw", @@ -212,6 +242,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.4.1", + "10.100.6.80" + ] + }, "source": { "bytes": 95, "ip": "10.100.4.1", @@ -234,12 +270,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 59651000000, "end": "2018-08-09T16:43:02.334Z", "kind": "event", - "start": "2018-08-09T16:42:02.683Z" + "start": "2018-08-09T16:42:02.683Z", + "type": [ + "connection" + ] }, "flow": { "id": "f0LYEiUntL0", @@ -286,6 +328,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.6.93", + "13.32.251.8" + ] + }, "source": { "bytes": 3162, "ip": "10.100.6.93", @@ -308,12 +356,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 40015000000, "end": "2018-08-09T16:43:02.876Z", "kind": "event", - "start": "2018-08-09T16:42:22.861Z" + "start": "2018-08-09T16:42:22.861Z", + "type": [ + "connection" + ] }, "flow": { "id": "9ATz0HlBbIQ", @@ -360,6 +414,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.6.80", + "52.22.76.61" + ] + }, "source": { "bytes": 2711, "ip": "10.100.6.80", @@ -382,12 +442,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 37121000000, "end": "2018-08-09T16:43:02.43Z", "kind": "event", - "start": "2018-08-09T16:42:25.309Z" + "start": "2018-08-09T16:42:25.309Z", + "type": [ + "connection" + ] }, "flow": { "id": "vueGG5QVS_M", @@ -434,6 +500,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.6.93", + "13.32.251.125" + ] + }, "source": { "bytes": 20855, "ip": "10.100.6.93", @@ -456,12 +528,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 31322000000, "end": "2018-08-09T16:43:02.43Z", "kind": "event", - "start": "2018-08-09T16:42:31.108Z" + "start": "2018-08-09T16:42:31.108Z", + "type": [ + "connection" + ] }, "flow": { "id": "rJySLUBW94Y", @@ -508,6 +586,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.6.93", + "13.32.251.125" + ] + }, "source": { "bytes": 7495, "ip": "10.100.6.93", @@ -530,12 +614,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 31226000000, "end": "2018-08-09T16:43:02.334Z", "kind": "event", - "start": "2018-08-09T16:42:31.108Z" + "start": "2018-08-09T16:42:31.108Z", + "type": [ + "connection" + ] }, "flow": { "id": "pWQ3ZWUMRfU", @@ -582,6 +672,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.6.93", + "13.32.251.125" + ] + }, "source": { "bytes": 7049, "ip": "10.100.6.93", @@ -604,12 +700,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 30976000000, "end": "2018-08-09T16:43:02.334Z", "kind": "event", - "start": "2018-08-09T16:42:31.358Z" + "start": "2018-08-09T16:42:31.358Z", + "type": [ + "connection" + ] }, "flow": { "id": "M0l00u11bWc", @@ -656,6 +758,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "10.100.6.93", + "13.32.251.126" + ] + }, "source": { "bytes": 1348, "ip": "10.100.6.93", @@ -678,12 +786,18 @@ }, "event": { "action": "netflow_flow", - "category": "network_traffic", + "category": [ + "network_traffic", + "network" + ], "created": "2018-08-09T16:49:04Z", "duration": 0, "end": "2018-08-09T16:43:06.28Z", "kind": "event", - "start": "2018-08-09T16:43:06.28Z" + "start": "2018-08-09T16:43:06.28Z", + "type": [ + "connection" + ] }, "flow": { "id": "lzKTutEyrKA", @@ -730,6 +844,12 @@ "observer": { "ip": "10.100.4.1" }, + "related": { + "ip": [ + "192.168.1.4", + "10.100.0.1" + ] + }, "source": { "bytes": 82, "ip": "192.168.1.4", diff --git a/x-pack/filebeat/magefile.go b/x-pack/filebeat/magefile.go index f157b8b1a06..66d90e26e80 100644 --- a/x-pack/filebeat/magefile.go +++ b/x-pack/filebeat/magefile.go @@ -148,8 +148,6 @@ func includeList() error { // IntegTest executes integration tests (it uses Docker to run the tests). func IntegTest() { - devtools.AddIntegTestUsage() - defer devtools.StopIntegTestEnv() mg.SerialDeps(GoIntegTest, PythonIntegTest) } @@ -157,7 +155,11 @@ func IntegTest() { // Use TEST_COVERAGE=true to enable code coverage profiling. // Use RACE_DETECTOR=true to enable the race detector. func GoIntegTest(ctx context.Context) error { - return devtools.RunIntegTest("goIntegTest", func() error { + runner, err := devtools.NewDockerIntegrationRunner() + if err != nil { + return err + } + return runner.Test("goIntegTest", func() error { return devtools.GoTest(ctx, devtools.DefaultGoTestIntegrationArgs()) }) } @@ -170,10 +172,14 @@ func PythonIntegTest(ctx context.Context) error { if !devtools.IsInIntegTestEnv() { mg.Deps(Fields) } - return devtools.RunIntegTest("pythonIntegTest", func() error { + runner, err := devtools.NewDockerIntegrationRunner(append(devtools.ListMatchingEnvVars("TESTING_FILEBEAT_", "NOSE_"), "GENERATE")...) + if err != nil { + return err + } + return runner.Test("pythonIntegTest", func() error { mg.Deps(devtools.BuildSystemTestBinary) args := devtools.DefaultPythonTestIntegrationArgs() args.Env["MODULES_PATH"] = devtools.CWD("module") return devtools.PythonNoseTest(args) - }, append(devtools.ListMatchingEnvVars("TESTING_FILEBEAT_", "NOSE_"), "GENERATE")...) + }) } diff --git a/x-pack/filebeat/module/activemq/_meta/docs.asciidoc b/x-pack/filebeat/module/activemq/_meta/docs.asciidoc index 6e7d6d74551..f632747c8a4 100644 --- a/x-pack/filebeat/module/activemq/_meta/docs.asciidoc +++ b/x-pack/filebeat/module/activemq/_meta/docs.asciidoc @@ -5,8 +5,6 @@ == ActiveMQ module -ga[] - This module parses Apache ActiveMQ logs. It supports application and audit logs. include::../include/what-happens.asciidoc[] diff --git a/x-pack/filebeat/module/aws/cloudtrail/_meta/fields.epr.yml b/x-pack/filebeat/module/aws/cloudtrail/_meta/fields.epr.yml new file mode 100644 index 00000000000..91c417b502a --- /dev/null +++ b/x-pack/filebeat/module/aws/cloudtrail/_meta/fields.epr.yml @@ -0,0 +1,45 @@ +- name: event.action + type: keyword + description: The action captured by the event. +- name: event.original + type: keyword + description: Raw text message of entire event. Used to demonstrate log integrity. +- name: user.name + type: keyword + description: Short name or login of the user. +- name: user.id + type: keyword + description: Unique identifier of the user. +- name: cloud.account.id + type: keyword + description: The cloud account or organization id used to identify different entities in a multi-tenant environment. +- name: event.provider + type: keyword + description: Source of the event. +- name: cloud.region + type: keyword + description: Region in which this host is running. +- name: source.address + type: keyword + description: Some event source addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the .address field. +- name: source.ip + type: ip + description: IP address of the source (IPv4 or IPv6). +- name: user_agent.device.name + type: keyword + description: Name of the device. +- name: user_agent.name + type: keyword + description: Name of the user agent. +- name: user_agent.original + type: keyword + description: Unparsed user_agent string. +- name: related.user + type: keyword + description: All the user names seen on your event. +- name: event.kind + type: keyword + description: Event kind (e.g. event, alert, metric, state, pipeline_error, signal) +- name: event.type + type: keyword + description: Event severity (e.g. info, error) diff --git a/x-pack/filebeat/module/aws/cloudwatch/_meta/fields.yml b/x-pack/filebeat/module/aws/cloudwatch/_meta/fields.yml index 844c13309d6..7d80e27ed15 100644 --- a/x-pack/filebeat/module/aws/cloudwatch/_meta/fields.yml +++ b/x-pack/filebeat/module/aws/cloudwatch/_meta/fields.yml @@ -5,3 +5,7 @@ description: > Fields for AWS CloudWatch logs. fields: + - name: message + type: text + description: > + CloudWatch log message. diff --git a/x-pack/filebeat/module/aws/cloudwatch/ingest/pipeline.yml b/x-pack/filebeat/module/aws/cloudwatch/ingest/pipeline.yml index d1f65f3ba85..ff7e20d1c3d 100644 --- a/x-pack/filebeat/module/aws/cloudwatch/ingest/pipeline.yml +++ b/x-pack/filebeat/module/aws/cloudwatch/ingest/pipeline.yml @@ -4,8 +4,8 @@ processors: - grok: field: message patterns: - - "%{TIMESTAMP_ISO8601:_tmp.timestamp} %{SYSLOGTIMESTAMP:_tmp.syslog_timestamp} %{GREEDYDATA:message}" - - "%{TIMESTAMP_ISO8601:_tmp.timestamp} %{GREEDYDATA:message}" + - "%{TIMESTAMP_ISO8601:_tmp.timestamp} %{SYSLOGTIMESTAMP:_tmp.syslog_timestamp} %{GREEDYDATA:aws.cloudwatch.message}" + - "%{TIMESTAMP_ISO8601:_tmp.timestamp} %{GREEDYDATA:aws.cloudwatch.message}" - date: field: '_tmp.timestamp' diff --git a/x-pack/filebeat/module/aws/cloudwatch/test/cloudwatch_ec2.log-expected.json b/x-pack/filebeat/module/aws/cloudwatch/test/cloudwatch_ec2.log-expected.json index 11d33c51e0b..bdc8b0c3a72 100644 --- a/x-pack/filebeat/module/aws/cloudwatch/test/cloudwatch_ec2.log-expected.json +++ b/x-pack/filebeat/module/aws/cloudwatch/test/cloudwatch_ec2.log-expected.json @@ -1,62 +1,68 @@ [ { "@timestamp": "2020-02-20T07:01:01.000Z", + "aws.cloudwatch.message": "ip-172-31-81-156 systemd: Stopping User Slice of root.", "event.dataset": "aws.cloudwatch", "event.module": "aws", "fileset.name": "cloudwatch", "input.type": "log", "log.offset": 0, - "message": "ip-172-31-81-156 systemd: Stopping User Slice of root.", + "message": "2020-02-20T07:01:01.000Z Feb 20 07:01:01 ip-172-31-81-156 systemd: Stopping User Slice of root.", "service.type": "aws" }, { "@timestamp": "2020-02-20T07:02:18.000Z", + "aws.cloudwatch.message": "ip-172-31-81-156 dhclient[3000]: XMT: Solicit on eth0, interval 125240ms.", "event.dataset": "aws.cloudwatch", "event.module": "aws", "fileset.name": "cloudwatch", "input.type": "log", "log.offset": 96, - "message": "ip-172-31-81-156 dhclient[3000]: XMT: Solicit on eth0, interval 125240ms.", + "message": "2020-02-20T07:02:18.000Z Feb 20 07:02:18 ip-172-31-81-156 dhclient[3000]: XMT: Solicit on eth0, interval 125240ms.", "service.type": "aws" }, { "@timestamp": "2020-02-20T07:02:37.000Z", + "aws.cloudwatch.message": "ip-172-31-81-156 dhclient[2898]: DHCPREQUEST on eth0 to 172.31.80.1 port 67 (xid=0x4575af22)", "event.dataset": "aws.cloudwatch", "event.module": "aws", "fileset.name": "cloudwatch", "input.type": "log", "log.offset": 211, - "message": "ip-172-31-81-156 dhclient[2898]: DHCPREQUEST on eth0 to 172.31.80.1 port 67 (xid=0x4575af22)", + "message": "2020-02-20T07:02:37.000Z Feb 20 07:02:37 ip-172-31-81-156 dhclient[2898]: DHCPREQUEST on eth0 to 172.31.80.1 port 67 (xid=0x4575af22)", "service.type": "aws" }, { "@timestamp": "2020-02-20T07:02:37.000Z", + "aws.cloudwatch.message": "ip-172-31-81-156 dhclient[2898]: DHCPACK from 172.31.80.1 (xid=0x4575af22)", "event.dataset": "aws.cloudwatch", "event.module": "aws", "fileset.name": "cloudwatch", "input.type": "log", "log.offset": 345, - "message": "ip-172-31-81-156 dhclient[2898]: DHCPACK from 172.31.80.1 (xid=0x4575af22)", + "message": "2020-02-20T07:02:37.000Z Feb 20 07:02:37 ip-172-31-81-156 dhclient[2898]: DHCPACK from 172.31.80.1 (xid=0x4575af22)", "service.type": "aws" }, { "@timestamp": "2020-02-20T07:02:37.000Z", + "aws.cloudwatch.message": "ip-172-31-81-156 dhclient[2898]: bound to 172.31.81.156 -- renewal in 1599 seconds.", "event.dataset": "aws.cloudwatch", "event.module": "aws", "fileset.name": "cloudwatch", "input.type": "log", "log.offset": 461, - "message": "ip-172-31-81-156 dhclient[2898]: bound to 172.31.81.156 -- renewal in 1599 seconds.", + "message": "2020-02-20T07:02:37.000Z Feb 20 07:02:37 ip-172-31-81-156 dhclient[2898]: bound to 172.31.81.156 -- renewal in 1599 seconds.", "service.type": "aws" }, { "@timestamp": "2020-02-20T07:02:37.000Z", + "aws.cloudwatch.message": "ip-172-31-81-156 ec2net: [get_meta] Trying to get http://169.254.169.254/latest/meta-data/network/interfaces/macs/12:e2:a9:95:8b:97/local-ipv4s", "event.dataset": "aws.cloudwatch", "event.module": "aws", "fileset.name": "cloudwatch", "input.type": "log", "log.offset": 586, - "message": "ip-172-31-81-156 ec2net: [get_meta] Trying to get http://169.254.169.254/latest/meta-data/network/interfaces/macs/12:e2:a9:95:8b:97/local-ipv4s", + "message": "2020-02-20T07:02:37.000Z Feb 20 07:02:37 ip-172-31-81-156 ec2net: [get_meta] Trying to get http://169.254.169.254/latest/meta-data/network/interfaces/macs/12:e2:a9:95:8b:97/local-ipv4s", "service.type": "aws" } ] \ No newline at end of file diff --git a/x-pack/filebeat/module/aws/ec2/_meta/fields.epr.yml b/x-pack/filebeat/module/aws/ec2/_meta/fields.epr.yml new file mode 100644 index 00000000000..3a22e7a7e80 --- /dev/null +++ b/x-pack/filebeat/module/aws/ec2/_meta/fields.epr.yml @@ -0,0 +1,3 @@ +- name: process.name + type: keyword + description: Process name. diff --git a/x-pack/filebeat/module/aws/elb/_meta/fields.epr.yml b/x-pack/filebeat/module/aws/elb/_meta/fields.epr.yml new file mode 100644 index 00000000000..f548842e70f --- /dev/null +++ b/x-pack/filebeat/module/aws/elb/_meta/fields.epr.yml @@ -0,0 +1,78 @@ +- name: destination.domain + type: keyword + description: Destination domain. +- name: event.start + type: date + description: event.start contains the date when the event started or when the activity was first observed. +- name: destination.bytes + type: long + description: Bytes sent from the destination to the source. +- name: http.response.status_code + type: long + description: HTTP response status code. +- name: http.request.body.bytes + type: long + description: Size in bytes of the request body. +- name: http.response.body.bytes + type: long + description: Size in bytes of the response body. +- name: http.request.method + type: keyword + description: HTTP request method. +- name: http.request.referrer + type: keyword + description: Referrer for this HTTP request. +- name: http.version + type: keyword + description: HTTP version. +- name: user_agent.original + type: keyword + description: Unparsed user_agent string. +- name: cloud.provider + type: keyword + description: Name of the cloud provider. Example values are aws, azure, gcp, or digitalocean. +- name: event.kind + type: keyword + description: Event kind (e.g. event, alert, metric, state, pipeline_error, sig +- name: event.category + type: keyword + description: Event category (e.g. database) +- name: event.outcome + type: keyword + description: This is one of four ECS Categorization Fields, and indicates the lowest level in the ECS category hierarchy. +- name: tracing.trace.id + type: keyword + description: Unique identifier of the trace. +- name: event.end + type: date + description: event.end contains the date when the event ended or when the activity was last observed. +- name: source.ip + type: ip + description: IP address of the source. +- name: source.as.number + type: long + description: Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. +- name: source.as.organization.name + type: keyword + description: Organization name. +- name: source.geo.city_name + type: keyword + description: City name. +- name: source.geo.continent_name + type: keyword + description: Name of the continent. +- name: source.geo.country_iso_code + type: keyword + description: Country ISO code. +- name: source.geo.location + type: geo_point + description: Longitude and latitude. +- name: source.geo.region_iso_code + type: keyword + description: Region ISO code. +- name: source.geo.region_name + type: keyword + description: Region name. +- name: source.port + type: long + description: Port of the source. diff --git a/x-pack/filebeat/module/aws/elb/_meta/fields.yml b/x-pack/filebeat/module/aws/elb/_meta/fields.yml index 8e2597c2d09..9ddfb123901 100644 --- a/x-pack/filebeat/module/aws/elb/_meta/fields.yml +++ b/x-pack/filebeat/module/aws/elb/_meta/fields.yml @@ -98,5 +98,5 @@ The URL used if a redirection action was executed. - name: error.reason type: keyword - description: + description: > The error reason if the executed action failed. diff --git a/x-pack/filebeat/module/aws/fields.go b/x-pack/filebeat/module/aws/fields.go index b2ac0ac3729..22308cf2722 100644 --- a/x-pack/filebeat/module/aws/fields.go +++ b/x-pack/filebeat/module/aws/fields.go @@ -19,5 +19,5 @@ func init() { // AssetAws returns asset data. // This is the base64 encoded gzipped contents of module/aws. func AssetAws() string { - return "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" + return "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" } diff --git a/x-pack/filebeat/module/aws/s3access/_meta/fields.epr.yml b/x-pack/filebeat/module/aws/s3access/_meta/fields.epr.yml new file mode 100644 index 00000000000..5f5693a8279 --- /dev/null +++ b/x-pack/filebeat/module/aws/s3access/_meta/fields.epr.yml @@ -0,0 +1,90 @@ +- name: related.user + type: keyword + description: All the user names seen on your event. +- name: related.ip + type: ip + description: All of the IPs seen on your event. +- name: client.ip + type: ip + description: IP address of the client. +- name: client.address + type: keyword + description: Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the .address field. +- name: client.user.id + type: keyword + description: Unique identifiers of the user. +- name: event.id + type: keyword + description: Unique ID to describe the event. +- name: event.action + type: keyword + description: The action captured by the event. +- name: http.response.status_code + type: long + description: HTTP response status code. +- name: event.outcome + type: keyword + description: This is one of four ECS Categorization Fields, and indicates the lowest level in the ECS category hierarchy. +- name: event.code + type: keyword + description: Identification code for this event, if one exists. +- name: event.duration + type: long + description: Duration of the event in nanoseconds. +- name: http.request.referrer + type: keyword + description: Referrer for this HTTP request. +- name: tls.cipher + type: keyword + description: String indicating the cipher used during the current connection. +- name: tls.version + type: keyword + description: Numeric part of the version parsed from the original string. +- name: tls.version_protocol + type: keyword + description: Normalized lowercase protocol name parsed from original string. +- name: cloud.provider + type: keyword + description: Name of the cloud provider. Example values are aws, azure, gcp, or digitalocean. +- name: event.kind + type: keyword + description: Event kind (e.g. event, alert, metric, state, pipeline_error, signal) +- name: geo.city_name + type: keyword + description: City name. +- name: geo.continent_name + type: keyword + description: Name of the continent. +- name: geo.country_iso_code + type: keyword + description: Country ISO code. +- name: geo.location + type: geo_point + description: Longitude and latitude. +- name: geo.region_iso_code + type: keyword + description: Region ISO code. +- name: geo.region_name + type: keyword + description: Region name. +- name: user_agent.device.name + type: keyword + description: Name of the device. +- name: user_agent.name + type: keyword + description: Name of the user agent. +- name: user_agent.original + type: keyword + description: Unparsed user_agent string. +- name: user_agent.os.full + type: keyword + description: Operating system name, including the version or code name. +- name: user_agent.os.name + type: keyword + description: Operating system name, without the version. +- name: user_agent.os.version + type: keyword + description: Operating system version as a raw string. +- name: user_agent.version + type: keyword + description: Version of the user agent. diff --git a/x-pack/filebeat/module/aws/vpcflow/_meta/fields.epr.yml b/x-pack/filebeat/module/aws/vpcflow/_meta/fields.epr.yml new file mode 100644 index 00000000000..7293e8090ff --- /dev/null +++ b/x-pack/filebeat/module/aws/vpcflow/_meta/fields.epr.yml @@ -0,0 +1,123 @@ +- name: event.start + type: date + description: event.start contains the date when the event started or when the activity was first observed. +- name: event.end + type: date + description: event.end contains the date when the event ended or when the activity was last observed. +- name: destination.geo.continent_name + type: keyword + description: Name of the continent. +- name: destination.geo.country_iso_code + type: keyword + description: Country ISO code. +- name: destination.geo.location + type: geo_point + description: Longitude and latitude. +- name: destination.ip + type: ip + description: IP address of the destination. +- name: destination.address + type: keyword + description: Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the .address field. +- name: destination.port + type: long + description: Port of the destination. +- name: event.category + type: keyword + description: Event category (e.g. database) +- name: event.outcome + type: keyword + description: This is one of four ECS Categorization Fields, and indicates the lowest level in the ECS category hierarchy. +- name: event.type + type: keyword + description: Event severity (e.g. info, error) +- name: source.as.number + type: long + description: Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. +- name: source.as.organization.name + type: keyword + description: Organization name. +- name: destination.as.number + type: long + description: Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. +- name: destination.as.organization.name + type: keyword + description: Organization name. +- name: event.original + type: keyword + description: Raw text message of entire event. Used to demonstrate log integrity. +- name: cloud.account.id + type: keyword + description: The cloud account or organization id used to identify different entities in a multi-tenant environment. +- name: cloud.instance.id + type: keyword + description: Instance ID of the host machine. +- name: cloud.provider + type: keyword + description: Name of the cloud provider. +- name: related.ip + type: ip + description: All of the IPs seen on your event. +- name: event.kind + type: keyword + description: Event kind (e.g. event, alert, metric, state, pipeline_error, signal) +- name: cloud.account.id + type: keyword + description: The cloud account or organization id used to identify different entities in a multi-tenant environment. +- name: network.bytes + type: long + description: Total bytes transferred in both directions. +- name: network.community_id + type: keyword + description: A hash of source and destination IPs and ports, as well as the protocol used in a communication. This is a tool-agnostic standard to identify flows. +- name: network.iana_number + type: keyword + description: IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml). Standardized list of protocols. This aligns well with NetFlow and sFlow related logs which use the IANA Protocol Number. +- name: network.packets + type: long + description: Total packets transferred in both directions. +- name: network.transport + type: keyword + description: Same as network.iana_number, but instead using the Keyword name of the transport layer (udp, tcp, ipv6-icmp, etc.) +- name: network.type + type: keyword + description: In the OSI Model this would be the Network Layer. ipv4, ipv6, ipsec, pim, etc +- name: source.address + type: keyword + description: Some event source addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the .address field. +- name: source.as.number + type: long + description: Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet. +- name: source.as.organization.name + type: keyword + description: Organization name. +- name: source.bytes + type: long + description: Bytes sent from the source to the destination. +- name: source.geo.city_name + type: keyword + description: City name. +- name: source.geo.continent_name + type: keyword + description: Name of the continent. +- name: source.geo.country_iso_code + type: keyword + description: Country ISO code. +- name: source.geo.location + type: geo_point + description: Longitude and latitude. +- name: source.geo.region_iso_code + type: keyword + description: Region ISO code. +- name: source.geo.region_name + type: keyword + description: Region name. +- name: source.ip + type: ip + description: IP address of the source (IPv4 or IPv6). +- name: source.packets + type: long + description: Packets sent from the source to the destination. +- name: source.port + type: long + description: Port of the source. diff --git a/x-pack/filebeat/module/checkpoint/firewall/ingest/pipeline.json b/x-pack/filebeat/module/checkpoint/firewall/ingest/pipeline.json index cff272b7db1..e478d54e73d 100644 --- a/x-pack/filebeat/module/checkpoint/firewall/ingest/pipeline.json +++ b/x-pack/filebeat/module/checkpoint/firewall/ingest/pipeline.json @@ -61,9 +61,9 @@ } }, { - "set": { + "append": { "field": "event.category", - "value": ["network"], + "value": "network", "if": "ctx.checkpoint?.operation != 'Log In'" } }, @@ -242,9 +242,9 @@ } }, { - "set": { + "append": { "field": "event.category", - "value": ["authentication"], + "value": "authentication", "if": "ctx.checkpoint?.operation == 'Log In'" } }, @@ -270,7 +270,7 @@ } }, { - "set" : { + "append" : { "field": "event.type", "value": ["allowed", "connection"], "if": "['Accept', 'Allow'].contains(ctx.checkpoint?.rule_action)" @@ -298,7 +298,7 @@ } }, { - "set" : { + "append" : { "field": "event.type", "value": ["connection", "denied"], "if": "['Drop', 'Reject', 'Block', 'Prevent'].contains(ctx.checkpoint?.rule_action)" @@ -1028,6 +1028,68 @@ "if": "ctx.checkpoint?.sys_message != null" } }, + { + "geoip" : { + "field": "source.ip", + "target_field": "source.geo", + "ignore_missing": true, + "if": "ctx.source?.geo == null" + } + }, + { + "geoip" : { + "field": "destination.ip", + "target_field": "destination.geo", + "ignore_missing": true, + "if": "ctx.destination?.geo == null" + } + }, + { + "geoip" : { + "database_file": "GeoLite2-ASN.mmdb", + "field": "source.ip", + "target_field": "source.as", + "properties": ["asn", "organization_name"], + "ignore_missing": true + } + }, + { + "geoip" : { + "database_file": "GeoLite2-ASN.mmdb", + "field": "destination.ip", + "target_field": "destination.as", + "properties": ["asn", "organization_name"], + "ignore_missing": true + } + }, + { + "rename" : { + "field": "source.as.asn", + "target_field": "source.as.number", + "ignore_missing": true + } + }, + { + "rename" : { + "field": "source.as.organization_name", + "target_field": "source.as.organization.name", + "ignore_missing": true + } + }, + { + "rename" : { + "field": "destination.as.asn", + "target_field": "destination.as.number", + "ignore_missing": true + } + }, + { + "rename" : { + "field": "destination.as.organization_name", + "target_field": "destination.as.organization.name", + "ignore_missing": true + } + }, { "remove" : { "field": [ @@ -1050,4 +1112,4 @@ } } ] -} \ No newline at end of file +} diff --git a/x-pack/filebeat/module/checkpoint/firewall/test/checkpoint.log-expected.json b/x-pack/filebeat/module/checkpoint/firewall/test/checkpoint.log-expected.json index f966163307d..4e8517f4794 100644 --- a/x-pack/filebeat/module/checkpoint/firewall/test/checkpoint.log-expected.json +++ b/x-pack/filebeat/module/checkpoint/firewall/test/checkpoint.log-expected.json @@ -140,6 +140,15 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61794", + "destination.as.number": 25046, + "destination.as.organization.name": "Check Point Software Technologies LTD", + "destination.geo.city_name": "Tel Aviv", + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "IL", + "destination.geo.location.lat": 32.0678, + "destination.geo.location.lon": 34.7647, + "destination.geo.region_iso_code": "IL-TA", + "destination.geo.region_name": "Tel Aviv", "destination.ip": "194.29.39.10", "destination.port": "443", "event.action": "Accept", @@ -249,6 +258,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41566", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -358,6 +373,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48698", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -467,6 +488,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61150", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -576,6 +603,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "55110", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.81.142.43", "destination.port": "443", "event.action": "Accept", @@ -685,6 +718,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48718", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -794,6 +833,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62206", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -903,6 +948,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41596", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -1012,6 +1063,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61180", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -1121,6 +1178,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48732", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -1230,6 +1293,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62222", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -1339,6 +1408,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61188", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -1448,6 +1523,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41624", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -1557,6 +1638,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48758", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -1666,6 +1753,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62246", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -1775,6 +1868,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41638", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -1884,6 +1983,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61224", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -2037,6 +2142,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48776", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -2119,6 +2230,15 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "51436", + "destination.as.number": 25046, + "destination.as.organization.name": "Check Point Software Technologies LTD", + "destination.geo.city_name": "Tel Aviv", + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "IL", + "destination.geo.location.lat": 32.0678, + "destination.geo.location.lon": 34.7647, + "destination.geo.region_iso_code": "IL-TA", + "destination.geo.region_name": "Tel Aviv", "destination.ip": "194.29.39.47", "destination.port": "443", "event.action": "Accept", @@ -2334,6 +2454,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62396", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -2443,6 +2569,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48914", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -2552,6 +2684,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41844", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -2661,6 +2799,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62468", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -2770,6 +2914,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61434", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -2879,6 +3029,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41856", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -3032,6 +3188,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48990", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -3141,6 +3303,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62478", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -3250,6 +3418,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41864", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -3359,6 +3533,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61446", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -3468,6 +3648,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "48998", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -3524,6 +3710,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41870", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -3686,6 +3878,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62488", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -3795,6 +3993,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61454", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -3929,6 +4133,15 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62122", + "destination.as.number": 25046, + "destination.as.organization.name": "Check Point Software Technologies LTD", + "destination.geo.city_name": "Tel Aviv", + "destination.geo.continent_name": "Asia", + "destination.geo.country_iso_code": "IL", + "destination.geo.location.lat": 32.0678, + "destination.geo.location.lon": 34.7647, + "destination.geo.region_iso_code": "IL-TA", + "destination.geo.region_name": "Tel Aviv", "destination.ip": "194.29.39.10", "destination.port": "443", "event.action": "Accept", @@ -4091,6 +4304,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "55424", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.81.142.43", "destination.port": "443", "event.action": "Accept", @@ -4200,6 +4419,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "49026", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -4309,6 +4534,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62514", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -4418,6 +4649,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41902", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -4527,6 +4764,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61490", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -4636,6 +4879,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "49042", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", @@ -4745,6 +4994,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41914", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -4854,6 +5109,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "62534", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.41", "destination.port": "80", "event.action": "Accept", @@ -4963,6 +5224,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "61500", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.36", "destination.port": "80", "event.action": "Accept", @@ -5072,6 +5339,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "41938", + "destination.as.number": 16625, + "destination.as.organization.name": "Akamai Technologies, Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "104.99.234.45", "destination.port": "443", "event.action": "Accept", @@ -5181,6 +5454,12 @@ "checkpoint.rule_action": "Accept", "client.ip": "192.168.1.100", "client.port": "49102", + "destination.as.number": 30148, + "destination.as.organization.name": "Sucuri", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "192.124.249.31", "destination.port": "80", "event.action": "Accept", diff --git a/x-pack/filebeat/module/cisco/_meta/docs.asciidoc b/x-pack/filebeat/module/cisco/_meta/docs.asciidoc index f1a40037f6e..b72070d4918 100644 --- a/x-pack/filebeat/module/cisco/_meta/docs.asciidoc +++ b/x-pack/filebeat/module/cisco/_meta/docs.asciidoc @@ -289,7 +289,7 @@ parameters on your Elasticsearch cluster: - {ref}/circuit-breaker.html#script-compilation-circuit-breaker[script.max_compilations_rate]: Increase to at least `100/5m`. -- {ref}/modules-scripting-using.html#modules-scripting-using-caching[script.cache_max_size]: +- {ref}/modules-scripting-using.html#modules-scripting-using-caching[script.cache.max_size]: Increase to at least `200` if using both filesets or other script-heavy modules. [float] diff --git a/x-pack/filebeat/module/cisco/asa/test/asa-fix.log b/x-pack/filebeat/module/cisco/asa/test/asa-fix.log new file mode 100644 index 00000000000..00819e8eec1 --- /dev/null +++ b/x-pack/filebeat/module/cisco/asa/test/asa-fix.log @@ -0,0 +1,5 @@ +Apr 17 2020 14:08:08 SNL-ASA-VPN-A01 : %ASA-6-302016: Teardown UDP connection 110577675 for Outside:10.123.123.123/53723(LOCAL\Elastic) to Inside:10.233.123.123/53 duration 0:00:00 bytes 148 (zzzzzz) +Apr 17 2020 14:00:31 SNL-ASA-VPN-A01 : %ASA-4-106023: Deny icmp src Inside:10.123.123.123 dst Outside:10.123.123.123 (type 11, code 0) by access-group "Inside_access_in" [0x0, 0x0] +Apr 15 2013 09:36:50: %ASA-4-106023: Deny tcp src dmz:10.123.123.123/6316 dst outside:10.123.123.123/53 type 3, code 0, by access-group "acl_dmz" [0xe3afb522, 0x0] +Apr 17 2020 14:16:20 SNL-ASA-VPN-A01 : %ASA-4-106023: Deny udp src Inside:10.123.123.123/57621(LOCAL\Elastic) dst Outside:10.123.123.123/57621 by access-group "Inside_access_in" [0x0, 0x0] +Apr 17 2020 14:15:07 SNL-ASA-VPN-A01 : %ASA-2-106017: Deny IP due to Land Attack from 10.123.123.123 to 10.123.123.123 diff --git a/x-pack/filebeat/module/cisco/asa/test/asa-fix.log-expected.json b/x-pack/filebeat/module/cisco/asa/test/asa-fix.log-expected.json new file mode 100644 index 00000000000..de470786f66 --- /dev/null +++ b/x-pack/filebeat/module/cisco/asa/test/asa-fix.log-expected.json @@ -0,0 +1,152 @@ +[ + { + "cisco.asa.connection_id": "110577675", + "cisco.asa.destination_interface": "Inside", + "cisco.asa.message_id": "302016", + "cisco.asa.source_interface": "Outside", + "cisco.asa.source_username": "(LOCAL\\Elastic)", + "destination.address": "10.233.123.123", + "destination.ip": "10.233.123.123", + "destination.port": 53, + "event.action": "flow-expiration", + "event.code": 302016, + "event.dataset": "cisco.asa", + "event.duration": 0, + "event.end": "2020-04-17T14:08:08.000-02:00", + "event.module": "cisco", + "event.original": "%ASA-6-302016: Teardown UDP connection 110577675 for Outside:10.123.123.123/53723(LOCAL\\Elastic) to Inside:10.233.123.123/53 duration 0:00:00 bytes 148 (zzzzzz)", + "event.severity": 6, + "event.start": "2020-04-17T16:08:08.000Z", + "event.timezone": "-02:00", + "fileset.name": "asa", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "informational", + "log.offset": 0, + "network.bytes": 148, + "network.iana_number": 17, + "network.transport": "udp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "source.port": 53723, + "tags": [ + "cisco-asa" + ] + }, + { + "cisco.asa.destination_interface": "Outside", + "cisco.asa.message_id": "106023", + "cisco.asa.rule_name": "Inside_access_in", + "cisco.asa.source_interface": "Inside", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "event.action": "firewall-rule", + "event.code": 106023, + "event.dataset": "cisco.asa", + "event.module": "cisco", + "event.original": "%ASA-4-106023: Deny icmp src Inside:10.123.123.123 dst Outside:10.123.123.123 (type 11, code 0) by access-group \"Inside_access_in\" [0x0, 0x0]", + "event.outcome": "deny", + "event.severity": 4, + "event.timezone": "-02:00", + "fileset.name": "asa", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "warning", + "log.offset": 200, + "network.iana_number": 1, + "network.transport": "icmp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "tags": [ + "cisco-asa" + ] + }, + { + "cisco.asa.destination_interface": "outside", + "cisco.asa.message_id": "106023", + "cisco.asa.rule_name": "acl_dmz", + "cisco.asa.source_interface": "dmz", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "destination.port": 53, + "event.action": "firewall-rule", + "event.code": 106023, + "event.dataset": "cisco.asa", + "event.module": "cisco", + "event.original": "%ASA-4-106023: Deny tcp src dmz:10.123.123.123/6316 dst outside:10.123.123.123/53 type 3, code 0, by access-group \"acl_dmz\" [0xe3afb522, 0x0]", + "event.outcome": "deny", + "event.severity": 4, + "event.timezone": "-02:00", + "fileset.name": "asa", + "input.type": "log", + "log.level": "warning", + "log.offset": 381, + "network.iana_number": 6, + "network.transport": "tcp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "source.port": 6316, + "tags": [ + "cisco-asa" + ] + }, + { + "cisco.asa.destination_interface": "Outside", + "cisco.asa.message_id": "106023", + "cisco.asa.rule_name": "Inside_access_in", + "cisco.asa.source_interface": "Inside", + "cisco.asa.source_username": "(LOCAL\\Elastic)", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "destination.port": 57621, + "event.action": "firewall-rule", + "event.code": 106023, + "event.dataset": "cisco.asa", + "event.module": "cisco", + "event.original": "%ASA-4-106023: Deny udp src Inside:10.123.123.123/57621(LOCAL\\Elastic) dst Outside:10.123.123.123/57621 by access-group \"Inside_access_in\" [0x0, 0x0]", + "event.outcome": "deny", + "event.severity": 4, + "event.timezone": "-02:00", + "fileset.name": "asa", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "warning", + "log.offset": 545, + "network.iana_number": 17, + "network.transport": "udp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "source.port": 57621, + "tags": [ + "cisco-asa" + ] + }, + { + "cisco.asa.message_id": "106017", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "event.action": "firewall-rule", + "event.code": 106017, + "event.dataset": "cisco.asa", + "event.module": "cisco", + "event.original": "%ASA-2-106017: Deny IP due to Land Attack from 10.123.123.123 to 10.123.123.123", + "event.outcome": "deny", + "event.severity": 2, + "event.timezone": "-02:00", + "fileset.name": "asa", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "critical", + "log.offset": 734, + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "tags": [ + "cisco-asa" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/cisco/ftd/test/asa-fix.log b/x-pack/filebeat/module/cisco/ftd/test/asa-fix.log new file mode 100644 index 00000000000..00819e8eec1 --- /dev/null +++ b/x-pack/filebeat/module/cisco/ftd/test/asa-fix.log @@ -0,0 +1,5 @@ +Apr 17 2020 14:08:08 SNL-ASA-VPN-A01 : %ASA-6-302016: Teardown UDP connection 110577675 for Outside:10.123.123.123/53723(LOCAL\Elastic) to Inside:10.233.123.123/53 duration 0:00:00 bytes 148 (zzzzzz) +Apr 17 2020 14:00:31 SNL-ASA-VPN-A01 : %ASA-4-106023: Deny icmp src Inside:10.123.123.123 dst Outside:10.123.123.123 (type 11, code 0) by access-group "Inside_access_in" [0x0, 0x0] +Apr 15 2013 09:36:50: %ASA-4-106023: Deny tcp src dmz:10.123.123.123/6316 dst outside:10.123.123.123/53 type 3, code 0, by access-group "acl_dmz" [0xe3afb522, 0x0] +Apr 17 2020 14:16:20 SNL-ASA-VPN-A01 : %ASA-4-106023: Deny udp src Inside:10.123.123.123/57621(LOCAL\Elastic) dst Outside:10.123.123.123/57621 by access-group "Inside_access_in" [0x0, 0x0] +Apr 17 2020 14:15:07 SNL-ASA-VPN-A01 : %ASA-2-106017: Deny IP due to Land Attack from 10.123.123.123 to 10.123.123.123 diff --git a/x-pack/filebeat/module/cisco/ftd/test/asa-fix.log-expected.json b/x-pack/filebeat/module/cisco/ftd/test/asa-fix.log-expected.json new file mode 100644 index 00000000000..bf6c6b521da --- /dev/null +++ b/x-pack/filebeat/module/cisco/ftd/test/asa-fix.log-expected.json @@ -0,0 +1,157 @@ +[ + { + "@timestamp": "2020-04-17T14:08:08.000-02:00", + "cisco.ftd.connection_id": "110577675", + "cisco.ftd.destination_interface": "Inside", + "cisco.ftd.message_id": "302016", + "cisco.ftd.source_interface": "Outside", + "cisco.ftd.source_username": "(LOCAL\\Elastic)", + "destination.address": "10.233.123.123", + "destination.ip": "10.233.123.123", + "destination.port": 53, + "event.action": "flow-expiration", + "event.code": 302016, + "event.dataset": "cisco.ftd", + "event.duration": 0, + "event.end": "2020-04-17T14:08:08.000-02:00", + "event.module": "cisco", + "event.original": "%ASA-6-302016: Teardown UDP connection 110577675 for Outside:10.123.123.123/53723(LOCAL\\Elastic) to Inside:10.233.123.123/53 duration 0:00:00 bytes 148 (zzzzzz)", + "event.severity": 6, + "event.start": "2020-04-17T16:08:08.000Z", + "event.timezone": "-02:00", + "fileset.name": "ftd", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "informational", + "log.offset": 0, + "network.bytes": 148, + "network.iana_number": 17, + "network.transport": "udp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "source.port": 53723, + "tags": [ + "cisco-ftd" + ] + }, + { + "@timestamp": "2020-04-17T14:00:31.000-02:00", + "cisco.ftd.destination_interface": "Outside", + "cisco.ftd.message_id": "106023", + "cisco.ftd.rule_name": "Inside_access_in", + "cisco.ftd.source_interface": "Inside", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "event.action": "firewall-rule", + "event.code": 106023, + "event.dataset": "cisco.ftd", + "event.module": "cisco", + "event.original": "%ASA-4-106023: Deny icmp src Inside:10.123.123.123 dst Outside:10.123.123.123 (type 11, code 0) by access-group \"Inside_access_in\" [0x0, 0x0]", + "event.outcome": "deny", + "event.severity": 4, + "event.timezone": "-02:00", + "fileset.name": "ftd", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "warning", + "log.offset": 200, + "network.iana_number": 1, + "network.transport": "icmp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "tags": [ + "cisco-ftd" + ] + }, + { + "@timestamp": "2013-04-15T09:36:50.000-02:00", + "cisco.ftd.destination_interface": "outside", + "cisco.ftd.message_id": "106023", + "cisco.ftd.rule_name": "acl_dmz", + "cisco.ftd.source_interface": "dmz", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "destination.port": 53, + "event.action": "firewall-rule", + "event.code": 106023, + "event.dataset": "cisco.ftd", + "event.module": "cisco", + "event.original": "%ASA-4-106023: Deny tcp src dmz:10.123.123.123/6316 dst outside:10.123.123.123/53 type 3, code 0, by access-group \"acl_dmz\" [0xe3afb522, 0x0]", + "event.outcome": "deny", + "event.severity": 4, + "event.timezone": "-02:00", + "fileset.name": "ftd", + "input.type": "log", + "log.level": "warning", + "log.offset": 381, + "network.iana_number": 6, + "network.transport": "tcp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "source.port": 6316, + "tags": [ + "cisco-ftd" + ] + }, + { + "@timestamp": "2020-04-17T14:16:20.000-02:00", + "cisco.ftd.destination_interface": "Outside", + "cisco.ftd.message_id": "106023", + "cisco.ftd.rule_name": "Inside_access_in", + "cisco.ftd.source_interface": "Inside", + "cisco.ftd.source_username": "(LOCAL\\Elastic)", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "destination.port": 57621, + "event.action": "firewall-rule", + "event.code": 106023, + "event.dataset": "cisco.ftd", + "event.module": "cisco", + "event.original": "%ASA-4-106023: Deny udp src Inside:10.123.123.123/57621(LOCAL\\Elastic) dst Outside:10.123.123.123/57621 by access-group \"Inside_access_in\" [0x0, 0x0]", + "event.outcome": "deny", + "event.severity": 4, + "event.timezone": "-02:00", + "fileset.name": "ftd", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "warning", + "log.offset": 545, + "network.iana_number": 17, + "network.transport": "udp", + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "source.port": 57621, + "tags": [ + "cisco-ftd" + ] + }, + { + "@timestamp": "2020-04-17T14:15:07.000-02:00", + "cisco.ftd.message_id": "106017", + "destination.address": "10.123.123.123", + "destination.ip": "10.123.123.123", + "event.action": "firewall-rule", + "event.code": 106017, + "event.dataset": "cisco.ftd", + "event.module": "cisco", + "event.original": "%ASA-2-106017: Deny IP due to Land Attack from 10.123.123.123 to 10.123.123.123", + "event.outcome": "deny", + "event.severity": 2, + "event.timezone": "-02:00", + "fileset.name": "ftd", + "host.hostname": "SNL-ASA-VPN-A01", + "input.type": "log", + "log.level": "critical", + "log.offset": 734, + "service.type": "cisco", + "source.address": "10.123.123.123", + "source.ip": "10.123.123.123", + "tags": [ + "cisco-ftd" + ] + } +] \ No newline at end of file diff --git a/x-pack/filebeat/module/cisco/shared/ingest/asa-ftd-pipeline.yml b/x-pack/filebeat/module/cisco/shared/ingest/asa-ftd-pipeline.yml index 9dfc96d77e8..babf697616b 100644 --- a/x-pack/filebeat/module/cisco/shared/ingest/asa-ftd-pipeline.yml +++ b/x-pack/filebeat/module/cisco/shared/ingest/asa-ftd-pipeline.yml @@ -1,3 +1,4 @@ +--- description: "Pipeline for Cisco {< .internal_PREFIX >} logs" processors: # @@ -240,10 +241,11 @@ processors: if: "ctx._temp_.cisco.message_id == '106022'" field: "message" pattern: "%{event.outcome} %{network.transport} connection spoof from %{source.address} to %{destination.address} on interface %{_temp_.cisco.source_interface}" - - dissect: + - grok: if: "ctx._temp_.cisco.message_id == '106023'" field: "message" - pattern: '%{event.outcome} %{network.transport} src %{_temp_.cisco.source_interface}:%{source.address}/%{source.port} dst %{_temp_.cisco.destination_interface}:%{destination.address}/%{destination.port} %{} access%{}group "%{_temp_.cisco.list_id}"%{}' + patterns: + - ^%{NOTSPACE:event.outcome} %{NOTSPACE:network.transport} src %{NOTSPACE:_temp_.cisco.source_interface}:%{IPORHOST:source.address}(/%{POSINT:source.port})?\s*(%{GREEDYDATA:_temp_.cisco.source_username} )?dst %{NOTSPACE:_temp_.cisco.destination_interface}:%{IPORHOST:destination.address}(/%{POSINT:destination.port})?%{DATA}by access.group "%{NOTSPACE:_temp_.cisco.list_id}" - dissect: if: "ctx._temp_.cisco.message_id == '106027'" field: "message" @@ -440,8 +442,8 @@ processors: field: "message" if: '["302014", "302016", "302018", "302021", "302036", "302304", "302306"].contains(ctx._temp_.cisco.message_id)' patterns: - - "Teardown %{NOTSPACE:network.transport} (?:state-bypass )?connection %{NOTSPACE:_temp_.cisco.connection_id} (?:for|from) %{NOTCOLON:_temp_.cisco.source_interface}:%{DATA:source.address}/%{NUMBER:source.port:int} (?:%{NOTSPACE:_temp_.cisco.source_username} )?to %{NOTCOLON:_temp_.cisco.destination_interface}:%{DATA:destination.address}/%{NUMBER:destination.port:int} (?:%{NOTSPACE:_temp_.cisco.destination_username} )?(?:duration %{TIME:_temp_.duration_hms} bytes %{NUMBER:network.bytes:int})%{GREEDYDATA}" - - "Teardown %{NOTSPACE:network.transport} connection for faddr (?:%{NOTCOLON:_temp_.cisco.source_interface}:)?%{ECSDESTIPORHOST}/%{NUMBER} (?:%{NOTSPACE:_temp_.cisco.destination_username} )?gaddr (?:%{NOTCOLON}:)?%{MAPPEDSRC}/%{NUMBER} laddr (?:%{NOTCOLON:_temp_.cisco.source_interface}:)?%{ECSSOURCEIPORHOST}/%{NUMBER}(?: %{NOTSPACE:_temp_.cisco.source_username})?%{GREEDYDATA}" + - Teardown %{NOTSPACE:network.transport} (?:state-bypass )?connection %{NOTSPACE:_temp_.cisco.connection_id} (?:for|from) %{NOTCOLON:_temp_.cisco.source_interface}:%{DATA:source.address}/%{NUMBER:source.port:int}\s*(?:%{NOTSPACE:_temp_.cisco.source_username} )?to %{NOTCOLON:_temp_.cisco.destination_interface}:%{DATA:destination.address}/%{NUMBER:destination.port:int}\s*(?:%{NOTSPACE:_temp_.cisco.destination_username} )?(?:duration %{TIME:_temp_.duration_hms} bytes %{NUMBER:network.bytes:int})%{GREEDYDATA} + - Teardown %{NOTSPACE:network.transport} connection for faddr (?:%{NOTCOLON:_temp_.cisco.source_interface}:)?%{ECSDESTIPORHOST}/%{NUMBER}\s*(?:%{NOTSPACE:_temp_.cisco.destination_username} )?gaddr (?:%{NOTCOLON}:)?%{MAPPEDSRC}/%{NUMBER} laddr (?:%{NOTCOLON:_temp_.cisco.source_interface}:)?%{ECSSOURCEIPORHOST}/%{NUMBER}\s*(?:%{NOTSPACE:_temp_.cisco.source_username})?%{GREEDYDATA} pattern_definitions: NOTCOLON: "[^:]*" ECSSOURCEIPORHOST: "(?:%{IP:source.address}|%{HOSTNAME:source.domain})" diff --git a/x-pack/filebeat/module/netflow/_meta/fields.yml b/x-pack/filebeat/module/netflow/_meta/fields.yml index fc4bf3bb887..c9bb9852ce1 100644 --- a/x-pack/filebeat/module/netflow/_meta/fields.yml +++ b/x-pack/filebeat/module/netflow/_meta/fields.yml @@ -3,4 +3,5 @@ description: > Module for receiving NetFlow and IPFIX flow records over UDP. The module does not add fields beyond what the netflow input provides. + skipdocs: fields: diff --git a/x-pack/filebeat/module/netflow/fields.go b/x-pack/filebeat/module/netflow/fields.go index 6d6ea26f0af..f8e6a0b3670 100644 --- a/x-pack/filebeat/module/netflow/fields.go +++ b/x-pack/filebeat/module/netflow/fields.go @@ -19,5 +19,5 @@ func init() { // AssetNetflow returns asset data. // This is the base64 encoded gzipped contents of module/netflow. func AssetNetflow() string { - return "eJw8jjFOw0AQRfs9xbtAcoAtqFCkFKAUINGazBiPWHas3Ymt3B4Z4fTv/f8OfOs9UzXG4uvhx+VWNEFYFM28apyKrwlE+7XZHOY185QAXv5gRm80vaotVr92g6EK58vp/ME2vAHepOOLNt6fL0feJuVxB+LaqR4MIoymRTqfevcqrNMQxKR7JVbnWzA3X0y0HxP/Qk6/AQAA//9CcUYh" + return "eJw8jjFOw0AQRfs9xbtAcoAtqFCkFKAUINEazxiPsuxYuxNbuT0KwvTv/f8OXPWeqRpT8e3w7XIrmiAsimZeNU7FtwSifWy2hHnNPCWAl1+YyRtNR7XV6tduMFThfDmdP3gMPwBv0vFVG+/PlyNvs/J/B+LaqR4MIkymRTqfevcqbPMQxKx7JVaXW7A0X020HxP0qy3iY8+JPzmnnwAAAP//qK1KBQ==" } diff --git a/x-pack/filebeat/module/panw/fields.go b/x-pack/filebeat/module/panw/fields.go index 8d877ad9d7e..5864f7597ab 100644 --- a/x-pack/filebeat/module/panw/fields.go +++ b/x-pack/filebeat/module/panw/fields.go @@ -19,5 +19,5 @@ func init() { // AssetPanw returns asset data. // This is the base64 encoded gzipped contents of module/panw. func AssetPanw() string { - return "eJzMmM9u4zYQxu95irm5BeLcesmhQNpFgABpauw26HFBUyOJNcXRDkdxtU+/IC3bWpu2tLbXiG6STH6/+fNRpKewwPYeauWWNwBixOLmLkOv2dRiyN3D7zcAAH9R1liEnBhmyhI8WCF4QVkSLzz8Mnt4mf796dcbgNygzfx9HDQFp6rttOGStsZ7KJiaunuSEAvXY5wHcqYKpMQ4B1SR4q77UV8KdvTIbx+nVI9Jf6dPHOUTQa9iBkuFv+uP3cPqkXFj0aN8L9XhLbBdEmc7744xAsCLqhAoj4xhcpBSCVRKdIkZSGk8ePTekLtLAnlqWGOSZy9dwzRd0oQA/xd0WcQSqqcW39B2YkDz/1DL3c7oVNr6pF/J7XIO5W4Ecbg+rbCCQFfvQ1nr8xgnyLnaS95loTYqP0Dm1G6DwdGijiSakZfpy8M/6zKqLGP0/hZMvn4U3hoPNXJOXGG2z3i4zr3MpgDXARx4OYJ/P4KnWQpws4oQp/K4BrHkisuhBLEtTNKqGXoxToV5r+TXnuK7M+2HHtv7cm6f7D3at1/Vvof7z88z8qCVB8w80kMHYkq7eoSvB519HtcIi7vV3uJK9u7UTrR2rVX92aQ8cgEPzZReoIBWtTSM8PQh+keBlIzqUBbhTBONaWhNVdU4I2069DHhj0xBuP5cq8UMWFpOS+XLzaY0dNhvU2nq7Z74QGPlxl5rkxekTmypENxp/fKHcYpb2GRn3SkrGo9OAu8cQTll26+Yrsu8jbH8a2z2aDiM4zejMbWcpIuczH3D9kqpb9iemHmtBAvi9ue4+TH2a6zH68fn8LWRiY/srx+fN9rpVTuMXRfkNo55Q86MFiAXb2OFlcvA+OQEaKREhkmlrNGGGj+5hUnBql0qxsktEMNkjs4UbjJkIkvLfdufcXh7CrsDpyy4pkI2GkyGTkxukGMXo9Ll/n4hfY7DLw06jZ9dU82Rk4yJ79oA4DMVgE647ZPFE6bxYJxmrNAJZp28GGVtoo6vznxpcBuSpSIiDcTULfaMRw6pJ+U9dB3xqnOC1JiPyw7UJdsg8d/CTiNEm/8AXbj5qXybrPXJvgUAAP//KvmK+g==" + return "eJzMmM9u4zYQxu95irm5BeLcesmhQNpFgABpauw26HFBUyOJNcXRDkdxtU+/IC3LWouOtE42iG6STH6/+fNRpJewwfYaauW2FwBixGJ/l6HXbGox5K7h9wsAgL8oayxCTgwrZQlurBA8oGyJNx5+Wd08LP/+9OsFQG7QZv46DlqCU9Vh2nBJW+M1FExN3T1JiIXrNs4DOVMFUmKcA6pIcdX9aCgFR3rkD49Tqs9Jf6dPHOUTQe9iBkuFvxqOHWENyLix6FG+l+rwNthuibOjd88xAsCDqhAoj4xhcpBSCVRKdIkZSGk8ePTekLtKAnlqWGOSZ5SuaZouaUKA/wu6LGIJ1UuLT2g7MaD1f6jl6mh0Km1D0q/kjjmncjeDOFyfdlhBoKv3qawNeYwT5FyNkve6UL3KD5A5ddxg8GxRZxKtyMvy4eaffRlVljF6fwkm3z8Kb42HGjknrjAbM56u8yCzKcB9ACdezuAfR3C3SgH2qwhxKo97EEuueD2UIHaASVo1Qy/GqTDvG/l1oPjuTPthwPa+nDske4/2HVZ16OHh85cZedLKE2ae6aETMaVdPcPXk85+GdcMi7vd3uKN7N2pnWntWqv6s0l55BU8tFJ6gwJa1dIwwt2H6B8FUjKqU1mEF5poTkNrqqrGGWnToc8Jf2YKwvXnXi1mwNJ2WSpf9pvS0GG/LaWpD3viE42VG/tWm7wgdWZLheDO65c/jFPcQp+dfafsaDw6CbxrBOWUbb9iui7rNsbyr7HZreEwjp+MxtRyki5yMvcN2zdKfcP2zMxrJVgQtz/HzbexX2M9Hj/eh6+NLHxkf/x432unV+0wdl+QyzjmCTkzWoBcvI0VVi4D45MToJESGRaVskYbavziEhYFq3arGBeXQAyLNTpTuMWUiSxtx7Z/weHtLuwOnLLgmgrZaDAZOjG5QY5djEqX4/1C+hyHXxp0Gj+7plojJxkT37UJwHsqAJ1wOySLJ0zjwTjNWKETzDp5McraRB0fnfnS4CEkS0VEmoipW+wZnzmknpX30HXEu84JUnM+LkdQr9kGif8Wjhoh2vwH6MLNT+Xrs5YiGwIpffK8ModlOYK5iROCqA26nqD3yLcAAAD//yC/rB0=" } diff --git a/x-pack/filebeat/module/panw/panos/_meta/fields.yml b/x-pack/filebeat/module/panw/panos/_meta/fields.yml index 14920667ca6..a5900461f08 100644 --- a/x-pack/filebeat/module/panw/panos/_meta/fields.yml +++ b/x-pack/filebeat/module/panw/panos/_meta/fields.yml @@ -127,3 +127,7 @@ type: keyword description: > Palo Alto Networks name for the threat. + - name: action + type: keyword + description: >- + Action taken for the session. diff --git a/x-pack/filebeat/module/panw/panos/config/input.yml b/x-pack/filebeat/module/panw/panos/config/input.yml index 7998f04511a..929237b99af 100644 --- a/x-pack/filebeat/module/panw/panos/config/input.yml +++ b/x-pack/filebeat/module/panw/panos/config/input.yml @@ -70,7 +70,7 @@ processors: destination.nat.port: 27 _temp_.labels: 28 network.transport: 29 - event.outcome: 30 + panw.panos.action: 30 network.bytes: 31 client.bytes: 32 destination.bytes: 32 @@ -123,7 +123,7 @@ processors: destination.nat.port: 27 _temp_.labels: 28 network.transport: 29 - event.outcome: 30 + panw.panos.action: 30 panw.panos.threat.resource: 31 url.original: 31 panw.panos.threat.name: 32 diff --git a/x-pack/filebeat/module/panw/panos/ingest/pipeline.yml b/x-pack/filebeat/module/panw/panos/ingest/pipeline.yml index 135d90a04dc..1c2c912bd87 100644 --- a/x-pack/filebeat/module/panw/panos/ingest/pipeline.yml +++ b/x-pack/filebeat/module/panw/panos/ingest/pipeline.yml @@ -175,34 +175,82 @@ processors: # Set event.category depending on log type. - set: + field: event.kind + value: event + if: 'ctx?._temp_?.message_type == "TRAFFIC"' + - append: field: event.category - value: network_traffic + value: + - network_traffic + - network if: 'ctx?._temp_?.message_type == "TRAFFIC"' - set: + field: event.kind + value: alert + if: 'ctx?._temp_?.message_type == "THREAT"' + - append: field: event.category - value: security_threat + value: + - security_threat + - intrusion_detection + - network if: 'ctx?._temp_?.message_type == "THREAT"' - - drop: if: 'ctx?.event?.category == null' + - append: + field: event.type + value: allowed + if: "ctx?.panw?.panos?.action != null && ['alert', 'allow', 'continue'].contains(ctx.panw.panos.action)" + - append: + field: event.type + value: denied + if: "ctx?.panw?.panos?.action != null && ['deny', 'drop', 'reset-client', 'reset-server', 'reset-both', 'block-url', 'block-ip', 'random-drop', 'sinkhole', 'block'].contains(ctx.panw.panos.action)" + - set: + field: event.outcome + value: success + # event.action for traffic logs. - set: field: event.action value: flow_started if: 'ctx?._temp_?.message_subtype == "start"' + - append: + field: event.type + value: + - start + - connection + if: 'ctx?._temp_?.message_subtype == "start"' - set: field: event.action value: flow_terminated if: 'ctx?._temp_?.message_subtype == "end"' + - append: + field: event.type + value: + - end + - connection + if: 'ctx?._temp_?.message_subtype == "end"' - set: field: event.action value: flow_dropped if: 'ctx?._temp_?.message_subtype == "drop"' + - append: + field: event.type + value: + - denied + - connection + if: 'ctx?._temp_?.message_subtype == "drop"' - set: field: event.action value: flow_denied if: 'ctx?._temp_?.message_subtype == "deny"' + - append: + field: event.type + value: + - denied + - connection + if: 'ctx?._temp_?.message_subtype == "deny"' # event.action for threat logs. - set: @@ -276,21 +324,21 @@ processors: # Normalize event.outcome. # These values appear in the TRAFFIC docs but look like a mistake. - set: - field: event.outcome + field: panw.panos.action value: 'drop-icmp' - if: 'ctx?.event?.outcome == "drop icmp" || ctx?.event?.outcome == "drop ICMP"' + if: 'ctx?.panw?.panos?.action == "drop icmp" || ctx?.panw?.panos?.action == "drop ICMP"' - set: - field: event.outcome + field: panw.panos.action value: 'reset-both' - if: 'ctx?.event?.outcome == "reset both"' + if: 'ctx?.panw?.panos?.action == "reset both"' - set: - field: event.outcome + field: panw.panos.action value: 'reset-client' - if: 'ctx?.event?.outcome == "reset client"' + if: 'ctx?.panw?.panos?.action == "reset client"' - set: - field: event.outcome + field: panw.panos.action value: 'reset-server' - if: 'ctx?.event?.outcome == "reset server"' + if: 'ctx?.panw?.panos?.action == "reset server"' # Build related.ip array from src/dest/NAT IPs. - append: @@ -391,6 +439,36 @@ processors: value: 'URL-filtering' if: 'ctx?.panw?.panos?.threat?.id == "9999"' + - set: + field: rule.name + value: "{{panw.panos.ruleset}}" + if: "ctx?.panw?.panos?.ruleset != null" + + - append: + field: related.user + value: "{{client.user.name}}" + if: "ctx?.client?.user?.name != null" + + - append: + field: related.user + value: "{{source.user.name}}" + if: "ctx?.source?.user?.name != null" + + - append: + field: related.user + value: "{{server.user.name}}" + if: "ctx?.server?.user?.name != null" + + - append: + field: related.user + value: "{{destination.user.name}}" + if: "ctx?.destination?.user?.name != null" + + - append: + field: related.hash + value: "{{panw.panos.file.hash}}" + if: "ctx?.panw?.panos?.file?.hash != null" + # Remove temporary fields. - remove: field: diff --git a/x-pack/filebeat/module/panw/panos/test/pan_inc_other.log-expected.json b/x-pack/filebeat/module/panw/panos/test/pan_inc_other.log-expected.json index e94019b5a55..5b43295399c 100644 --- a/x-pack/filebeat/module/panw/panos/test/pan_inc_other.log-expected.json +++ b/x-pack/filebeat/module/panw/panos/test/pan_inc_other.log-expected.json @@ -23,14 +23,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:56.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:56.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -44,6 +53,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -62,6 +72,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, diff --git a/x-pack/filebeat/module/panw/panos/test/pan_inc_threat.log-expected.json b/x-pack/filebeat/module/panw/panos/test/pan_inc_threat.log-expected.json index ecf18d56eb3..f6ca00ac200 100644 --- a/x-pack/filebeat/module/panw/panos/test/pan_inc_threat.log-expected.json +++ b/x-pack/filebeat/module/panw/panos/test/pan_inc_threat.log-expected.json @@ -20,12 +20,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -38,6 +46,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -58,6 +67,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -94,12 +108,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -112,6 +134,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -132,6 +155,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -168,12 +196,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -186,6 +222,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -206,6 +243,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -242,12 +284,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -260,6 +310,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -280,6 +331,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -316,12 +372,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -334,6 +398,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -354,6 +419,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -390,12 +460,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -408,6 +486,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -428,6 +507,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -464,12 +548,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -482,6 +574,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -502,6 +595,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -538,12 +636,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -556,6 +662,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -576,6 +683,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -612,12 +724,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -630,6 +750,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -650,6 +771,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -686,12 +812,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -704,6 +838,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -724,6 +859,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -760,12 +900,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -778,6 +926,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -798,6 +947,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -834,12 +988,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -852,6 +1014,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -872,6 +1035,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -908,12 +1076,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -926,6 +1102,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -946,6 +1123,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -982,12 +1164,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -999,6 +1189,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1019,6 +1210,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1055,12 +1251,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1073,6 +1277,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1093,6 +1298,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1129,12 +1339,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1147,6 +1365,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1167,6 +1386,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1200,12 +1424,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1217,6 +1449,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1237,6 +1470,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "78.159.99.224", "server.port": 80, "service.type": "panw", @@ -1273,12 +1511,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1291,6 +1537,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1311,6 +1558,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1347,12 +1599,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1365,6 +1625,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1385,6 +1646,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1421,12 +1687,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1439,6 +1713,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1459,6 +1734,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1495,12 +1775,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1513,6 +1801,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1533,6 +1822,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1569,12 +1863,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1587,6 +1889,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1607,6 +1910,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1643,12 +1951,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1661,6 +1977,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1681,6 +1998,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1717,12 +2039,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1735,6 +2065,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1755,6 +2086,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1791,12 +2127,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1809,6 +2153,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1829,6 +2174,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1865,12 +2215,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1883,6 +2241,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1903,6 +2262,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -1939,12 +2303,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1957,6 +2329,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1977,6 +2350,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2013,12 +2391,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2031,6 +2417,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2051,6 +2438,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2087,12 +2479,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2105,6 +2505,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2125,6 +2526,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2161,12 +2567,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2179,6 +2593,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2199,6 +2614,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2235,12 +2655,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2253,6 +2681,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2273,6 +2702,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2309,12 +2743,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2327,6 +2769,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2347,6 +2790,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2383,12 +2831,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2401,6 +2857,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2421,6 +2878,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2454,12 +2916,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2471,6 +2941,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2491,6 +2962,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "69.43.161.167", "server.port": 80, "service.type": "panw", @@ -2524,12 +3000,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2541,6 +3025,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2561,6 +3046,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "202.31.187.154", "server.port": 80, "service.type": "panw", @@ -2594,12 +3084,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2611,6 +3109,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2631,6 +3130,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "89.111.176.67", "server.port": 80, "service.type": "panw", @@ -2667,12 +3171,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2684,6 +3196,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2704,6 +3217,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2737,12 +3255,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2754,6 +3280,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2774,6 +3301,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "208.73.210.29", "server.port": 80, "service.type": "panw", @@ -2807,12 +3339,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2824,6 +3364,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2844,6 +3385,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "208.73.210.29", "server.port": 80, "service.type": "panw", @@ -2880,12 +3426,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2897,6 +3451,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2917,6 +3472,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -2950,12 +3510,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2967,6 +3535,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2987,6 +3556,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "208.73.210.29", "server.port": 80, "service.type": "panw", @@ -3020,12 +3594,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3037,6 +3619,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3057,6 +3640,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "89.108.64.156", "server.port": 80, "service.type": "panw", @@ -3090,12 +3678,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3107,6 +3703,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3127,6 +3724,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "89.108.64.156", "server.port": 80, "service.type": "panw", @@ -3154,10 +3756,15 @@ "destination.port": 58849, "destination.user.name": "crusher", "event.action": "spyware_detected", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "drop-all-packets", + "event.outcome": "success", "event.severity": 1, "event.timezone": "-02:00", "fileset.name": "panos", @@ -3171,6 +3778,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "drop-all-packets", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3191,6 +3799,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 58849, "server.user.name": "crusher", @@ -3236,12 +3849,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3253,6 +3874,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3273,6 +3895,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "216.8.179.25", "server.port": 80, "service.type": "panw", @@ -3306,12 +3933,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3323,6 +3958,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3343,6 +3979,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "69.43.161.154", "server.port": 80, "service.type": "panw", @@ -3376,12 +4017,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3393,6 +4042,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3413,6 +4063,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "208.91.196.252", "server.port": 80, "service.type": "panw", @@ -3446,12 +4101,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3463,6 +4126,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3483,6 +4147,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "208.73.210.29", "server.port": 80, "service.type": "panw", @@ -3519,12 +4188,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3536,6 +4213,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3556,6 +4234,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -3592,12 +4275,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3609,6 +4300,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3629,6 +4321,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -3665,12 +4362,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3682,6 +4387,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3702,6 +4408,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -3738,12 +4449,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3755,6 +4474,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "1606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3775,6 +4495,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -3811,12 +4536,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3828,6 +4561,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "1606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3848,6 +4582,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -3875,12 +4614,20 @@ "destination.port": 54431, "destination.user.name": "crusher", "event.action": "file_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "deny", + "event.outcome": "success", "event.severity": 4, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3892,6 +4639,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "1606001116", + "panw.panos.action": "deny", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3912,6 +4660,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 54431, "server.user.name": "crusher", @@ -3957,12 +4710,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3974,6 +4735,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "1606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3994,6 +4756,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -4021,12 +4788,20 @@ "destination.port": 61220, "destination.user.name": "crusher", "event.action": "file_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "deny", + "event.outcome": "success", "event.severity": 4, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4038,6 +4813,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "deny", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4058,6 +4834,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 61220, "server.user.name": "crusher", @@ -4094,12 +4875,20 @@ "destination.port": 61726, "destination.user.name": "crusher", "event.action": "file_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "deny", + "event.outcome": "success", "event.severity": 4, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4111,6 +4900,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "deny", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4131,6 +4921,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 61726, "server.user.name": "crusher", @@ -4175,12 +4970,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4192,6 +4995,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4212,6 +5016,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -4239,12 +5048,20 @@ "destination.port": 60212, "destination.user.name": "crusher", "event.action": "file_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "deny", + "event.outcome": "success", "event.severity": 4, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4256,6 +5073,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "deny", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4276,6 +5094,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 60212, "server.user.name": "crusher", @@ -4309,12 +5132,20 @@ "destination.port": 60392, "destination.user.name": "crusher", "event.action": "file_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "deny", + "event.outcome": "success", "event.severity": 4, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4326,6 +5157,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "deny", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4346,6 +5178,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 60392, "server.user.name": "crusher", @@ -4388,12 +5225,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4405,6 +5250,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4425,6 +5271,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "213.180.199.61", "server.port": 80, "service.type": "panw", @@ -4458,12 +5309,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4475,6 +5334,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4495,6 +5355,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "213.180.199.61", "server.port": 80, "service.type": "panw", @@ -4528,12 +5393,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4545,6 +5418,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4565,6 +5439,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "213.180.199.61", "server.port": 80, "service.type": "panw", @@ -4592,12 +5471,20 @@ "destination.port": 54431, "destination.user.name": "crusher", "event.action": "file_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "deny", + "event.outcome": "success", "event.severity": 4, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4609,6 +5496,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "deny", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4629,6 +5517,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 54431, "server.user.name": "crusher", @@ -4674,12 +5567,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4691,6 +5592,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4711,6 +5613,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.ip": "204.232.231.46", "server.port": 80, "service.type": "panw", @@ -4747,12 +5654,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4764,6 +5679,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4784,6 +5700,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "207.46.140.46", "server.port": 80, "service.type": "panw", @@ -4811,12 +5732,20 @@ "destination.port": 1039, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4828,6 +5757,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4848,6 +5778,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.6", "server.port": 1039, "server.user.name": "jordy", @@ -4884,12 +5819,20 @@ "destination.port": 1064, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4901,6 +5844,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4921,6 +5865,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.6", "server.port": 1064, "server.user.name": "jordy", @@ -4966,12 +5915,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4983,6 +5940,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5003,6 +5961,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "65.54.71.11", "server.port": 80, "service.type": "panw", @@ -5030,12 +5993,20 @@ "destination.port": 1071, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5047,6 +6018,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5067,6 +6039,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.6", "server.port": 1071, "server.user.name": "jordy", @@ -5106,12 +6083,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5123,6 +6108,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5143,6 +6129,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "208.85.40.48", "server.port": 80, "service.type": "panw", @@ -5170,12 +6161,20 @@ "destination.port": 57876, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5187,6 +6186,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5207,6 +6207,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 57876, "server.user.name": "picard", @@ -5240,12 +6245,20 @@ "destination.port": 1082, "destination.user.name": "jordy", "event.action": "file_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "deny", + "event.outcome": "success", "event.severity": 4, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5257,6 +6270,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "deny", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5277,6 +6291,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.6", "server.port": 1082, "server.user.name": "jordy", @@ -5313,12 +6332,20 @@ "destination.port": 50986, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5330,6 +6357,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5350,6 +6378,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 50986, "server.user.name": "picard", @@ -5383,12 +6416,20 @@ "destination.port": 51716, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5400,6 +6441,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5420,6 +6462,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 51716, "server.user.name": "picard", @@ -5453,12 +6500,20 @@ "destination.port": 52119, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5470,6 +6525,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5490,6 +6546,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 52119, "server.user.name": "picard", @@ -5523,12 +6584,20 @@ "destination.port": 52411, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5540,6 +6609,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5560,6 +6630,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 52411, "server.user.name": "picard", @@ -5599,12 +6674,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5616,6 +6699,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5636,6 +6720,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "74.125.239.6", "server.port": 80, "service.type": "panw", @@ -5663,12 +6752,20 @@ "destination.port": 53026, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5680,6 +6777,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5700,6 +6798,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 53026, "server.user.name": "picard", @@ -5733,12 +6836,20 @@ "destination.port": 53809, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5750,6 +6861,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5770,6 +6882,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 53809, "server.user.name": "picard", @@ -5803,12 +6920,20 @@ "destination.port": 55912, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5820,6 +6945,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5840,6 +6966,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 55912, "server.user.name": "picard", @@ -5873,12 +7004,20 @@ "destination.port": 55916, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5890,6 +7029,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5910,6 +7050,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 55916, "server.user.name": "picard", @@ -5943,12 +7088,20 @@ "destination.port": 1046, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5960,6 +7113,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5980,6 +7134,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.6", "server.port": 1046, "server.user.name": "jordy", @@ -6016,12 +7175,20 @@ "destination.port": 61734, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6033,6 +7200,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6053,6 +7221,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 61734, "server.user.name": "jordy", @@ -6086,12 +7259,20 @@ "destination.port": 62292, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6103,6 +7284,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6123,6 +7305,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 62292, "server.user.name": "jordy", @@ -6156,12 +7343,20 @@ "destination.port": 64669, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6173,6 +7368,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6193,6 +7389,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 64669, "server.user.name": "jordy", @@ -6229,12 +7430,20 @@ "destination.port": 65265, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6246,6 +7455,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6266,6 +7476,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 65265, "server.user.name": "picard", @@ -6299,12 +7514,20 @@ "destination.port": 64979, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6316,6 +7539,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6336,6 +7560,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 64979, "server.user.name": "picard", @@ -6369,12 +7598,20 @@ "destination.port": 49432, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6386,6 +7623,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6406,6 +7644,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 49432, "server.user.name": "picard", @@ -6442,12 +7685,20 @@ "destination.port": 49722, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6459,6 +7710,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6479,6 +7731,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 49722, "server.user.name": "picard", @@ -6518,12 +7775,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6535,6 +7800,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6555,6 +7821,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "74.125.224.201", "server.port": 80, "service.type": "panw", @@ -6582,12 +7853,20 @@ "destination.port": 50108, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6599,6 +7878,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6619,6 +7899,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 50108, "server.user.name": "picard", @@ -6652,12 +7937,20 @@ "destination.port": 50387, "destination.user.name": "picard", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6669,6 +7962,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6689,6 +7983,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "picard", + "picard" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 50387, "server.user.name": "picard", @@ -6728,12 +8027,20 @@ "destination.nat.port": 0, "destination.port": 80, "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6745,6 +8052,7 @@ "network.direction": "inbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6765,6 +8073,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "208.85.40.48", "server.port": 80, "service.type": "panw", @@ -6792,12 +8105,20 @@ "destination.port": 60005, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6809,6 +8130,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6829,6 +8151,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 60005, "server.user.name": "jordy", @@ -6862,12 +8189,20 @@ "destination.port": 60443, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6879,6 +8214,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6899,6 +8235,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 60443, "server.user.name": "jordy", @@ -6932,12 +8273,20 @@ "destination.port": 60822, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6949,6 +8298,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6969,6 +8319,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 60822, "server.user.name": "jordy", @@ -7002,12 +8357,20 @@ "destination.port": 61105, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7019,6 +8382,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7039,6 +8403,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 61105, "server.user.name": "jordy", @@ -7072,12 +8441,20 @@ "destination.port": 60782, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "alert", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7089,6 +8466,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "alert", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7109,6 +8487,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 60782, "server.user.name": "jordy", @@ -7142,12 +8525,20 @@ "destination.port": 61470, "destination.user.name": "jordy", "event.action": "data_match", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7159,6 +8550,7 @@ "network.direction": "outbound", "network.transport": "tcp", "observer.serial_number": "01606001116", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7179,6 +8571,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "jordy", + "jordy" + ], + "rule.name": "rule1", "server.ip": "192.168.0.2", "server.port": 61470, "server.user.name": "jordy", diff --git a/x-pack/filebeat/module/panw/panos/test/pan_inc_traffic.log-expected.json b/x-pack/filebeat/module/panw/panos/test/pan_inc_traffic.log-expected.json index 4565c577acd..c285f88d43d 100644 --- a/x-pack/filebeat/module/panw/panos/test/pan_inc_traffic.log-expected.json +++ b/x-pack/filebeat/module/panw/panos/test/pan_inc_traffic.log-expected.json @@ -23,14 +23,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:59.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:59.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -44,6 +53,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -62,6 +72,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -100,14 +115,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:58.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -121,6 +145,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -139,6 +164,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -177,14 +207,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:58.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -198,6 +237,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -216,6 +256,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -257,14 +302,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:58.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -278,6 +332,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -296,6 +351,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -337,14 +397,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:58.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -358,6 +427,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -376,6 +446,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -414,14 +489,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:58.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -435,6 +519,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -453,6 +538,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -491,14 +581,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:58.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -512,6 +611,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -530,6 +630,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -571,14 +676,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:28.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:27.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -592,6 +706,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -610,6 +725,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 806, "server.ip": "204.232.231.46", "server.packets": 6, @@ -651,14 +771,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:28.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:28.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -672,6 +801,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -690,6 +820,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 806, "server.ip": "204.232.231.46", "server.packets": 6, @@ -731,14 +866,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:28.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:27.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -752,6 +896,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -770,6 +915,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 806, "server.ip": "204.232.231.46", "server.packets": 6, @@ -811,14 +961,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:58.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -832,6 +991,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -850,6 +1010,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -891,14 +1056,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:57.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:57.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -912,6 +1086,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -930,6 +1105,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -971,14 +1151,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:57.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:57.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -992,6 +1181,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1010,6 +1200,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -1051,14 +1246,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:57.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:57.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1072,6 +1276,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1090,6 +1295,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -1131,14 +1341,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:27.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1152,6 +1371,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1170,6 +1390,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 806, "server.ip": "204.232.231.46", "server.packets": 6, @@ -1211,14 +1436,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:26.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1232,6 +1466,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1250,6 +1485,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 806, "server.ip": "204.232.231.46", "server.packets": 6, @@ -1291,14 +1531,23 @@ "destination.packets": 18, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 512000000000, "event.end": "2012-04-10T04:38:26.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:29:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1312,6 +1561,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1330,6 +1580,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 551, "server.ip": "204.232.231.46", "server.packets": 18, @@ -1371,14 +1626,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:56.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:56.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1392,6 +1656,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1410,6 +1675,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -1451,14 +1721,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:56.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:56.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1472,6 +1751,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1490,6 +1770,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -1528,14 +1813,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:56.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:56.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1549,6 +1843,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1567,6 +1862,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -1605,14 +1905,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:56.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:56.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1626,6 +1935,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1644,6 +1954,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -1685,14 +2000,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:56.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:56.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1706,6 +2030,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1724,6 +2049,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -1762,14 +2092,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:26.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:26.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1783,6 +2122,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1801,6 +2141,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 98, "server.ip": "205.171.2.25", "server.packets": 1, @@ -1842,14 +2187,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:26.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:26.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1863,6 +2217,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1881,6 +2236,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 806, "server.ip": "204.232.231.46", "server.packets": 6, @@ -1922,14 +2282,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:26.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:26.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -1943,6 +2312,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -1961,6 +2331,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 806, "server.ip": "204.232.231.46", "server.packets": 6, @@ -2002,14 +2377,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:56.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:56.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2023,6 +2407,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2041,6 +2426,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -2079,14 +2469,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:55.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2100,6 +2499,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2118,6 +2518,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -2156,14 +2561,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:55.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2177,6 +2591,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2195,6 +2610,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -2236,14 +2656,23 @@ "destination.packets": 8, "destination.port": 13069, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 125000000000, "event.end": "2012-04-10T04:39:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:37:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2257,6 +2686,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2275,6 +2705,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 504, "server.ip": "98.149.55.63", "server.packets": 8, @@ -2316,14 +2751,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:55.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2337,6 +2781,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2355,6 +2800,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -2393,14 +2843,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:55.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2414,6 +2873,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2432,6 +2892,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -2473,14 +2938,23 @@ "destination.packets": 10, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:25.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:24.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2494,6 +2968,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2512,6 +2987,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 9130, "server.ip": "212.48.10.58", "server.packets": 10, @@ -2553,14 +3033,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:55.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2574,6 +3063,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2592,6 +3082,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -2630,14 +3125,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:54.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2651,6 +3155,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2669,6 +3174,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -2707,14 +3217,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:54.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2728,6 +3247,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2746,6 +3266,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -2787,14 +3312,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:54.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2808,6 +3342,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2826,6 +3361,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -2867,14 +3407,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:54.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2888,6 +3437,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2906,6 +3456,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -2944,14 +3499,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:54.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -2965,6 +3529,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -2983,6 +3548,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -3021,14 +3591,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:54.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3042,6 +3621,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3060,6 +3640,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -3097,14 +3682,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:24.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:24.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "log.offset": 14217, @@ -3117,6 +3711,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3135,6 +3730,7 @@ "0.0.0.0", "0.0.0.0" ], + "rule.name": "rule1", "server.bytes": 111, "server.ip": "8.8.8.8", "server.packets": 1, @@ -3172,14 +3768,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:24.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:23.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3193,6 +3798,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3211,6 +3817,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 906, "server.ip": "62.211.68.12", "server.packets": 6, @@ -3251,14 +3862,23 @@ "destination.packets": 10, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:24.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:24.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "log.offset": 14933, @@ -3271,6 +3891,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3289,6 +3910,7 @@ "0.0.0.0", "0.0.0.0" ], + "rule.name": "rule1", "server.bytes": 5013, "server.ip": "50.19.102.116", "server.packets": 10, @@ -3329,14 +3951,23 @@ "destination.packets": 1, "destination.port": 40026, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:24.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:24.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3350,6 +3981,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3368,6 +4000,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 99, "server.ip": "65.55.223.19", "server.packets": 1, @@ -3409,14 +4046,23 @@ "destination.packets": 1, "destination.port": 40029, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:24.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:24.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3430,6 +4076,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3448,6 +4095,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 902, "server.ip": "65.55.223.24", "server.packets": 1, @@ -3485,14 +4137,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:24.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:24.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "log.offset": 16061, @@ -3505,6 +4166,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3523,6 +4185,7 @@ "0.0.0.0", "0.0.0.0" ], + "rule.name": "rule1", "server.bytes": 141, "server.ip": "8.8.8.8", "server.packets": 1, @@ -3563,14 +4226,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:54.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3584,6 +4256,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3602,6 +4275,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -3640,14 +4318,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:53.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:53.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3661,6 +4348,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3679,6 +4367,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -3720,14 +4413,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:53.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:53.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3741,6 +4443,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3759,6 +4462,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -3797,14 +4505,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:53.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:53.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3818,6 +4535,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3836,6 +4554,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -3874,14 +4597,23 @@ "destination.packets": 2, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:23.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:22.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3895,6 +4627,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3913,6 +4646,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 316, "server.ip": "205.171.2.25", "server.packets": 2, @@ -3951,14 +4689,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:23.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:23.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -3972,6 +4719,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -3990,6 +4738,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 121, "server.ip": "205.171.2.25", "server.packets": 1, @@ -4028,14 +4781,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:23.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:23.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4049,6 +4811,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4067,6 +4830,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 169, "server.ip": "205.171.2.25", "server.packets": 1, @@ -4105,14 +4873,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:23.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:23.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4126,6 +4903,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4144,6 +4922,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 954, "server.ip": "62.211.68.12", "server.packets": 6, @@ -4185,14 +4968,23 @@ "destination.packets": 12, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 2000000000, "event.end": "2012-04-10T04:39:23.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4206,6 +4998,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4224,6 +5017,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 9130, "server.ip": "212.48.10.58", "server.packets": 12, @@ -4265,14 +5063,23 @@ "destination.packets": 18, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 512000000000, "event.end": "2012-04-10T04:38:23.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:29:51.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4286,6 +5093,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4304,6 +5112,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 555, "server.ip": "204.232.231.46", "server.packets": 18, @@ -4342,14 +5155,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:53.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:53.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4363,6 +5185,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4381,6 +5204,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -4422,14 +5250,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:53.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:53.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4443,6 +5280,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4461,6 +5299,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -4499,14 +5342,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4520,6 +5372,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4538,6 +5391,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -4576,14 +5434,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4597,6 +5464,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4615,6 +5483,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -4656,14 +5529,23 @@ "destination.packets": 1, "destination.port": 40043, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4677,6 +5559,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4695,6 +5578,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "65.55.223.31", "server.packets": 1, @@ -4736,14 +5624,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4757,6 +5654,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4775,6 +5673,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -4813,14 +5716,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4834,6 +5746,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4852,6 +5765,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -4890,14 +5808,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4911,6 +5838,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -4929,6 +5857,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -4967,14 +5900,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:22.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -4988,6 +5930,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5006,6 +5949,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 906, "server.ip": "62.211.68.12", "server.packets": 6, @@ -5044,14 +5992,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:22.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:22.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5065,6 +6022,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5083,6 +6041,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 163, "server.ip": "205.171.2.25", "server.packets": 1, @@ -5121,14 +6084,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:51.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:51.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5142,6 +6114,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5160,6 +6133,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -5198,14 +6176,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:51.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:51.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5219,6 +6206,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5237,6 +6225,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -5278,14 +6271,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:51.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:51.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5299,6 +6301,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5317,6 +6320,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -5355,14 +6363,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:21.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5376,6 +6393,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5394,6 +6412,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 922, "server.ip": "62.211.68.12", "server.packets": 6, @@ -5435,14 +6458,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:51.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:51.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5456,6 +6488,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5474,6 +6507,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -5512,14 +6550,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5533,6 +6580,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5551,6 +6599,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -5589,14 +6642,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5610,6 +6672,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5628,6 +6691,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -5669,14 +6737,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5690,6 +6767,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5708,6 +6786,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -5746,14 +6829,23 @@ "destination.packets": 17, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:20.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5767,6 +6859,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5785,6 +6878,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 26786, "server.ip": "8.5.1.1", "server.packets": 17, @@ -5823,14 +6921,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5844,6 +6951,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5862,6 +6970,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -5900,14 +7013,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -5921,6 +7043,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -5939,6 +7062,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -5980,14 +7108,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6001,6 +7138,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6019,6 +7157,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -6051,14 +7194,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:20.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6072,6 +7224,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6090,6 +7243,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 169, "server.ip": "192.168.0.1", "server.packets": 1, @@ -6131,14 +7289,23 @@ "destination.packets": 12, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 3000000000, "event.end": "2012-04-10T04:39:20.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:17.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6152,6 +7319,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6170,6 +7338,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 9064, "server.ip": "212.48.10.58", "server.packets": 12, @@ -6211,14 +7384,23 @@ "destination.packets": 12, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 7000000000, "event.end": "2012-04-10T04:39:20.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6232,6 +7414,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6250,6 +7433,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 9124, "server.ip": "212.48.10.58", "server.packets": 12, @@ -6282,14 +7470,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:20.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6303,6 +7500,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6321,6 +7519,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 137, "server.ip": "192.168.0.1", "server.packets": 1, @@ -6353,14 +7556,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:20.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6374,6 +7586,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6392,6 +7605,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 93, "server.ip": "192.168.0.1", "server.packets": 1, @@ -6433,14 +7651,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:49.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:49.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6454,6 +7681,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6472,6 +7700,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -6510,14 +7743,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:49.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:49.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6531,6 +7773,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6549,6 +7792,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -6587,14 +7835,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:49.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:49.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6608,6 +7865,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6626,6 +7884,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -6667,14 +7930,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:49.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:49.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6688,6 +7960,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6706,6 +7979,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -6744,14 +8022,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:49.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:49.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6765,6 +8052,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6783,6 +8071,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -6815,14 +8108,23 @@ "destination.packets": 2, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:18.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6836,6 +8138,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6854,6 +8157,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "192.168.0.1", "server.packets": 2, @@ -6892,14 +8200,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:49.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:49.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6913,6 +8230,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -6931,6 +8249,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -6972,14 +8295,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:48.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:48.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -6993,6 +8325,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7011,6 +8344,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -7049,14 +8387,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:48.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:48.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7070,6 +8417,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7088,6 +8436,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -7126,14 +8479,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:48.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:48.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7147,6 +8509,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7165,6 +8528,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "205.171.2.25", "server.packets": 1, @@ -7203,14 +8571,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:18.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:17.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7224,6 +8601,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7242,6 +8620,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 906, "server.ip": "62.211.68.12", "server.packets": 6, @@ -7283,14 +8666,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:48.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:48.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7304,6 +8696,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7322,6 +8715,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -7363,14 +8761,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:48.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:48.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7384,6 +8791,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7402,6 +8810,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -7443,14 +8856,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:47.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:47.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7464,6 +8886,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7482,6 +8905,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, @@ -7514,14 +8942,23 @@ "destination.packets": 2, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2012-04-10T04:39:17.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7535,6 +8972,7 @@ "network.transport": "udp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7553,6 +8991,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "192.168.0.1", "server.packets": 2, @@ -7594,14 +9037,23 @@ "destination.packets": 3, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:47.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:47.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7615,6 +9067,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7633,6 +9086,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 78, "server.ip": "204.232.231.46", "server.packets": 3, @@ -7674,14 +9132,23 @@ "destination.packets": 3, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:47.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:47.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7695,6 +9162,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7713,6 +9181,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 78, "server.ip": "204.232.231.46", "server.packets": 3, @@ -7754,14 +9227,23 @@ "destination.packets": 1, "destination.port": 80, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2012-04-10T04:39:46.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2012-04-10T04:39:46.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.captive_portal": true, @@ -7775,6 +9257,7 @@ "network.transport": "tcp", "network.type": "ipv4", "observer.serial_number": "01606001116", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "0.0.0.0", "panw.panos.destination.nat.port": 0, @@ -7793,6 +9276,11 @@ "0.0.0.0", "0.0.0.0" ], + "related.user": [ + "crusher", + "crusher" + ], + "rule.name": "rule1", "server.bytes": 0, "server.ip": "204.232.231.46", "server.packets": 1, diff --git a/x-pack/filebeat/module/panw/panos/test/threat.log-expected.json b/x-pack/filebeat/module/panw/panos/test/threat.log-expected.json index c8c9082e093..c17fcbee131 100644 --- a/x-pack/filebeat/module/panw/panos/test/threat.log-expected.json +++ b/x-pack/filebeat/module/panw/panos/test/threat.log-expected.json @@ -16,12 +16,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -38,6 +46,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -59,6 +68,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -90,12 +100,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -112,6 +130,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -133,6 +152,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -164,12 +184,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -186,6 +214,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -207,6 +236,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -238,12 +268,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -260,6 +298,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -281,6 +320,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -312,12 +352,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -334,6 +382,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -355,6 +404,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -386,12 +436,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -408,6 +466,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -429,6 +488,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -460,12 +520,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -482,6 +550,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -503,6 +572,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -534,12 +604,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -556,6 +634,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -577,6 +656,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -608,12 +688,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -630,6 +718,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -651,6 +740,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -682,12 +772,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -704,6 +802,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -725,6 +824,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -756,12 +856,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -778,6 +886,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -799,6 +908,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -830,12 +940,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -852,6 +970,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -873,6 +992,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -904,12 +1024,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -926,6 +1054,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -947,6 +1076,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -978,12 +1108,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1000,6 +1138,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1021,6 +1160,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1052,12 +1192,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1074,6 +1222,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1095,6 +1244,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1126,12 +1276,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1148,6 +1306,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1169,6 +1328,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1200,12 +1360,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1222,6 +1390,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1243,6 +1412,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1274,12 +1444,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1296,6 +1474,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1317,6 +1496,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1348,12 +1528,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1370,6 +1558,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1391,6 +1580,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1422,12 +1612,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1444,6 +1642,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1465,6 +1664,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1496,12 +1696,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1518,6 +1726,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.137.131", "panw.panos.destination.nat.port": 443, @@ -1539,6 +1748,7 @@ "192.168.1.63", "23.72.137.131" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.137.131", "server.port": 443, "service.type": "panw", @@ -1570,12 +1780,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1592,6 +1810,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1613,6 +1832,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1644,12 +1864,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1666,6 +1894,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1687,6 +1916,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1718,12 +1948,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1740,6 +1978,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1761,6 +2000,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1792,12 +2032,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1814,6 +2062,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1835,6 +2084,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1866,12 +2116,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1888,6 +2146,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1909,6 +2168,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -1940,12 +2200,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1962,6 +2230,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -1983,6 +2252,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2014,12 +2284,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2036,6 +2314,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2057,6 +2336,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2088,12 +2368,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2110,6 +2398,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2131,6 +2420,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2162,12 +2452,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2184,6 +2482,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2205,6 +2504,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2236,12 +2536,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2258,6 +2566,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2279,6 +2588,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2310,12 +2620,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2332,6 +2650,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2353,6 +2672,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2384,12 +2704,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2406,6 +2734,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2427,6 +2756,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2458,12 +2788,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2480,6 +2818,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2501,6 +2840,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2532,12 +2872,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2554,6 +2902,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "152.195.55.192", "panw.panos.destination.nat.port": 443, @@ -2575,6 +2924,7 @@ "192.168.1.63", "152.195.55.192" ], + "rule.name": "new_outbound_from_trust", "server.ip": "152.195.55.192", "server.port": 443, "service.type": "panw", @@ -2606,12 +2956,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2628,6 +2986,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "151.101.2.2", "panw.panos.destination.nat.port": 443, @@ -2649,6 +3008,7 @@ "192.168.1.63", "151.101.2.2" ], + "rule.name": "new_outbound_from_trust", "server.ip": "151.101.2.2", "server.port": 443, "service.type": "panw", @@ -2683,12 +3043,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2705,6 +3073,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.192.7.152", "panw.panos.destination.nat.port": 443, @@ -2726,6 +3095,7 @@ "192.168.1.63", "54.192.7.152" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.192.7.152", "server.port": 443, "service.type": "panw", @@ -2760,12 +3130,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2782,6 +3160,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -2803,6 +3182,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -2837,12 +3217,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2859,6 +3247,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -2880,6 +3269,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -2914,12 +3304,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2936,6 +3334,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -2957,6 +3356,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -2991,12 +3391,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3013,6 +3421,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3034,6 +3443,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3068,12 +3478,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3090,6 +3508,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3111,6 +3530,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3145,12 +3565,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3167,6 +3595,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3188,6 +3617,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3222,12 +3652,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3244,6 +3682,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3265,6 +3704,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3299,12 +3739,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3321,6 +3769,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3342,6 +3791,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3376,12 +3826,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3398,6 +3856,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3419,6 +3878,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3453,12 +3913,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3475,6 +3943,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3496,6 +3965,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3530,12 +4000,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3552,6 +4030,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3573,6 +4052,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3607,12 +4087,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3629,6 +4117,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.4.120.175", "panw.panos.destination.nat.port": 443, @@ -3650,6 +4139,7 @@ "192.168.1.63", "52.4.120.175" ], + "rule.name": "new_outbound_from_trust", "server.ip": "52.4.120.175", "server.port": 443, "service.type": "panw", @@ -3684,12 +4174,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3706,6 +4204,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "216.58.194.98", "panw.panos.destination.nat.port": 443, @@ -3727,6 +4226,7 @@ "192.168.1.63", "216.58.194.98" ], + "rule.name": "new_outbound_from_trust", "server.ip": "216.58.194.98", "server.port": 443, "service.type": "panw", @@ -3758,12 +4258,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3780,6 +4288,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -3801,6 +4310,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -3832,12 +4342,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3854,6 +4372,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -3875,6 +4394,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -3906,12 +4426,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3928,6 +4456,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -3949,6 +4478,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -3980,12 +4510,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4002,6 +4540,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -4023,6 +4562,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -4054,12 +4594,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4076,6 +4624,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -4097,6 +4646,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -4128,12 +4678,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4150,6 +4708,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -4171,6 +4730,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -4202,12 +4762,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4224,6 +4792,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -4245,6 +4814,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -4276,12 +4846,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4298,6 +4876,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -4319,6 +4898,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -4350,12 +4930,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4372,6 +4960,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -4393,6 +4982,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -4424,12 +5014,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4446,6 +5044,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.72.145.245", "panw.panos.destination.nat.port": 443, @@ -4467,6 +5066,7 @@ "192.168.1.63", "23.72.145.245" ], + "rule.name": "new_outbound_from_trust", "server.ip": "23.72.145.245", "server.port": 443, "service.type": "panw", @@ -4501,12 +5101,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4523,6 +5131,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -4544,6 +5153,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -4578,12 +5188,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4600,6 +5218,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -4621,6 +5240,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -4655,12 +5275,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4677,6 +5305,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -4698,6 +5327,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -4732,12 +5362,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4754,6 +5392,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -4775,6 +5414,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -4809,12 +5449,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4831,6 +5479,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -4852,6 +5501,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -4886,12 +5536,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4908,6 +5566,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -4929,6 +5588,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -4963,12 +5623,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4985,6 +5653,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5006,6 +5675,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5040,12 +5710,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5062,6 +5740,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5083,6 +5762,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5117,12 +5797,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5139,6 +5827,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5160,6 +5849,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5194,12 +5884,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5216,6 +5914,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5237,6 +5936,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5271,12 +5971,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5293,6 +6001,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5314,6 +6023,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5348,12 +6058,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5370,6 +6088,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5391,6 +6110,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5425,12 +6145,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5447,6 +6175,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5468,6 +6197,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5502,12 +6232,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5524,6 +6262,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5545,6 +6284,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5579,12 +6319,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5601,6 +6349,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5622,6 +6371,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", @@ -5656,12 +6406,20 @@ "destination.nat.port": 443, "destination.port": 443, "event.action": "url_filtering", - "event.category": "security_threat", + "event.category": [ + "security_threat", + "intrusion_detection", + "network" + ], "event.dataset": "panw.panos", + "event.kind": "alert", "event.module": "panw", - "event.outcome": "block-url", + "event.outcome": "success", "event.severity": 5, "event.timezone": "-02:00", + "event.type": [ + "denied" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5678,6 +6436,7 @@ "network.transport": "tcp", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "block-url", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.209.101.70", "panw.panos.destination.nat.port": 443, @@ -5699,6 +6458,7 @@ "192.168.1.63", "54.209.101.70" ], + "rule.name": "new_outbound_from_trust", "server.ip": "54.209.101.70", "server.port": 443, "service.type": "panw", diff --git a/x-pack/filebeat/module/panw/panos/test/traffic.log-expected.json b/x-pack/filebeat/module/panw/panos/test/traffic.log-expected.json index 563290f9dba..9e1333f9fb8 100644 --- a/x-pack/filebeat/module/panw/panos/test/traffic.log-expected.json +++ b/x-pack/filebeat/module/panw/panos/test/traffic.log-expected.json @@ -19,14 +19,23 @@ "destination.packets": 16, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 586000000000, "event.end": "2018-11-30T16:08:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T15:59:04.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -44,6 +53,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "184.51.253.152", "panw.panos.destination.nat.port": 443, @@ -63,6 +73,7 @@ "192.168.1.63", "184.51.253.152" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 5976, "server.ip": "184.51.253.152", "server.packets": 16, @@ -99,14 +110,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:55.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -124,6 +144,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -143,6 +164,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -182,14 +204,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2018-11-30T16:08:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:51.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -207,6 +238,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "17.253.3.202", "panw.panos.destination.nat.port": 80, @@ -226,6 +258,7 @@ "192.168.1.63", "17.253.3.202" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 1035, "server.ip": "17.253.3.202", "server.packets": 6, @@ -262,14 +295,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:01.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:01.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -287,6 +329,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -306,6 +349,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -345,14 +389,23 @@ "destination.packets": 5, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:07:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:07:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -370,6 +423,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "216.58.194.99", "panw.panos.destination.nat.port": 443, @@ -389,6 +443,7 @@ "192.168.1.63", "216.58.194.99" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 1613, "server.ip": "216.58.194.99", "server.packets": 5, @@ -425,14 +480,23 @@ "destination.packets": 62, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 85000000000, "event.end": "2018-11-30T16:08:58.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:07:33.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -450,6 +514,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "209.234.224.22", "panw.panos.destination.nat.port": 443, @@ -469,6 +534,7 @@ "192.168.1.63", "209.234.224.22" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 21111, "server.ip": "209.234.224.22", "server.packets": 62, @@ -505,14 +571,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:07.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:07.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -530,6 +605,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -549,6 +625,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -585,14 +662,23 @@ "destination.packets": 7, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 15000000000, "event.end": "2018-11-30T16:07:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:07:04.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -610,6 +696,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "172.217.2.238", "panw.panos.destination.nat.port": 443, @@ -629,6 +716,7 @@ "192.168.1.63", "172.217.2.238" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 3732, "server.ip": "172.217.2.238", "server.packets": 7, @@ -665,14 +753,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:50.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:50.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -690,6 +787,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -709,6 +807,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 221, "server.ip": "8.8.8.8", "server.packets": 1, @@ -745,14 +844,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:51.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:51.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -770,6 +878,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -789,6 +898,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 221, "server.ip": "8.8.8.8", "server.packets": 1, @@ -825,14 +935,23 @@ "destination.packets": 16, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 593000000000, "event.end": "2018-11-30T16:08:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T15:58:59.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -850,6 +969,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "17.249.60.78", "panw.panos.destination.nat.port": 443, @@ -869,6 +989,7 @@ "192.168.1.63", "17.249.60.78" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 5469, "server.ip": "17.249.60.78", "server.packets": 16, @@ -905,14 +1026,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -930,6 +1060,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -949,6 +1080,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 224, "server.ip": "8.8.8.8", "server.packets": 1, @@ -985,14 +1117,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1010,6 +1151,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -1029,6 +1171,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 117, "server.ip": "8.8.8.8", "server.packets": 1, @@ -1065,14 +1208,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1090,6 +1242,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -1109,6 +1262,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 307, "server.ip": "8.8.8.8", "server.packets": 1, @@ -1145,14 +1299,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:52.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:52.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1170,6 +1333,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -1189,6 +1353,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 365, "server.ip": "8.8.8.8", "server.packets": 1, @@ -1225,14 +1390,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1250,6 +1424,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -1269,6 +1444,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -1305,14 +1481,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2018-11-30T16:08:55.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1330,6 +1515,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -1349,6 +1535,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 161, "server.ip": "8.8.8.8", "server.packets": 1, @@ -1385,14 +1572,23 @@ "destination.packets": 14, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 17000000000, "event.end": "2018-11-30T16:09:11.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1410,6 +1606,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "98.138.49.44", "panw.panos.destination.nat.port": 443, @@ -1429,6 +1626,7 @@ "192.168.1.63", "98.138.49.44" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 7805, "server.ip": "98.138.49.44", "server.packets": 14, @@ -1465,14 +1663,23 @@ "destination.packets": 13, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 17000000000, "event.end": "2018-11-30T16:09:11.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:54.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1490,6 +1697,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "72.30.3.43", "panw.panos.destination.nat.port": 443, @@ -1509,6 +1717,7 @@ "192.168.1.63", "72.30.3.43" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 6106, "server.ip": "72.30.3.43", "server.packets": 13, @@ -1545,14 +1754,23 @@ "destination.packets": 2, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:15.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:15.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1570,6 +1788,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -1589,6 +1808,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 196, "server.ip": "8.8.8.8", "server.packets": 2, @@ -1625,14 +1845,23 @@ "destination.packets": 19, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 116000000000, "event.end": "2018-11-30T16:09:12.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:07:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1650,6 +1879,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "172.217.9.142", "panw.panos.destination.nat.port": 80, @@ -1669,6 +1899,7 @@ "192.168.1.63", "172.217.9.142" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 3245, "server.ip": "172.217.9.142", "server.packets": 19, @@ -1705,14 +1936,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:08:57.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:08:57.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1730,6 +1970,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -1749,6 +1990,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 179, "server.ip": "8.8.8.8", "server.packets": 1, @@ -1788,14 +2030,23 @@ "destination.packets": 13, "destination.port": 443, "event.action": "flow_started", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "start", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1813,6 +2064,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.84.80.198", "panw.panos.destination.nat.port": 443, @@ -1832,6 +2084,7 @@ "192.168.1.63", "54.84.80.198" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 4537, "server.ip": "54.84.80.198", "server.packets": 13, @@ -1869,14 +2122,23 @@ "destination.packets": 8, "destination.port": 4282, "event.action": "flow_dropped", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 13000000000, "event.end": "2018-11-30T16:09:25.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:12.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "denied", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1894,6 +2156,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "199.167.55.52", "panw.panos.destination.nat.port": 4282, @@ -1913,6 +2176,7 @@ "192.168.1.63", "199.167.55.52" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 0, "server.ip": "199.167.55.52", "server.packets": 8, @@ -1949,14 +2213,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_denied", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "denied", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -1974,6 +2247,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -1993,6 +2267,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -2028,14 +2303,21 @@ "destination.nat.port": 53, "destination.packets": 1, "destination.port": 53, - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:02.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:02.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2053,6 +2335,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -2072,6 +2355,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 130, "server.ip": "8.8.8.8", "server.packets": 1, @@ -2107,14 +2391,21 @@ "destination.nat.port": 443, "destination.packets": 6, "destination.port": 443, - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 15000000000, "event.end": "2018-11-30T16:07:35.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:07:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2132,6 +2423,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "172.217.9.142", "panw.panos.destination.nat.port": 443, @@ -2151,6 +2443,7 @@ "192.168.1.63", "172.217.9.142" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 1991, "server.ip": "172.217.9.142", "server.packets": 6, @@ -2187,14 +2480,23 @@ "destination.packets": 8, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:21.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2212,6 +2514,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "151.101.2.2", "panw.panos.destination.nat.port": 443, @@ -2231,6 +2534,7 @@ "192.168.1.63", "151.101.2.2" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 523, "server.ip": "151.101.2.2", "server.packets": 8, @@ -2270,14 +2574,23 @@ "destination.packets": 5, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:07:36.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:07:36.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2295,6 +2608,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "216.58.194.66", "panw.panos.destination.nat.port": 443, @@ -2314,6 +2628,7 @@ "192.168.1.63", "216.58.194.66" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 2428, "server.ip": "216.58.194.66", "server.packets": 5, @@ -2350,14 +2665,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:25.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:25.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2375,6 +2699,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -2394,6 +2719,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -2430,14 +2756,23 @@ "destination.packets": 2, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:25.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:25.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2455,6 +2790,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -2474,6 +2810,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 196, "server.ip": "8.8.8.8", "server.packets": 2, @@ -2510,14 +2847,23 @@ "destination.packets": 12, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:22.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:22.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2535,6 +2881,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "184.51.253.193", "panw.panos.destination.nat.port": 443, @@ -2554,6 +2901,7 @@ "192.168.1.63", "184.51.253.193" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 5003, "server.ip": "184.51.253.193", "server.packets": 12, @@ -2590,14 +2938,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:08.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:08.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2615,6 +2972,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -2634,6 +2992,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 171, "server.ip": "8.8.8.8", "server.packets": 1, @@ -2671,14 +3030,23 @@ "destination.packets": 1, "destination.port": 4282, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:33.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:33.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2696,6 +3064,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "199.167.55.52", "panw.panos.destination.nat.port": 4282, @@ -2715,6 +3084,7 @@ "192.168.1.63", "199.167.55.52" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 0, "server.ip": "199.167.55.52", "server.packets": 1, @@ -2754,14 +3124,23 @@ "destination.packets": 11, "destination.port": 17472, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:25.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:25.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2779,6 +3158,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "199.167.52.219", "panw.panos.destination.nat.port": 17472, @@ -2798,6 +3178,7 @@ "192.168.1.63", "199.167.52.219" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 2316, "server.ip": "199.167.52.219", "server.packets": 11, @@ -2837,14 +3218,23 @@ "destination.packets": 19, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 4000000000, "event.end": "2018-11-30T16:09:25.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2862,6 +3252,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.71.117.196", "panw.panos.destination.nat.port": 443, @@ -2881,6 +3272,7 @@ "192.168.1.63", "52.71.117.196" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 13966, "server.ip": "52.71.117.196", "server.packets": 19, @@ -2917,14 +3309,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:12.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:12.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -2942,6 +3343,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -2961,6 +3363,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 244, "server.ip": "8.8.8.8", "server.packets": 1, @@ -2997,14 +3400,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:12.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:12.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3022,6 +3434,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -3041,6 +3454,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 205, "server.ip": "8.8.8.8", "server.packets": 1, @@ -3080,14 +3494,23 @@ "destination.packets": 24, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 8000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3105,6 +3528,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "35.186.194.41", "panw.panos.destination.nat.port": 443, @@ -3124,6 +3548,7 @@ "192.168.1.63", "35.186.194.41" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 2302, "server.ip": "35.186.194.41", "server.packets": 24, @@ -3159,14 +3584,23 @@ "destination.packets": 63, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 8000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3184,6 +3618,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "35.201.124.9", "panw.panos.destination.nat.port": 443, @@ -3203,6 +3638,7 @@ "192.168.1.63", "35.201.124.9" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 6757, "server.ip": "35.201.124.9", "server.packets": 63, @@ -3242,14 +3678,23 @@ "destination.packets": 17, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 6000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3267,6 +3712,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "100.24.131.237", "panw.panos.destination.nat.port": 443, @@ -3286,6 +3732,7 @@ "192.168.1.63", "100.24.131.237" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 9007, "server.ip": "100.24.131.237", "server.packets": 17, @@ -3322,14 +3769,23 @@ "destination.packets": 8, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 13000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3347,6 +3803,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "184.51.252.247", "panw.panos.destination.nat.port": 443, @@ -3366,6 +3823,7 @@ "192.168.1.63", "184.51.252.247" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 661, "server.ip": "184.51.252.247", "server.packets": 8, @@ -3405,14 +3863,23 @@ "destination.packets": 15, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 8000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3430,6 +3897,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "35.190.88.148", "panw.panos.destination.nat.port": 443, @@ -3449,6 +3917,7 @@ "192.168.1.63", "35.190.88.148" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 11136, "server.ip": "35.190.88.148", "server.packets": 15, @@ -3488,14 +3957,23 @@ "destination.packets": 15, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 8000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3513,6 +3991,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "35.186.243.83", "panw.panos.destination.nat.port": 443, @@ -3532,6 +4011,7 @@ "192.168.1.63", "35.186.243.83" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 11136, "server.ip": "35.186.243.83", "server.packets": 15, @@ -3568,14 +4048,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:12.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:12.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3593,6 +4082,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -3612,6 +4102,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 182, "server.ip": "8.8.8.8", "server.packets": 1, @@ -3648,14 +4139,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:12.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:12.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3673,6 +4173,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -3692,6 +4193,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 90, "server.ip": "8.8.8.8", "server.packets": 1, @@ -3731,14 +4233,23 @@ "destination.packets": 17, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 6000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3756,6 +4267,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "100.24.165.74", "panw.panos.destination.nat.port": 443, @@ -3775,6 +4287,7 @@ "192.168.1.63", "100.24.165.74" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 6669, "server.ip": "100.24.165.74", "server.packets": 17, @@ -3811,14 +4324,23 @@ "destination.packets": 8, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 13000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3836,6 +4358,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "184.51.252.247", "panw.panos.destination.nat.port": 443, @@ -3855,6 +4378,7 @@ "192.168.1.63", "184.51.252.247" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 661, "server.ip": "184.51.252.247", "server.packets": 8, @@ -3890,14 +4414,23 @@ "destination.packets": 15, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 8000000000, "event.end": "2018-11-30T16:09:27.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3915,6 +4448,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "35.201.94.140", "panw.panos.destination.nat.port": 443, @@ -3934,6 +4468,7 @@ "192.168.1.63", "35.201.94.140" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 11136, "server.ip": "35.201.94.140", "server.packets": 15, @@ -3970,14 +4505,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:31.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:31.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -3995,6 +4539,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -4012,6 +4557,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -4048,14 +4594,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4073,6 +4628,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4092,6 +4648,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 144, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4128,14 +4685,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4153,6 +4719,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4172,6 +4739,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 206, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4208,14 +4776,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4233,6 +4810,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4252,6 +4830,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 206, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4288,14 +4867,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4313,6 +4901,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4332,6 +4921,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 169, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4368,14 +4958,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4393,6 +4992,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4412,6 +5012,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 132, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4448,14 +5049,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4473,6 +5083,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4492,6 +5103,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 127, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4528,14 +5140,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4553,6 +5174,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4572,6 +5194,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 105, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4608,14 +5231,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4633,6 +5265,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4652,6 +5285,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 172, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4688,14 +5322,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4713,6 +5356,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4732,6 +5376,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 134, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4768,14 +5413,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4793,6 +5447,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4812,6 +5467,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 179, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4848,14 +5504,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4873,6 +5538,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4892,6 +5558,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 218, "server.ip": "8.8.8.8", "server.packets": 1, @@ -4928,14 +5595,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -4953,6 +5629,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -4972,6 +5649,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 172, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5008,14 +5686,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:13.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5033,6 +5720,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5052,6 +5740,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 305, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5091,14 +5780,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5116,6 +5814,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "66.28.0.45", "panw.panos.destination.nat.port": 53, @@ -5135,6 +5834,7 @@ "192.168.1.63", "66.28.0.45" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 527, "server.ip": "66.28.0.45", "server.packets": 1, @@ -5171,14 +5871,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5196,6 +5905,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5215,6 +5925,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 153, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5251,14 +5962,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5276,6 +5996,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5295,6 +6016,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 169, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5331,14 +6053,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5356,6 +6087,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5375,6 +6107,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 128, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5411,14 +6144,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5436,6 +6178,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5455,6 +6198,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 181, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5491,14 +6235,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5516,6 +6269,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5535,6 +6289,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 121, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5574,14 +6329,23 @@ "destination.packets": 6, "destination.port": 80, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:29.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:29.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5599,6 +6363,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "23.52.174.25", "panw.panos.destination.nat.port": 80, @@ -5618,6 +6383,7 @@ "192.168.1.63", "23.52.174.25" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 1246, "server.ip": "23.52.174.25", "server.packets": 6, @@ -5654,14 +6420,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 1000000000, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:13.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5679,6 +6454,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5698,6 +6474,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 315, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5734,14 +6511,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5759,6 +6545,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5778,6 +6565,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 130, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5817,14 +6605,23 @@ "destination.packets": 5, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 12000000000, "event.end": "2018-11-30T16:09:29.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:17.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5842,6 +6639,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "54.230.5.228", "panw.panos.destination.nat.port": 443, @@ -5861,6 +6659,7 @@ "192.168.1.63", "54.230.5.228" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 288, "server.ip": "54.230.5.228", "server.packets": 5, @@ -5897,14 +6696,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:14.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:14.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -5922,6 +6730,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -5941,6 +6750,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 149, "server.ip": "8.8.8.8", "server.packets": 1, @@ -5977,14 +6787,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:15.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:15.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6002,6 +6821,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6021,6 +6841,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 202, "server.ip": "8.8.8.8", "server.packets": 1, @@ -6057,14 +6878,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:15.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:15.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6082,6 +6912,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6101,6 +6932,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 195, "server.ip": "8.8.8.8", "server.packets": 1, @@ -6137,14 +6969,23 @@ "destination.packets": 1, "destination.port": 123, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:15.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:15.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6162,6 +7003,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "208.83.246.20", "panw.panos.destination.nat.port": 123, @@ -6181,6 +7023,7 @@ "192.168.1.63", "208.83.246.20" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 90, "server.ip": "208.83.246.20", "server.packets": 1, @@ -6217,14 +7060,22 @@ "destination.packets": 2, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:16.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "drop-icmp", + "event.outcome": "success", "event.start": "2018-11-30T16:09:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6242,6 +7093,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "drop-icmp", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6261,6 +7113,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 192, "server.ip": "8.8.8.8", "server.packets": 2, @@ -6297,14 +7150,22 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:16.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "reset-client", + "event.outcome": "success", "event.start": "2018-11-30T16:09:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6322,6 +7183,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "reset-client", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6341,6 +7203,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 208, "server.ip": "8.8.8.8", "server.packets": 1, @@ -6377,14 +7240,22 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:16.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "reset-server", + "event.outcome": "success", "event.start": "2018-11-30T16:09:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6402,6 +7273,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "reset-server", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6421,6 +7293,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 100, "server.ip": "8.8.8.8", "server.packets": 1, @@ -6459,14 +7332,22 @@ "destination.packets": 13, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 10000000000, "event.end": "2018-11-30T16:09:31.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "reset-both", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6484,6 +7365,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "reset-both", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "35.185.88.112", "panw.panos.destination.nat.port": 443, @@ -6503,6 +7385,7 @@ "192.168.1.63", "35.185.88.112" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 7237, "server.ip": "35.185.88.112", "server.packets": 13, @@ -6539,14 +7422,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:16.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6564,6 +7456,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6583,6 +7476,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 109, "server.ip": "8.8.8.8", "server.packets": 1, @@ -6619,14 +7513,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:16.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6644,6 +7547,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6663,6 +7567,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 116, "server.ip": "8.8.8.8", "server.packets": 1, @@ -6699,14 +7604,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:16.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:16.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6724,6 +7638,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -6743,6 +7658,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 96, "server.ip": "8.8.8.8", "server.packets": 1, @@ -6782,14 +7698,23 @@ "destination.packets": 8, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 11000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6807,6 +7732,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "50.19.85.24", "panw.panos.destination.nat.port": 443, @@ -6826,6 +7752,7 @@ "192.168.1.63", "50.19.85.24" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 654, "server.ip": "50.19.85.24", "server.packets": 8, @@ -6865,14 +7792,23 @@ "destination.packets": 8, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 11000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6890,6 +7826,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "50.19.85.24", "panw.panos.destination.nat.port": 443, @@ -6909,6 +7846,7 @@ "192.168.1.63", "50.19.85.24" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 654, "server.ip": "50.19.85.24", "server.packets": 8, @@ -6948,14 +7886,23 @@ "destination.packets": 8, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 11000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -6973,6 +7920,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "50.19.85.24", "panw.panos.destination.nat.port": 443, @@ -6992,6 +7940,7 @@ "192.168.1.63", "50.19.85.24" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 654, "server.ip": "50.19.85.24", "server.packets": 8, @@ -7028,14 +7977,23 @@ "destination.packets": 12, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 11000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7053,6 +8011,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "104.254.150.9", "panw.panos.destination.nat.port": 443, @@ -7072,6 +8031,7 @@ "192.168.1.63", "104.254.150.9" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 7820, "server.ip": "104.254.150.9", "server.packets": 12, @@ -7111,14 +8071,23 @@ "destination.packets": 8, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 11000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:21.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7136,6 +8105,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "50.19.85.24", "panw.panos.destination.nat.port": 443, @@ -7155,6 +8125,7 @@ "192.168.1.63", "50.19.85.24" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 654, "server.ip": "50.19.85.24", "server.packets": 8, @@ -7194,14 +8165,23 @@ "destination.packets": 4, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 12000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7219,6 +8199,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.0.218.108", "panw.panos.destination.nat.port": 443, @@ -7238,6 +8219,7 @@ "192.168.1.63", "52.0.218.108" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 214, "server.ip": "52.0.218.108", "server.packets": 4, @@ -7277,14 +8259,23 @@ "destination.packets": 4, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 12000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7302,6 +8293,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "52.6.117.19", "panw.panos.destination.nat.port": 443, @@ -7321,6 +8313,7 @@ "192.168.1.63", "52.6.117.19" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 214, "server.ip": "52.6.117.19", "server.packets": 4, @@ -7360,14 +8353,23 @@ "destination.packets": 4, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 12000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7385,6 +8387,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "34.238.96.22", "panw.panos.destination.nat.port": 443, @@ -7404,6 +8407,7 @@ "192.168.1.63", "34.238.96.22" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 214, "server.ip": "34.238.96.22", "server.packets": 4, @@ -7443,14 +8447,23 @@ "destination.packets": 4, "destination.port": 443, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 12000000000, "event.end": "2018-11-30T16:09:32.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:20.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7468,6 +8481,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "130.211.47.17", "panw.panos.destination.nat.port": 443, @@ -7487,6 +8501,7 @@ "192.168.1.63", "130.211.47.17" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 280, "server.ip": "130.211.47.17", "server.packets": 4, @@ -7523,14 +8538,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:18.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:18.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7548,6 +8572,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -7567,6 +8592,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 172, "server.ip": "8.8.8.8", "server.packets": 1, @@ -7603,14 +8629,23 @@ "destination.packets": 6, "destination.port": 0, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:37.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:37.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7628,6 +8663,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 0, @@ -7647,6 +8683,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 588, "server.ip": "8.8.8.8", "server.packets": 6, @@ -7683,14 +8720,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7708,6 +8754,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -7727,6 +8774,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 94, "server.ip": "8.8.8.8", "server.packets": 1, @@ -7763,14 +8811,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7788,6 +8845,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -7807,6 +8865,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 170, "server.ip": "8.8.8.8", "server.packets": 1, @@ -7843,14 +8902,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7868,6 +8936,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -7887,6 +8956,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 94, "server.ip": "8.8.8.8", "server.packets": 1, @@ -7923,14 +8993,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -7948,6 +9027,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -7967,6 +9047,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 94, "server.ip": "8.8.8.8", "server.packets": 1, @@ -8003,14 +9084,23 @@ "destination.packets": 1, "destination.port": 53, "event.action": "flow_terminated", - "event.category": "network_traffic", + "event.category": [ + "network_traffic", + "network" + ], "event.dataset": "panw.panos", "event.duration": 0, "event.end": "2018-11-30T16:09:19.000-02:00", + "event.kind": "event", "event.module": "panw", - "event.outcome": "allow", + "event.outcome": "success", "event.start": "2018-11-30T16:09:19.000-02:00", "event.timezone": "-02:00", + "event.type": [ + "allowed", + "end", + "connection" + ], "fileset.name": "panos", "input.type": "log", "labels.nat_translated": true, @@ -8028,6 +9118,7 @@ "network.type": "ipv4", "observer.hostname": "PA-220", "observer.serial_number": "012801096514", + "panw.panos.action": "allow", "panw.panos.destination.interface": "ethernet1/1", "panw.panos.destination.nat.ip": "8.8.8.8", "panw.panos.destination.nat.port": 53, @@ -8047,6 +9138,7 @@ "192.168.1.63", "8.8.8.8" ], + "rule.name": "new_outbound_from_trust", "server.bytes": 166, "server.ip": "8.8.8.8", "server.packets": 1, diff --git a/x-pack/filebeat/module/rabbitmq/log/ingest/pipeline.yml b/x-pack/filebeat/module/rabbitmq/log/ingest/pipeline.yml index b6bc5f57f63..58097c578d8 100644 --- a/x-pack/filebeat/module/rabbitmq/log/ingest/pipeline.yml +++ b/x-pack/filebeat/module/rabbitmq/log/ingest/pipeline.yml @@ -26,6 +26,9 @@ processors: - remove: field: - timestamp +- set: + field: event.kind + value: event on_failure: - set: field: error.message diff --git a/x-pack/filebeat/module/rabbitmq/log/test/test.log-expected.json b/x-pack/filebeat/module/rabbitmq/log/test/test.log-expected.json index 747b866dabe..0bdae14b894 100644 --- a/x-pack/filebeat/module/rabbitmq/log/test/test.log-expected.json +++ b/x-pack/filebeat/module/rabbitmq/log/test/test.log-expected.json @@ -2,6 +2,7 @@ { "@timestamp": "2019-04-03T11:13:15.076-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -15,6 +16,7 @@ { "@timestamp": "2019-04-03T11:13:15.510-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -31,6 +33,7 @@ { "@timestamp": "2019-04-03T11:13:15.512-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -47,6 +50,7 @@ { "@timestamp": "2019-04-12T10:00:53.458-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -60,6 +64,7 @@ { "@timestamp": "2019-04-12T10:00:53.550-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -76,6 +81,7 @@ { "@timestamp": "2019-04-12T10:00:53.550-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -89,6 +95,7 @@ { "@timestamp": "2019-04-12T10:00:54.553-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -102,6 +109,7 @@ { "@timestamp": "2019-04-12T10:00:54.555-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -115,6 +123,7 @@ { "@timestamp": "2019-04-12T10:00:54.567-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -128,6 +137,7 @@ { "@timestamp": "2019-04-12T10:00:54.567-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -141,6 +151,7 @@ { "@timestamp": "2019-04-12T10:00:54.568-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -154,6 +165,7 @@ { "@timestamp": "2019-04-12T10:00:54.569-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -167,6 +179,7 @@ { "@timestamp": "2019-04-12T10:00:54.579-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -180,6 +193,7 @@ { "@timestamp": "2019-04-12T10:00:54.588-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -193,6 +207,7 @@ { "@timestamp": "2019-04-12T10:00:54.589-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -206,6 +221,7 @@ { "@timestamp": "2019-04-12T10:00:54.598-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -219,6 +235,7 @@ { "@timestamp": "2019-04-12T10:00:54.606-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -232,6 +249,7 @@ { "@timestamp": "2019-04-12T10:00:54.615-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -245,6 +263,7 @@ { "@timestamp": "2019-04-12T10:00:54.615-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -261,6 +280,7 @@ { "@timestamp": "2019-04-12T10:01:01.031-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -277,6 +297,7 @@ { "@timestamp": "2019-04-12T10:11:15.094-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -290,6 +311,7 @@ { "@timestamp": "2019-04-12T10:11:15.101-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -303,6 +325,7 @@ { "@timestamp": "2019-04-12T10:19:14.450-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -319,6 +342,7 @@ { "@timestamp": "2019-04-12T10:19:14.450-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", @@ -332,6 +356,7 @@ { "@timestamp": "2019-04-12T10:19:14.451-02:00", "event.dataset": "rabbitmq.log", + "event.kind": "event", "event.module": "rabbitmq", "event.timezone": "-02:00", "fileset.name": "log", diff --git a/x-pack/filebeat/module/zeek/capture_loss/ingest/pipeline.json b/x-pack/filebeat/module/zeek/capture_loss/ingest/pipeline.json deleted file mode 100644 index 7d662ab7da1..00000000000 --- a/x-pack/filebeat/module/zeek/capture_loss/ingest/pipeline.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek capture_loss.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.capture_loss.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.capture_loss.ts" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/capture_loss/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/capture_loss/ingest/pipeline.yml new file mode 100644 index 00000000000..3c6171bc045 --- /dev/null +++ b/x-pack/filebeat/module/zeek/capture_loss/ingest/pipeline.yml @@ -0,0 +1,21 @@ +description: Pipeline for normalizing Zeek capture_loss.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.capture_loss.ts + formats: + - UNIX +- remove: + field: zeek.capture_loss.ts +- set: + field: event.kind + value: metric +- set: + field: event.type + value: info +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/capture_loss/manifest.yml b/x-pack/filebeat/module/zeek/capture_loss/manifest.yml index 97ae0f09d40..5349b0581c6 100644 --- a/x-pack/filebeat/module/zeek/capture_loss/manifest.yml +++ b/x-pack/filebeat/module/zeek/capture_loss/manifest.yml @@ -11,5 +11,5 @@ var: - name: tags default: [zeek.capture_loss] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/capture_loss.yml diff --git a/x-pack/filebeat/module/zeek/capture_loss/test/capture_loss-json.log-expected.json b/x-pack/filebeat/module/zeek/capture_loss/test/capture_loss-json.log-expected.json index 0ae18ff9c37..14f20eb3189 100644 --- a/x-pack/filebeat/module/zeek/capture_loss/test/capture_loss-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/capture_loss/test/capture_loss-json.log-expected.json @@ -2,7 +2,9 @@ { "@timestamp": "2019-09-10T16:19:28.465Z", "event.dataset": "zeek.capture_loss", + "event.kind": "metric", "event.module": "zeek", + "event.type": "info", "fileset.name": "capture_loss", "input.type": "log", "log.offset": 0, diff --git a/x-pack/filebeat/module/zeek/connection/config/connection.yml b/x-pack/filebeat/module/zeek/connection/config/connection.yml index 14c5b529708..f91d24f8020 100644 --- a/x-pack/filebeat/module/zeek/connection/config/connection.yml +++ b/x-pack/filebeat/module/zeek/connection/config/connection.yml @@ -75,20 +75,27 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network {{ if .community_id }} - if: equals.network.transport: icmp then: community_id: fields: - source_ip: source.address - destination_ip: destination.address icmp_type: zeek.connection.icmp.type icmp_code: zeek.connection.icmp.code else: community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/connection/ingest/pipeline.json b/x-pack/filebeat/module/zeek/connection/ingest/pipeline.json deleted file mode 100644 index a930fd08ec9..00000000000 --- a/x-pack/filebeat/module/zeek/connection/ingest/pipeline.json +++ /dev/null @@ -1,160 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek conn.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.connection.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.connection.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "script": { - "source": "ctx.event.duration = Math.round(ctx.temp.duration * params.scale)", - "params": { - "scale": 1000000000 - }, - "if": "ctx.temp?.duration != null" - } - }, - { - "remove": { - "field": "temp.duration", - "ignore_missing": true - } - }, - { - "script": { - "source": "if (ctx.zeek.connection.local_orig) ctx.tags.add(\"local_orig\");", - "if": "ctx.zeek.connection.local_orig != null" - } - }, - { - "script": { - "source": "if (ctx.zeek.connection.local_resp) ctx.tags.add(\"local_resp\");", - "if": "ctx.zeek.connection.local_resp != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "script": { - "source": "ctx.network.packets = ctx.source.packets + ctx.destination.packets", - "ignore_failure": true - } - }, - { - "script": { - "source": "ctx.network.bytes = ctx.source.bytes + ctx.destination.bytes", - "ignore_failure": true - } - }, - { - "script": { - "source": "if (ctx.zeek.connection.local_orig == true && ctx.zeek.connection.local_resp == true) {ctx.network.direction = \"internal\"} else if (ctx.zeek.connection.local_orig == true && ctx.zeek.connection.local_resp == false) {ctx.network.direction = \"outbound\"} else if (ctx.zeek.connection.local_orig == false && ctx.zeek.connection.local_resp == true) {ctx.network.direction = \"inbound\"} else {ctx.network.direction = \"external\"}" - } - }, - { - "geoip": { - "field": "destination.ip", - "target_field": "destination.geo" - } - }, - { - "geoip": { - "field": "source.ip", - "target_field": "source.geo" - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "source.ip", - "target_field": "source.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "destination.ip", - "target_field": "destination.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.asn", - "target_field": "source.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.organization_name", - "target_field": "source.as.organization.name", - "ignore_missing": true - } - }, - { - "rename": { - "field": "destination.as.asn", - "target_field": "destination.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "destination.as.organization_name", - "target_field": "destination.as.organization.name", - "ignore_missing": true - } - }, - { - "script": { - "source": "if (ctx.zeek.connection.state == \"S0\") {ctx.zeek.connection.state_message = \"Connection attempt seen, no reply.\"} else if (ctx.zeek.connection.state == \"S1\") {ctx.zeek.connection.state_message = \"Connection established, not terminated.\"} else if (ctx.zeek.connection.state == \"SF\") {ctx.zeek.connection.state_message = \"Normal establishment and termination.\"} else if (ctx.zeek.connection.state == \"REJ\") {ctx.zeek.connection.state_message = \"Connection attempt rejected.\"} else if (ctx.zeek.connection.state == \"S2\") {ctx.zeek.connection.state_message = \" Connection established and close attempt by originator seen (but no reply from responder).\"} else if (ctx.zeek.connection.state == \"S3\") {ctx.zeek.connection.state_message = \"Connection established and close attempt by responder seen (but no reply from originator).\"} else if (ctx.zeek.connection.state == \"RSTO\") {ctx.zeek.connection.state_message = \"Connection established, originator aborted (sent a RST).\"} else if (ctx.zeek.connection.state == \"RSTR\") {ctx.zeek.connection.state_message = \"Responder sent a RST.\"} else if (ctx.zeek.connection.state == \"RSTOS0\") {ctx.zeek.connection.state_message = \"Originator sent a SYN followed by a RST, we never saw a SYN-ACK from the responder.\"} else if (ctx.zeek.connection.state == \"RSTRH\") {ctx.zeek.connection.state_message = \"Responder sent a SYN ACK followed by a RST, we never saw a SYN from the (purported) originator.\"} else if (ctx.zeek.connection.state == \"SH\") {ctx.zeek.connection.state_message = \"Originator sent a SYN followed by a FIN, we never saw a SYN ACK from the responder (hence the connection was 'half' open).\"} else if (ctx.zeek.connection.state == \"SHR\") {ctx.zeek.connection.state_message = \"Responder sent a SYN ACK followed by a FIN, we never saw a SYN from the originator.\"} else if (ctx.zeek.connection.state == \"OTH\") {ctx.zeek.connection.state_message = \"No SYN seen, just midstream traffic (a 'partial connection' that was not later closed).\"}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/connection/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/connection/ingest/pipeline.yml new file mode 100644 index 00000000000..b660079324a --- /dev/null +++ b/x-pack/filebeat/module/zeek/connection/ingest/pipeline.yml @@ -0,0 +1,187 @@ +description: Pipeline for normalizing Zeek conn.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.connection.ts + formats: + - UNIX +- remove: + field: zeek.connection.ts +- set: + field: event.id + value: '{{zeek.session_id}}' + if: ctx.zeek.session_id != null +- script: + source: ctx.event.duration = Math.round(ctx.temp.duration * params.scale) + params: + scale: 1000000000 + if: ctx.temp?.duration != null +- remove: + field: temp.duration + ignore_missing: true +- script: + source: if (ctx.zeek.connection.local_orig) ctx.tags.add("local_orig"); + if: ctx.zeek.connection.local_orig != null +- script: + source: if (ctx.zeek.connection.local_resp) ctx.tags.add("local_resp"); + if: ctx.zeek.connection.local_resp != null +- set: + field: source.ip + value: '{{source.address}}' +- append: + field: related.ip + value: '{{source.address}}' +- set: + field: destination.ip + value: '{{destination.address}}' +- append: + field: related.ip + value: '{{destination.address}}' +- script: + source: ctx.network.packets = ctx.source.packets + ctx.destination.packets + ignore_failure: true +- script: + source: ctx.network.bytes = ctx.source.bytes + ctx.destination.bytes + ignore_failure: true +- script: + source: >- + if (ctx?.zeek?.connection?.local_orig == true) { + if (ctx?.zeek?.connection?.local_resp == true) { + ctx.network.direction = "internal"; + } else { + ctx.network.direction = "outbound"; + } + } else { + if (ctx?.zeek?.connection?.local_resp == true) { + ctx.network.direction = "inbound"; + } else { + ctx.network.direction = "external"; + } + } +- geoip: + field: destination.ip + target_field: destination.geo +- geoip: + field: source.ip + target_field: source.geo +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- set: + field: event.kind + value: event +- append: + field: event.category + value: network +- script: + params: + S0: + conn_str: "Connection attempt seen, no reply." + types: + - connection + - start + S1: + conn_str: "Connection established, not terminated." + types: + - connection + - start + SF: + conn_str: "Normal establishment and termination." + types: + - connection + - start + - end + REG: + conn_str: "Connection attempt rejected." + types: + - connection + - start + - denied + S2: + conn_str: "Connection established and close attempt by originator seen (but no reply from responder)." + types: + - connection + - info + S3: + conn_str: "Connection established and close attempt by responder seen (but no reply from originator)." + types: + - connection + - info + RSTO: + conn_str: "Connection established, originator aborted (sent a RST)." + types: + - connection + - info + RSTR: + conn_str: "Responder sent a RST." + types: + - connection + - info + RSTOS0: + conn_str: "Originator sent a SYN followed by a RST, we never saw a SYN-ACK from the responder." + types: + - connection + - info + RSTRH: + conn_str: "Responder sent a SYN ACK followed by a RST, we never saw a SYN from the (purported) originator." + types: + - connection + - info + SH: + conn_str: "Originator sent a SYN followed by a FIN, we never saw a SYN ACK from the responder (hence the connection was 'half' open)." + types: + - connection + - info + SHR: + conn_str: "Responder sent a SYN ACK followed by a FIN, we never saw a SYN from the originator." + types: + - connection + - info + OTH: + conn_str: "No SYN seen, just midstream traffic (a 'partial connection' that was not later closed)." + types: + - connection + - info + source: >- + if (ctx?.zeek?.connection?.state == null) { + return; + } + if (params.containsKey(ctx.zeek.connection.state)) { + ctx.zeek.connection.state_message = params[ctx.zeek.connection.state]["conn_str"]; + ctx.event.type = params[ctx.zeek.connection.state]["types"]; + } +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/connection/manifest.yml b/x-pack/filebeat/module/zeek/connection/manifest.yml index 0361f0c89fa..0acad34d69c 100644 --- a/x-pack/filebeat/module/zeek/connection/manifest.yml +++ b/x-pack/filebeat/module/zeek/connection/manifest.yml @@ -13,7 +13,7 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/connection.yml requires.processors: diff --git a/x-pack/filebeat/module/zeek/connection/test/connection-json.log-expected.json b/x-pack/filebeat/module/zeek/connection/test/connection-json.log-expected.json index 4e5615a3a51..35a539b1493 100644 --- a/x-pack/filebeat/module/zeek/connection/test/connection-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/connection/test/connection-json.log-expected.json @@ -6,10 +6,20 @@ "destination.ip": "192.168.86.1", "destination.packets": 1, "destination.port": 53, + "event.category": [ + "network", + "network" + ], "event.dataset": "zeek.connection", "event.duration": 76967000, "event.id": "CAcJw21BbVedgFnYH3", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "start", + "end" + ], "fileset.name": "connection", "input.type": "log", "log.offset": 0, @@ -19,6 +29,10 @@ "network.packets": 2, "network.protocol": "dns", "network.transport": "udp", + "related.ip": [ + "192.168.86.167", + "192.168.86.1" + ], "service.type": "zeek", "source.address": "192.168.86.167", "source.bytes": 103, @@ -51,10 +65,20 @@ "destination.ip": "8.8.8.8", "destination.packets": 1, "destination.port": 53, + "event.category": [ + "network", + "network" + ], "event.dataset": "zeek.connection", "event.duration": 76967000, "event.id": "CAcJw21BbVedgFnYH4", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "start", + "end" + ], "fileset.name": "connection", "input.type": "log", "log.offset": 398, @@ -64,6 +88,10 @@ "network.packets": 2, "network.protocol": "dns", "network.transport": "udp", + "related.ip": [ + "192.168.86.167", + "8.8.8.8" + ], "service.type": "zeek", "source.address": "192.168.86.167", "source.bytes": 103, @@ -95,10 +123,20 @@ "destination.ip": "8.8.8.8", "destination.packets": 1, "destination.port": 53, + "event.category": [ + "network", + "network" + ], "event.dataset": "zeek.connection", "event.duration": 76967000, "event.id": "CAcJw21BbVedgFnYH5", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "start", + "end" + ], "fileset.name": "connection", "input.type": "log", "log.offset": 792, @@ -108,6 +146,10 @@ "network.packets": 2, "network.protocol": "dns", "network.transport": "udp", + "related.ip": [ + "4.4.2.2", + "8.8.8.8" + ], "service.type": "zeek", "source.address": "4.4.2.2", "source.as.number": 3356, @@ -137,9 +179,18 @@ "destination.bytes": 0, "destination.ip": "198.51.100.249", "destination.packets": 0, + "event.category": [ + "network", + "network" + ], "event.dataset": "zeek.connection", "event.id": "Cc6NJ3GRlfjE44I3h", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "info" + ], "fileset.name": "connection", "input.type": "log", "log.offset": 1181, @@ -148,6 +199,10 @@ "network.direction": "external", "network.packets": 1, "network.transport": "icmp", + "related.ip": [ + "192.0.2.205", + "198.51.100.249" + ], "service.type": "zeek", "source.address": "192.0.2.205", "source.bytes": 107, @@ -165,4 +220,4 @@ "zeek.connection.state_message": "No SYN seen, just midstream traffic (a 'partial connection' that was not later closed).", "zeek.session_id": "Cc6NJ3GRlfjE44I3h" } -] +] \ No newline at end of file diff --git a/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml b/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml index e7875bca0df..0ba1b0fc673 100644 --- a/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml +++ b/x-pack/filebeat/module/zeek/dce_rpc/config/dce_rpc.yml @@ -36,7 +36,23 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol + - info {{ if .community_id }} - community_id: {{ end }} diff --git a/x-pack/filebeat/module/zeek/dce_rpc/ingest/pipeline.json b/x-pack/filebeat/module/zeek/dce_rpc/ingest/pipeline.json deleted file mode 100644 index 0f274438186..00000000000 --- a/x-pack/filebeat/module/zeek/dce_rpc/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek dce_rpc.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.dce_rpc.ts", - "formats": ["UNIX"] - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "remove": { - "field": "zeek.dce_rpc.ts" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/dce_rpc/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/dce_rpc/ingest/pipeline.yml new file mode 100644 index 00000000000..1ecda252cc8 --- /dev/null +++ b/x-pack/filebeat/module/zeek/dce_rpc/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek dce_rpc.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.dce_rpc.ts + formats: + - UNIX +- remove: + field: zeek.dce_rpc.ts +- append: + field: related.ip + value: '{{source.ip}}' +- geoip: + field: source.ip + target_field: source.geo +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: '{{destination.ip}}' +- geoip: + field: destination.ip + target_field: destination.geo +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- set: + field: event.action + value: '{{zeek.dce_rpc.operation}}' + if: "ctx?.zeek?.dce_rpc?.operation != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/dce_rpc/manifest.yml b/x-pack/filebeat/module/zeek/dce_rpc/manifest.yml index 853c7084f7e..21ba27eac96 100644 --- a/x-pack/filebeat/module/zeek/dce_rpc/manifest.yml +++ b/x-pack/filebeat/module/zeek/dce_rpc/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/dce_rpc.yml diff --git a/x-pack/filebeat/module/zeek/dce_rpc/test/dce_rpc-json.log-expected.json b/x-pack/filebeat/module/zeek/dce_rpc/test/dce_rpc-json.log-expected.json index 881f30d1b79..6128801caa7 100644 --- a/x-pack/filebeat/module/zeek/dce_rpc/test/dce_rpc-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/dce_rpc/test/dce_rpc-json.log-expected.json @@ -4,14 +4,29 @@ "destination.address": "172.16.128.202", "destination.ip": "172.16.128.202", "destination.port": 445, + "event.action": "BrowserrQueryOtherDomains", + "event.category": [ + "network" + ], "event.dataset": "zeek.dce_rpc", "event.id": "CsNHVHa1lzFtvJzT8", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol", + "info" + ], "fileset.name": "dce_rpc", "input.type": "log", "log.offset": 0, + "network.community_id": "1:SJNAD5vtzZuhQjGtfaI8svTnyuw=", "network.protocol": "dce_rpc", "network.transport": "tcp", + "related.ip": [ + "172.16.133.6", + "172.16.128.202" + ], "service.type": "zeek", "source.address": "172.16.133.6", "source.ip": "172.16.133.6", diff --git a/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml b/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml index 5878c8d7894..97c45a17920 100644 --- a/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml +++ b/x-pack/filebeat/module/zeek/dhcp/config/dhcp.yml @@ -94,9 +94,27 @@ processors: fields: port: 67 + - convert: + fields: + - {from: "zeek.dhcp.address.client", to: "source.address"} + - {from: "zeek.dhcp.address.client", to: "source.ip", type: "ip"} + - {from: "zeek.dhcp.address.client", to: "client.address"} + - {from: "zeek.dhcp.address.server", to: "destination.address"} + - {from: "zeek.dhcp.address.server", to: "destination.ip", type: "ip"} + - {from: "zeek.dhcp.address.server", to: "server.address"} + - {from: "zeek.dhcp.domain", to: "network.name"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol + - info {{ if .community_id }} - community_id: - fields: - source.address: zeek.dhcp.address.client - destination.address: zeek.dhcp.address.server {{ end }} diff --git a/x-pack/filebeat/module/zeek/dhcp/ingest/pipeline.json b/x-pack/filebeat/module/zeek/dhcp/ingest/pipeline.json deleted file mode 100644 index 92c1a43dd4a..00000000000 --- a/x-pack/filebeat/module/zeek/dhcp/ingest/pipeline.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek dhcp.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.dhcp.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.dhcp.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.address", - "value": "{{zeek.dhcp.address.client}}", - "if": "ctx.zeek.dhcp.address?.client != null" - } - }, - { - "set": { - "field": "client.address", - "value": "{{zeek.dhcp.address.client}}", - "if": "ctx.zeek.dhcp.address?.client != null" - } - }, - { - "set": { - "field": "destination.address", - "value": "{{zeek.dhcp.address.server}}", - "if": "ctx.zeek.dhcp.address?.server != null" - } - }, - { - "set": { - "field": "server.address", - "value": "{{zeek.dhcp.address.server}}", - "if": "ctx.zeek.dhcp.address?.server != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}", - "if": "ctx.source?.address != null" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}", - "if": "ctx.destination?.address != null" - } - }, - { - "set": { - "field": "network.name", - "value": "{{zeek.dhcp.domain}}", - "if": "ctx.zeek.dhcp.domain != null" - } - } - ], - "on_failure": [{ - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/dhcp/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/dhcp/ingest/pipeline.yml new file mode 100644 index 00000000000..49df687ecc3 --- /dev/null +++ b/x-pack/filebeat/module/zeek/dhcp/ingest/pipeline.yml @@ -0,0 +1,27 @@ +description: Pipeline for normalizing Zeek dhcp.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.dhcp.ts + formats: + - UNIX +- remove: + field: zeek.dhcp.ts +- set: + field: event.id + value: '{{zeek.session_id}}' + if: ctx.zeek.session_id != null +- append: + field: related.ip + value: '{{source.ip}}' + if: 'ctx?.source?.ip != null' +- append: + field: related.ip + value: '{{destination.ip}}' + if: 'ctx?.destination?.ip != null' +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/dhcp/manifest.yml b/x-pack/filebeat/module/zeek/dhcp/manifest.yml index a09038725e3..7cb434b1955 100644 --- a/x-pack/filebeat/module/zeek/dhcp/manifest.yml +++ b/x-pack/filebeat/module/zeek/dhcp/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/dhcp.yml diff --git a/x-pack/filebeat/module/zeek/dhcp/test/dhcp-json.log-expected.json b/x-pack/filebeat/module/zeek/dhcp/test/dhcp-json.log-expected.json index 63fd7367dd8..ec36a36c503 100644 --- a/x-pack/filebeat/module/zeek/dhcp/test/dhcp-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/dhcp/test/dhcp-json.log-expected.json @@ -5,15 +5,29 @@ "destination.address": "192.168.199.254", "destination.ip": "192.168.199.254", "destination.port": 67, + "event.category": [ + "network" + ], "event.dataset": "zeek.dhcp", "event.id": "{0=CmWOt6VWaNGqXYcH6, 1=CLObLo4YHn0u23Tp8a}", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol", + "info" + ], "fileset.name": "dhcp", "input.type": "log", "log.offset": 0, + "network.community_id": "1:HsGjbon+HsK9xnMq+1A32BR9C4Y=", "network.name": "localdomain", "network.protocol": "dhcp", "network.transport": "udp", + "related.ip": [ + "192.168.199.132", + "192.168.199.254" + ], "server.address": "192.168.199.254", "service.type": "zeek", "source.address": "192.168.199.132", diff --git a/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml b/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml index 4dec34b4b59..d059b4c79f9 100644 --- a/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml +++ b/x-pack/filebeat/module/zeek/dnp3/config/dnp3.yml @@ -46,9 +46,23 @@ processors: ignore_missing: true fail_on_error: false + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol + - info {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/dnp3/ingest/pipeline.json b/x-pack/filebeat/module/zeek/dnp3/ingest/pipeline.json deleted file mode 100644 index 3f7e3c4baee..00000000000 --- a/x-pack/filebeat/module/zeek/dnp3/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek dnp3.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.dnp3.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.dnp3.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/dnp3/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/dnp3/ingest/pipeline.yml new file mode 100644 index 00000000000..ad4670dc350 --- /dev/null +++ b/x-pack/filebeat/module/zeek/dnp3/ingest/pipeline.yml @@ -0,0 +1,64 @@ +description: Pipeline for normalizing Zeek dnp3.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.dnp3.ts + formats: + - UNIX +- remove: + field: zeek.dnp3.ts +- set: + field: event.action + value: '{{zeek.dnp3.function.request}}' + if: "ctx?.zeek?.dnp3?.function?.request != null" +- set: + field: event.action + value: '{{zeek.dnp3.function.reply}}' + if: "ctx?.zeek?.dnp3?.function?.reply != null" +- lowercase: + field: event.action + ignore_missing: true +- geoip: + field: destination.ip + target_field: destination.geo +- geoip: + field: source.ip + target_field: source.geo +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/dnp3/manifest.yml b/x-pack/filebeat/module/zeek/dnp3/manifest.yml index 73488debb12..98de1c3af82 100644 --- a/x-pack/filebeat/module/zeek/dnp3/manifest.yml +++ b/x-pack/filebeat/module/zeek/dnp3/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/dnp3.yml diff --git a/x-pack/filebeat/module/zeek/dnp3/test/dnp3-json.log-expected.json b/x-pack/filebeat/module/zeek/dnp3/test/dnp3-json.log-expected.json index 040dabff377..fa386feb1ce 100644 --- a/x-pack/filebeat/module/zeek/dnp3/test/dnp3-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/dnp3/test/dnp3-json.log-expected.json @@ -4,9 +4,19 @@ "destination.address": "127.0.0.1", "destination.ip": "127.0.0.1", "destination.port": 20000, + "event.action": "read", + "event.category": [ + "network" + ], "event.dataset": "zeek.dnp3", "event.id": "CQV6tj1w1t4WzQpHoe", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol", + "info" + ], "fileset.name": "dnp3", "input.type": "log", "log.offset": 0, diff --git a/x-pack/filebeat/module/zeek/dns/config/dns.yml b/x-pack/filebeat/module/zeek/dns/config/dns.yml index 96e67d9f840..7b4c332f5df 100644 --- a/x-pack/filebeat/module/zeek/dns/config/dns.yml +++ b/x-pack/filebeat/module/zeek/dns/config/dns.yml @@ -13,6 +13,11 @@ processors: - decode_json_fields: fields: [event.original] target: zeek.dns + - registered_domain: + ignore_missing: true + ignore_failure: true + field: zeek.dns.query + target_field: dns.question.registered_domain - script: lang: javascript id: zeek_dns_flags @@ -105,12 +110,54 @@ processors: evt.Put("event.duration", rttSec * 1000000000); } + function addTopLevelDomain(evt) { + var rd = evt.Get("dns.question.registered_domain"); + if (!rd) { + return; + } + var firstPeriod = rd.indexOf("."); + if (firstPeriod == -1) { + return; + } + evt.Put("dns.question.top_level_domain", rd.substr(firstPeriod + 1)); + } + + function addEventOutcome(evt) { + var rcode = evt.Get("zeek.dns.rcode"); + if (rcode == null) { + return; + } + if (rcode == 0) { + evt.Put("event.outcome", "success"); + } else { + evt.Put("event.outcome", "failure"); + } + } + + function addRelatedIP(evt) { + var related = []; + var src = evt.Get("zeek.dns.id.orig_h"); + if (src != null) { + related.push(src); + } + var dst = evt.Get("zeek.dns.id.resp_h"); + if (dst != null) { + related.push(dst); + } + if (related.length > 0) { + evt.Put("related.ip", related); + } + } + function process(evt) { addDnsHeaderFlags(evt); addDnsQuestionClass(evt); addDnsAnswers(evt); setDnsType(evt); addEventDuration(evt); + addTopLevelDomain(evt); + addEventOutcome(evt); + addRelatedIP(evt); } - convert: ignore_missing: true @@ -136,13 +183,18 @@ processors: - {from: zeek.dns.query, to: dns.question.name} - {from: zeek.dns.qtype_name, to: dns.question.type} - {from: zeek.dns.rcode_name, to: dns.response_code} - - registered_domain: - ignore_missing: true - ignore_failure: true - field: dns.question.name - target_field: dns.question.registered_domain + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - info + - protocol {{ if .community_id }} - - community_id: ~ + - community_id: {{ end }} - timestamp: ignore_missing: true diff --git a/x-pack/filebeat/module/zeek/dns/test/dns-json.log-expected.json b/x-pack/filebeat/module/zeek/dns/test/dns-json.log-expected.json index a8e2cd94b3a..0c01c52e428 100644 --- a/x-pack/filebeat/module/zeek/dns/test/dns-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/dns/test/dns-json.log-expected.json @@ -26,22 +26,37 @@ "dns.question.class": "IN", "dns.question.name": "dd625ffb4fc54735b281862aa1cd6cd4.us-west1.gcp.cloud.es.io", "dns.question.registered_domain": "es.io", + "dns.question.top_level_domain": "io", "dns.question.type": "A", "dns.resolved_ip": [ "35.199.178.4" ], "dns.response_code": "NOERROR", "dns.type": "answer", + "event.category": [ + "network" + ], "event.dataset": "zeek.dns", "event.duration": 76967000, "event.id": "CAcJw21BbVedgFnYH3", + "event.kind": "event", "event.module": "zeek", "event.original": "{\"ts\":1547188415.857497,\"uid\":\"CAcJw21BbVedgFnYH3\",\"id.orig_h\":\"192.168.86.167\",\"id.orig_p\":38339,\"id.resp_h\":\"192.168.86.1\",\"id.resp_p\":53,\"proto\":\"udp\",\"trans_id\":15209,\"rtt\":0.076967,\"query\":\"dd625ffb4fc54735b281862aa1cd6cd4.us-west1.gcp.cloud.es.io\",\"qclass\":1,\"qclass_name\":\"C_INTERNET\",\"qtype\":1,\"qtype_name\":\"A\",\"rcode\":0,\"rcode_name\":\"NOERROR\",\"AA\":false,\"TC\":false,\"RD\":true,\"RA\":true,\"Z\":0,\"answers\":[\"proxy-production-us-west1.gcp.cloud.es.io\",\"proxy-production-us-west1-v1-009.gcp.cloud.es.io\",\"35.199.178.4\"],\"TTLs\":[119.0,119.0,59.0],\"rejected\":false}", + "event.outcome": "success", + "event.type": [ + "connection", + "info", + "protocol" + ], "fileset.name": "dns", "input.type": "log", "log.offset": 0, "network.community_id": "1:Z26DBGVYoBKQ1FT6qfPaAqBnJik=", "network.transport": "udp", + "related.ip": [ + "192.168.86.167", + "192.168.86.1" + ], "service.type": "zeek", "source.address": "192.168.86.167", "source.ip": "192.168.86.167", @@ -84,17 +99,31 @@ "dns.question.class": "IN", "dns.question.name": "_googlecast._tcp.local", "dns.question.registered_domain": "_tcp.local", + "dns.question.top_level_domain": "local", "dns.question.type": "PTR", "dns.type": "query", + "event.category": [ + "network" + ], "event.dataset": "zeek.dns", "event.id": "C19a1k4lTv46YMbeOk", + "event.kind": "event", "event.module": "zeek", "event.original": "{\"ts\":1567095830.680046,\"uid\":\"C19a1k4lTv46YMbeOk\",\"id.orig_h\":\"fe80::4ef:15cf:769f:ff21\",\"id.orig_p\":5353,\"id.resp_h\":\"ff02::fb\",\"id.resp_p\":5353,\"proto\":\"udp\",\"trans_id\":0,\"query\":\"_googlecast._tcp.local\",\"qclass\":1,\"qclass_name\":\"C_INTERNET\",\"qtype\":12,\"qtype_name\":\"PTR\",\"AA\":false,\"TC\":false,\"RD\":false,\"RA\":false,\"Z\":0,\"rejected\":false}", + "event.type": [ + "connection", + "info", + "protocol" + ], "fileset.name": "dns", "input.type": "log", "log.offset": 566, "network.community_id": "1:Jq0sRtlGSMjsvMBE1ZYybbR2tI0=", "network.transport": "udp", + "related.ip": [ + "fe80::4ef:15cf:769f:ff21", + "ff02::fb" + ], "service.type": "zeek", "source.address": "fe80::4ef:15cf:769f:ff21", "source.ip": "fe80::4ef:15cf:769f:ff21", @@ -130,17 +159,32 @@ "dns.id": 0, "dns.question.name": "_googlecast._tcp.local", "dns.question.registered_domain": "_tcp.local", + "dns.question.top_level_domain": "local", "dns.response_code": "NOERROR", "dns.type": "answer", + "event.category": [ + "network" + ], "event.dataset": "zeek.dns", "event.id": "CdiVAw7jJw6gsX5H", + "event.kind": "event", "event.module": "zeek", "event.original": "{\"ts\":1567095830.734329,\"uid\":\"CdiVAw7jJw6gsX5H\",\"id.orig_h\":\"192.168.86.237\",\"id.orig_p\":5353,\"id.resp_h\":\"224.0.0.251\",\"id.resp_p\":5353,\"proto\":\"udp\",\"trans_id\":0,\"query\":\"_googlecast._tcp.local\",\"rcode\":0,\"rcode_name\":\"NOERROR\",\"AA\":true,\"TC\":false,\"RD\":false,\"RA\":false,\"Z\":0,\"answers\":[\"bravia-4k-gb-5c89f865c9d569ab338815b35e3acc56._googlecast._tcp.local\"],\"TTLs\":[120.0],\"rejected\":false}", + "event.outcome": "success", + "event.type": [ + "connection", + "info", + "protocol" + ], "fileset.name": "dns", "input.type": "log", "log.offset": 909, "network.community_id": "1:QIR5YXlirWwWA18ZyY/RnvQoaic=", "network.transport": "udp", + "related.ip": [ + "192.168.86.237", + "224.0.0.251" + ], "service.type": "zeek", "source.address": "192.168.86.237", "source.ip": "192.168.86.237", diff --git a/x-pack/filebeat/module/zeek/dpd/config/dpd.yml b/x-pack/filebeat/module/zeek/dpd/config/dpd.yml index 9e6a0138ef2..0a31b70f6bd 100644 --- a/x-pack/filebeat/module/zeek/dpd/config/dpd.yml +++ b/x-pack/filebeat/module/zeek/dpd/config/dpd.yml @@ -36,10 +36,22 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.session_id", to: "event.id"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - info {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/dpd/ingest/pipeline.json b/x-pack/filebeat/module/zeek/dpd/ingest/pipeline.json deleted file mode 100644 index 7a8958013fc..00000000000 --- a/x-pack/filebeat/module/zeek/dpd/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek dpd.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.dpd.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.dpd.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/dpd/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/dpd/ingest/pipeline.yml new file mode 100644 index 00000000000..f30ff172fa8 --- /dev/null +++ b/x-pack/filebeat/module/zeek/dpd/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek dpd.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.dpd.ts + formats: + - UNIX +- remove: + field: zeek.dpd.ts +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/dpd/manifest.yml b/x-pack/filebeat/module/zeek/dpd/manifest.yml index b331bca2921..aeba0ef31fc 100644 --- a/x-pack/filebeat/module/zeek/dpd/manifest.yml +++ b/x-pack/filebeat/module/zeek/dpd/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/dpd.yml diff --git a/x-pack/filebeat/module/zeek/dpd/test/dpd-json.log-expected.json b/x-pack/filebeat/module/zeek/dpd/test/dpd-json.log-expected.json index d3f58dbd4e0..0d6173e172e 100644 --- a/x-pack/filebeat/module/zeek/dpd/test/dpd-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/dpd/test/dpd-json.log-expected.json @@ -4,14 +4,26 @@ "destination.address": "192.168.10.10", "destination.ip": "192.168.10.10", "destination.port": 445, + "event.category": [ + "network" + ], "event.dataset": "zeek.dpd", "event.id": "CRrT7S1ccw9H6hzCR", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "info" + ], "fileset.name": "dpd", "input.type": "log", "log.offset": 0, "network.community_id": "1:b+Szw+ia464igf5e+MwW1WUzw9Y=", "network.transport": "tcp", + "related.ip": [ + "192.168.10.31", + "192.168.10.10" + ], "service.type": "zeek", "source.address": "192.168.10.31", "source.ip": "192.168.10.31", diff --git a/x-pack/filebeat/module/zeek/files/config/files.yml b/x-pack/filebeat/module/zeek/files/config/files.yml index 7148b82a481..74259307f41 100644 --- a/x-pack/filebeat/module/zeek/files/config/files.yml +++ b/x-pack/filebeat/module/zeek/files/config/files.yml @@ -15,9 +15,25 @@ processors: fields: - from: "json" to: "zeek.files" - - from: "zeek.files.conn_uids" to: "zeek.files.session_ids" - ignore_missing: true fail_on_error: false + - convert: + fields: + - {from: "zeek.files.mime_type", to: "file.mime_type"} + - {from: "zeek.files.filename", to: "file.name"} + - {from: "zeek.files.total_bytes", to: "file.size"} + - {from: "zeek.files.md5", to: "file.hash.md5"} + - {from: "zeek.files.sha1", to: "file.hash.sha1"} + - {from: "zeek.files.sha256", to: "file.hash.sha256"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - file + type: + - info diff --git a/x-pack/filebeat/module/zeek/files/ingest/pipeline.json b/x-pack/filebeat/module/zeek/files/ingest/pipeline.json deleted file mode 100644 index 1c47b4d0b42..00000000000 --- a/x-pack/filebeat/module/zeek/files/ingest/pipeline.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek files.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.files.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.files.ts" - } - }, - { - "script": { - "lang": "painless", - "source": "ctx.zeek.session_id = ctx.zeek.files.session_ids[0];", - "if": "ctx.zeek.files.session_ids != null", - "ignore_failure": true - } - }, - { - "script": { - "lang": "painless", - "source": "ctx.zeek.files.rx_host = ctx.zeek.files.rx_hosts[0]; ctx.zeek.files.remove('rx_hosts');", - "ignore_failure": true - } - }, - { - "script": { - "lang": "painless", - "source": "ctx.zeek.files.tx_host = ctx.zeek.files.tx_hosts[0]; ctx.zeek.files.remove('tx_hosts');", - "ignore_failure": true - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - } - ], - "on_failure": [{ - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/files/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/files/ingest/pipeline.yml new file mode 100644 index 00000000000..0d5abf9bdda --- /dev/null +++ b/x-pack/filebeat/module/zeek/files/ingest/pipeline.yml @@ -0,0 +1,66 @@ +description: Pipeline for normalizing Zeek files.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.files.ts + formats: + - UNIX +- remove: + field: zeek.files.ts +- script: + lang: painless + source: ctx.zeek.session_id = ctx.zeek.files.session_ids[0]; + if: ctx.zeek.files.session_ids != null + ignore_failure: true +- set: + field: event.id + value: '{{zeek.session_id}}' + if: ctx.zeek.session_id != null +- foreach: + field: zeek.files.tx_hosts + processor: + append: + field: related.ip + value: "{{_ingest._value}}" + ignore_missing: true +- script: + lang: painless + source: ctx.zeek.files.tx_host = ctx.zeek.files.tx_hosts[0]; ctx.zeek.files.remove('tx_hosts'); + ignore_failure: true +- set: + field: server.ip + value: "{{zeek.files.tx_host}}" + if: "ctx?.zeek?.files?.tx_host != null" +- foreach: + field: zeek.files.rx_hosts + processor: + append: + field: related.ip + value: "{{_ingest._value}}" + ignore_missing: true +- script: + lang: painless + source: ctx.zeek.files.rx_host = ctx.zeek.files.rx_hosts[0]; ctx.zeek.files.remove('rx_hosts'); + ignore_failure: true +- set: + field: client.ip + value: "{{zeek.files.rx_host}}" + if: "ctx?.zeek?.files?.rx_host != null" +- append: + field: related.hash + value: "{{file.hash.md5}}" + if: "ctx?.file?.hash?.md5 != null" +- append: + field: related.hash + value: "{{file.hash.sha1}}" + if: "ctx?.file?.hash?.sha1 != null" +- append: + field: related.hash + value: "{{file.hash.sha256}}" + if: "ctx?.file?.hash?.sha256 != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/files/manifest.yml b/x-pack/filebeat/module/zeek/files/manifest.yml index 68b53467346..bef3d7211b6 100644 --- a/x-pack/filebeat/module/zeek/files/manifest.yml +++ b/x-pack/filebeat/module/zeek/files/manifest.yml @@ -13,7 +13,7 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/files.yml requires.processors: diff --git a/x-pack/filebeat/module/zeek/files/test/files-json.log-expected.json b/x-pack/filebeat/module/zeek/files/test/files-json.log-expected.json index 4cc0e2d38e0..6fc38a5d22a 100644 --- a/x-pack/filebeat/module/zeek/files/test/files-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/files/test/files-json.log-expected.json @@ -1,12 +1,32 @@ [ { "@timestamp": "2019-01-17T01:33:16.636Z", + "client.ip": "10.178.98.102", + "event.category": [ + "file" + ], "event.dataset": "zeek.files", "event.id": "C8I0zn3r9EPbfLgta6", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "info" + ], + "file.hash.md5": "79e4a9840d7d3a96d7c04fe2434c892e", + "file.hash.sha1": "a8985d3a65e5e5c4b2d7d66d40c6dd2fb19c5436", + "file.mime_type": "application/pkix-cert", "fileset.name": "files", "input.type": "log", "log.offset": 0, + "related.hash": [ + "79e4a9840d7d3a96d7c04fe2434c892e", + "a8985d3a65e5e5c4b2d7d66d40c6dd2fb19c5436" + ], + "related.ip": [ + "35.199.178.4", + "10.178.98.102" + ], + "server.ip": "35.199.178.4", "service.type": "zeek", "tags": [ "zeek.files" @@ -38,12 +58,32 @@ }, { "@timestamp": "2019-01-17T01:33:21.566Z", + "client.ip": "10.178.98.102", + "event.category": [ + "file" + ], "event.dataset": "zeek.files", "event.id": "C6sjVo23iNApLnlAt6", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "info" + ], + "file.hash.md5": "b9742f12eb97eff531d94f7800c6706c", + "file.hash.sha1": "b88d13fe319d342e7a808ce3a0a1158111fc3c2a", + "file.mime_type": "application/pkix-cert", "fileset.name": "files", "input.type": "log", "log.offset": 452, + "related.hash": [ + "b9742f12eb97eff531d94f7800c6706c", + "b88d13fe319d342e7a808ce3a0a1158111fc3c2a" + ], + "related.ip": [ + "17.134.127.250", + "10.178.98.102" + ], + "server.ip": "17.134.127.250", "service.type": "zeek", "tags": [ "zeek.files" diff --git a/x-pack/filebeat/module/zeek/ftp/config/ftp.yml b/x-pack/filebeat/module/zeek/ftp/config/ftp.yml index 7c9e90cb96a..3e91ace4831 100644 --- a/x-pack/filebeat/module/zeek/ftp/config/ftp.yml +++ b/x-pack/filebeat/module/zeek/ftp/config/ftp.yml @@ -60,10 +60,27 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.ftp.user", to: "user.name"} + - {from: "zeek.ftp.command", to: "event.action"} + - {from: "zeek.ftp.mime.type", to: "file.mime_type"} + - {from: "zeek.ftp.file.size", to: "file.size"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - info + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/ftp/ingest/pipeline.json b/x-pack/filebeat/module/zeek/ftp/ingest/pipeline.json deleted file mode 100644 index 06b896b53d3..00000000000 --- a/x-pack/filebeat/module/zeek/ftp/ingest/pipeline.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek ftp.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.ftp.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.ftp.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "dot_expander": { - "field": "data_channel.passive", - "path": "zeek.ftp" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/ftp/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/ftp/ingest/pipeline.yml new file mode 100644 index 00000000000..7c15dce3ac5 --- /dev/null +++ b/x-pack/filebeat/module/zeek/ftp/ingest/pipeline.yml @@ -0,0 +1,68 @@ +description: Pipeline for normalizing Zeek ftp.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.ftp.ts + formats: + - UNIX +- remove: + field: zeek.ftp.ts +- dot_expander: + field: data_channel.passive + path: zeek.ftp +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- geoip: + field: destination.ip + target_field: destination.geo +- geoip: + field: source.ip + target_field: source.geo +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/ftp/manifest.yml b/x-pack/filebeat/module/zeek/ftp/manifest.yml index 3dd47573af9..cf51575cf84 100644 --- a/x-pack/filebeat/module/zeek/ftp/manifest.yml +++ b/x-pack/filebeat/module/zeek/ftp/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/ftp.yml diff --git a/x-pack/filebeat/module/zeek/ftp/test/ftp.log-expected.json b/x-pack/filebeat/module/zeek/ftp/test/ftp.log-expected.json index 7de6cc8897c..e6a47bd369e 100644 --- a/x-pack/filebeat/module/zeek/ftp/test/ftp.log-expected.json +++ b/x-pack/filebeat/module/zeek/ftp/test/ftp.log-expected.json @@ -4,15 +4,32 @@ "destination.address": "192.168.1.231", "destination.ip": "192.168.1.231", "destination.port": 21, + "event.action": "EPSV", + "event.category": [ + "network" + ], "event.dataset": "zeek.ftp", "event.id": "CpQoCn3o28tke89zv9", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "info", + "protocol" + ], "fileset.name": "ftp", "input.type": "log", "log.offset": 0, "network.community_id": "1:Szmpl33Czo3dQvU2V4/SrHfmBC0=", "network.protocol": "ftp", "network.transport": "tcp", + "related.ip": [ + "192.168.1.182", + "192.168.1.231" + ], + "related.user": [ + "ftp" + ], "service.type": "zeek", "source.address": "192.168.1.182", "source.ip": "192.168.1.182", @@ -20,6 +37,7 @@ "tags": [ "zeek.ftp" ], + "user.name": "ftp", "zeek.ftp.command": "EPSV", "zeek.ftp.data_channel.originating_host": "192.168.1.182", "zeek.ftp.data_channel.passive": true, @@ -36,15 +54,33 @@ "destination.address": "192.168.1.231", "destination.ip": "192.168.1.231", "destination.port": 21, + "event.action": "RETR", + "event.category": [ + "network" + ], "event.dataset": "zeek.ftp", "event.id": "CpQoCn3o28tke89zv9", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "info", + "protocol" + ], + "file.size": 39424, "fileset.name": "ftp", "input.type": "log", "log.offset": 394, "network.community_id": "1:Szmpl33Czo3dQvU2V4/SrHfmBC0=", "network.protocol": "ftp", "network.transport": "tcp", + "related.ip": [ + "192.168.1.182", + "192.168.1.231" + ], + "related.user": [ + "ftp" + ], "service.type": "zeek", "source.address": "192.168.1.182", "source.ip": "192.168.1.182", @@ -52,6 +88,7 @@ "tags": [ "zeek.ftp" ], + "user.name": "ftp", "zeek.ftp.arg": "ftp://192.168.1.231/resume.doc", "zeek.ftp.command": "RETR", "zeek.ftp.file.size": 39424, @@ -66,15 +103,32 @@ "destination.address": "192.168.1.231", "destination.ip": "192.168.1.231", "destination.port": 21, + "event.action": "STOR", + "event.category": [ + "network" + ], "event.dataset": "zeek.ftp", "event.id": "CpQoCn3o28tke89zv9", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "info", + "protocol" + ], "fileset.name": "ftp", "input.type": "log", "log.offset": 688, "network.community_id": "1:Szmpl33Czo3dQvU2V4/SrHfmBC0=", "network.protocol": "ftp", "network.transport": "tcp", + "related.ip": [ + "192.168.1.182", + "192.168.1.231" + ], + "related.user": [ + "ftp" + ], "service.type": "zeek", "source.address": "192.168.1.182", "source.ip": "192.168.1.182", @@ -82,6 +136,7 @@ "tags": [ "zeek.ftp" ], + "user.name": "ftp", "zeek.ftp.arg": "ftp://192.168.1.231/uploads/README", "zeek.ftp.command": "STOR", "zeek.ftp.password": "ftp", diff --git a/x-pack/filebeat/module/zeek/http/config/http.yml b/x-pack/filebeat/module/zeek/http/config/http.yml index 2c024397018..584160639cb 100644 --- a/x-pack/filebeat/module/zeek/http/config/http.yml +++ b/x-pack/filebeat/module/zeek/http/config/http.yml @@ -68,9 +68,26 @@ processors: ignore_missing: true fail_on_error: false + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "destination.port", to: "url.port"} + - {from: "http.request.method", to: "event.action"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + - web + type: + - connection + - info + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/http/ingest/pipeline.json b/x-pack/filebeat/module/zeek/http/ingest/pipeline.json deleted file mode 100644 index af771f8c745..00000000000 --- a/x-pack/filebeat/module/zeek/http/ingest/pipeline.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek http.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.http.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.http.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "set": { - "field": "url.port", - "value": "{{destination.port}}" - } - }, - { - "geoip": { - "field": "destination.ip", - "target_field": "destination.geo" - } - }, - { - "geoip": { - "field": "source.ip", - "target_field": "source.geo" - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "source.ip", - "target_field": "source.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "destination.ip", - "target_field": "destination.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.asn", - "target_field": "source.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.organization_name", - "target_field": "source.as.organization.name", - "ignore_missing": true - } - }, - { - "rename": { - "field": "destination.as.asn", - "target_field": "destination.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "destination.as.organization_name", - "target_field": "destination.as.organization.name", - "ignore_missing": true - } - }, - { - "user_agent": { - "field": "user_agent.original", - "ignore_missing": true - } - } - ], - "on_failure": [{ - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/http/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/http/ingest/pipeline.yml new file mode 100644 index 00000000000..62ffef0db45 --- /dev/null +++ b/x-pack/filebeat/module/zeek/http/ingest/pipeline.yml @@ -0,0 +1,82 @@ +description: Pipeline for normalizing Zeek http.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.http.ts + formats: + - UNIX +- remove: + field: zeek.http.ts +- geoip: + field: destination.ip + target_field: destination.geo +- geoip: + field: source.ip + target_field: source.geo +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- user_agent: + field: user_agent.original + ignore_missing: true +- lowercase: + field: "http.request.method" + ignore_missing: true +- lowercase: + field: "event.action" + ignore_missing: true +- set: + field: event.outcome + value: success + if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code < 400" +- set: + field: event.outcome + value: failure + if: "ctx?.http?.response?.status_code != null && ctx.http.response.status_code >= 400" +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{url.username}}" + if: "ctx?.url?.username != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/http/manifest.yml b/x-pack/filebeat/module/zeek/http/manifest.yml index a9ceabbaaa1..ddd253bb218 100644 --- a/x-pack/filebeat/module/zeek/http/manifest.yml +++ b/x-pack/filebeat/module/zeek/http/manifest.yml @@ -13,7 +13,7 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/http.yml requires.processors: diff --git a/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json b/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json index 20d3fedb1c7..ee72065d771 100644 --- a/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/http/test/http-json.log-expected.json @@ -13,12 +13,24 @@ "destination.geo.region_name": "California", "destination.ip": "17.253.5.203", "destination.port": 80, + "event.action": "get", + "event.category": [ + "network", + "web" + ], "event.dataset": "zeek.http", "event.id": "CCNp8v1SNzY7v9d1Ih", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "connection", + "info", + "protocol" + ], "fileset.name": "http", "http.request.body.bytes": 0, - "http.request.method": "GET", + "http.request.method": "get", "http.response.body.bytes": 3735, "http.response.status_code": 200, "http.version": "1.1", @@ -26,6 +38,10 @@ "log.offset": 0, "network.community_id": "1:dtBPRfpKEZyg1iOHss95buwv+cw=", "network.transport": "tcp", + "related.ip": [ + "10.178.98.102", + "17.253.5.203" + ], "service.type": "zeek", "source.address": "10.178.98.102", "source.ip": "10.178.98.102", @@ -35,7 +51,7 @@ ], "url.domain": "ocsp.apple.com", "url.original": "/ocsp04-aaica02/ME4wTKADAgEAMEUwQzBBMAkGBSsOAwIaBQAEFNqvF+Za6oA4ceFRLsAWwEInjUhJBBQx6napI3Sl39T97qDBpp7GEQ4R7AIIUP1IOZZ86ns=", - "url.port": "80", + "url.port": 80, "user_agent.device.name": "Other", "user_agent.name": "Other", "user_agent.original": "com.apple.trustd/2.0", diff --git a/x-pack/filebeat/module/zeek/intel/config/intel.yml b/x-pack/filebeat/module/zeek/intel/config/intel.yml index 38fe388bec0..2896ed72db9 100644 --- a/x-pack/filebeat/module/zeek/intel/config/intel.yml +++ b/x-pack/filebeat/module/zeek/intel/config/intel.yml @@ -61,3 +61,12 @@ processors: - zeek.intel.id.orig_p - zeek.intel.id.resp_h - zeek.intel.id.resp_p + - add_fields: + target: event + fields: + kind: alert + type: + - info +{{ if .community_id }} + - community_id: +{{ end }} diff --git a/x-pack/filebeat/module/zeek/intel/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/intel/ingest/pipeline.yml index 512cf67ff93..6a2bd6382ad 100644 --- a/x-pack/filebeat/module/zeek/intel/ingest/pipeline.yml +++ b/x-pack/filebeat/module/zeek/intel/ingest/pipeline.yml @@ -66,6 +66,15 @@ processors: field: destination.as.organization_name target_field: destination.as.organization.name ignore_missing: true + - append: + field: "related.ip" + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" + - append: + field: "related.ip" + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" + on_failure: - set: field: error.message diff --git a/x-pack/filebeat/module/zeek/intel/test/intel-json.log-expected.json b/x-pack/filebeat/module/zeek/intel/test/intel-json.log-expected.json index 1b2ac5464bf..d9de4e04efd 100644 --- a/x-pack/filebeat/module/zeek/intel/test/intel-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/intel/test/intel-json.log-expected.json @@ -12,11 +12,19 @@ "destination.ip": "198.41.0.4", "destination.port": 53, "event.dataset": "zeek.intel", + "event.kind": "alert", "event.module": "zeek", "event.original": "{\"ts\":1573030980.989353,\"uid\":\"Ctefoj1tgOPt4D0EK2\",\"id.orig_h\":\"192.168.1.1\",\"id.orig_p\":37598,\"id.resp_h\":\"198.41.0.4\",\"id.resp_p\":53,\"seen.indicator\":\"198.41.0.4\",\"seen.indicator_type\":\"Intel::ADDR\",\"seen.where\":\"Conn::IN_RESP\",\"seen.node\":\"worker-1-2\",\"matched\":[\"Intel::ADDR\"],\"sources\":[\"ETPRO Rep: AbusedTLD Score: 127\"]}", + "event.type": [ + "info" + ], "fileset.name": "intel", "input.type": "log", "log.offset": 0, + "related.ip": [ + "192.168.1.1", + "198.41.0.4" + ], "service.type": "zeek", "source.address": "192.168.1.1", "source.ip": "192.168.1.1", diff --git a/x-pack/filebeat/module/zeek/irc/config/irc.yml b/x-pack/filebeat/module/zeek/irc/config/irc.yml index 1ee45c0dc57..4d5783b8087 100644 --- a/x-pack/filebeat/module/zeek/irc/config/irc.yml +++ b/x-pack/filebeat/module/zeek/irc/config/irc.yml @@ -45,10 +45,28 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.irc.user", to: "user.name"} + - {from: "zeek.irc.command", to: "event.action"} + - {from: "zeek.irc.dcc.file.name", to: "file.name"} + - {from: "zeek.irc.dcc.file.size", to: "file.size"} + - {from: "zeek.irc.dcc.mime_type", to: "file.mime_type"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol + - info {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/irc/ingest/pipeline.json b/x-pack/filebeat/module/zeek/irc/ingest/pipeline.json deleted file mode 100644 index 40723512349..00000000000 --- a/x-pack/filebeat/module/zeek/irc/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek irc.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.irc.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.irc.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/irc/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/irc/ingest/pipeline.yml new file mode 100644 index 00000000000..ec04f4e7c93 --- /dev/null +++ b/x-pack/filebeat/module/zeek/irc/ingest/pipeline.yml @@ -0,0 +1,65 @@ +description: Pipeline for normalizing Zeek irc.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.irc.ts + formats: + - UNIX +- remove: + field: zeek.irc.ts +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- geoip: + field: destination.ip + target_field: destination.geo +- geoip: + field: source.ip + target_field: source.geo +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/irc/manifest.yml b/x-pack/filebeat/module/zeek/irc/manifest.yml index ce7cd7b714e..3bf899fd2c0 100644 --- a/x-pack/filebeat/module/zeek/irc/manifest.yml +++ b/x-pack/filebeat/module/zeek/irc/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/irc.yml diff --git a/x-pack/filebeat/module/zeek/irc/test/irc-json.log-expected.json b/x-pack/filebeat/module/zeek/irc/test/irc-json.log-expected.json index 2a12e671ea5..245d1154e86 100644 --- a/x-pack/filebeat/module/zeek/irc/test/irc-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/irc/test/irc-json.log-expected.json @@ -2,17 +2,37 @@ { "@timestamp": "2013-12-20T15:44:10.647Z", "destination.address": "38.229.70.20", + "destination.as.number": 23028, + "destination.as.organization.name": "Team Cymru Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "38.229.70.20", "destination.port": 8000, + "event.action": "USER", + "event.category": [ + "network" + ], "event.dataset": "zeek.irc", "event.id": "CNJBX5FQdL62VUUP1", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol", + "info" + ], "fileset.name": "irc", "input.type": "log", "log.offset": 0, "network.community_id": "1:YdkGov/c+KLtmg7Cf5DLDB4+YdQ=", "network.protocol": "irc", "network.transport": "tcp", + "related.ip": [ + "10.180.156.249", + "38.229.70.20" + ], "service.type": "zeek", "source.address": "10.180.156.249", "source.ip": "10.180.156.249", @@ -28,17 +48,40 @@ { "@timestamp": "2013-12-20T15:44:10.647Z", "destination.address": "38.229.70.20", + "destination.as.number": 23028, + "destination.as.organization.name": "Team Cymru Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "38.229.70.20", "destination.port": 8000, + "event.action": "NICK", + "event.category": [ + "network" + ], "event.dataset": "zeek.irc", "event.id": "CNJBX5FQdL62VUUP1", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol", + "info" + ], "fileset.name": "irc", "input.type": "log", "log.offset": 206, "network.community_id": "1:YdkGov/c+KLtmg7Cf5DLDB4+YdQ=", "network.protocol": "irc", "network.transport": "tcp", + "related.ip": [ + "10.180.156.249", + "38.229.70.20" + ], + "related.user": [ + "xxxxx" + ], "service.type": "zeek", "source.address": "10.180.156.249", "source.ip": "10.180.156.249", @@ -46,6 +89,7 @@ "tags": [ "zeek.irc" ], + "user.name": "xxxxx", "zeek.irc.addl": "+iw xxxxx XxxxxxXxxx ", "zeek.irc.command": "NICK", "zeek.irc.user": "xxxxx", @@ -55,17 +99,40 @@ { "@timestamp": "2013-12-20T15:44:10.706Z", "destination.address": "38.229.70.20", + "destination.as.number": 23028, + "destination.as.organization.name": "Team Cymru Inc.", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "38.229.70.20", "destination.port": 8000, + "event.action": "JOIN", + "event.category": [ + "network" + ], "event.dataset": "zeek.irc", "event.id": "CNJBX5FQdL62VUUP1", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol", + "info" + ], "fileset.name": "irc", "input.type": "log", "log.offset": 432, "network.community_id": "1:YdkGov/c+KLtmg7Cf5DLDB4+YdQ=", "network.protocol": "irc", "network.transport": "tcp", + "related.ip": [ + "10.180.156.249", + "38.229.70.20" + ], + "related.user": [ + "xxxxx" + ], "service.type": "zeek", "source.address": "10.180.156.249", "source.ip": "10.180.156.249", @@ -73,6 +140,7 @@ "tags": [ "zeek.irc" ], + "user.name": "xxxxx", "zeek.irc.addl": " with channel key: '-'", "zeek.irc.command": "JOIN", "zeek.irc.nick": "molochtest", diff --git a/x-pack/filebeat/module/zeek/kerberos/config/kerberos.yml b/x-pack/filebeat/module/zeek/kerberos/config/kerberos.yml index 4bbcf677b70..28c49507406 100644 --- a/x-pack/filebeat/module/zeek/kerberos/config/kerberos.yml +++ b/x-pack/filebeat/module/zeek/kerberos/config/kerberos.yml @@ -72,10 +72,33 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "source.address", to: "client.address"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "destination.address", to: "server.address"} + - {from: "zeek.kerberos.request_type", to: "event.action"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol + - authentication + - dissect: + when: + contains: + zeek.kerberos.client: "/" + tokenizer: "%{user.name}/%{user.domain}" + field: zeek.kerberos.client + target_prefix: "" {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/kerberos/ingest/pipeline.json b/x-pack/filebeat/module/zeek/kerberos/ingest/pipeline.json deleted file mode 100644 index 988e9b7f2b1..00000000000 --- a/x-pack/filebeat/module/zeek/kerberos/ingest/pipeline.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek kerberos.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.kerberos.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.kerberos.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "client.address", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "server.address", - "value": "{{destination.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "script": { - "source": "ctx.zeek.kerberos.valid.days = Math.round( (ctx.zeek.kerberos.valid.until - ctx.zeek.kerberos.valid.from) / 86400 )", - "if": "ctx.zeek.kerberos.valid?.from != null && ctx.zeek.kerberos.valid?.until != null" - } - }, - { - "date": { - "field": "zeek.kerberos.valid.until", - "target_field": "zeek.kerberos.valid.until", - "formats": ["UNIX"], - "if": "ctx.zeek.kerberos.valid?.until != null" - } - }, - { - "date": { - "field": "zeek.kerberos.valid.from", - "target_field": "zeek.kerberos.valid.from", - "formats": ["UNIX"], - "if": "ctx.zeek.kerberos.valid?.from != null" - } - } - ], - "on_failure": [{ - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/kerberos/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/kerberos/ingest/pipeline.yml new file mode 100644 index 00000000000..05005491115 --- /dev/null +++ b/x-pack/filebeat/module/zeek/kerberos/ingest/pipeline.yml @@ -0,0 +1,90 @@ +description: Pipeline for normalizing Zeek kerberos.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.kerberos.ts + formats: + - UNIX +- remove: + field: zeek.kerberos.ts +- script: + source: "ctx.zeek.kerberos.valid.days = Math.round( (ctx.zeek.kerberos.valid.until - ctx.zeek.kerberos.valid.from) / 86400 )" + if: "ctx.zeek.kerberos.valid?.from != null && ctx.zeek.kerberos.valid?.until != null" +- date: + field: zeek.kerberos.valid.until + target_field: zeek.kerberos.valid.until + formats: + - UNIX + if: ctx.zeek.kerberos.valid?.until != null +- date: + field: zeek.kerberos.valid.from + target_field: zeek.kerberos.valid.from + formats: + - UNIX + if: ctx.zeek.kerberos.valid?.from != null +- set: + field: event.outcome + value: success + if: "ctx?.zeek?.kerberos?.success == true" +- set: + field: event.outcome + value: failure + if: "ctx?.zeek?.kerberos?.success == false" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/kerberos/manifest.yml b/x-pack/filebeat/module/zeek/kerberos/manifest.yml index a2e040be371..4a94434f1d4 100644 --- a/x-pack/filebeat/module/zeek/kerberos/manifest.yml +++ b/x-pack/filebeat/module/zeek/kerberos/manifest.yml @@ -13,7 +13,7 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/kerberos.yml requires.processors: diff --git a/x-pack/filebeat/module/zeek/kerberos/test/kerberos-json.log-expected.json b/x-pack/filebeat/module/zeek/kerberos/test/kerberos-json.log-expected.json index a09e3ac8a4f..e01e42a4036 100644 --- a/x-pack/filebeat/module/zeek/kerberos/test/kerberos-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/kerberos/test/kerberos-json.log-expected.json @@ -5,15 +5,33 @@ "destination.address": "192.168.10.10", "destination.ip": "192.168.10.10", "destination.port": 88, + "event.action": "TGS", + "event.category": [ + "network" + ], "event.dataset": "zeek.kerberos", "event.id": "C56Flhb4WQBNkfMOl", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "connection", + "protocol", + "authentication" + ], "fileset.name": "kerberos", "input.type": "log", "log.offset": 0, "network.community_id": "1:DW/lSsosl8gZ8pqO9kKMm7cZheQ=", "network.protocol": "kerberos", "network.transport": "tcp", + "related.ip": [ + "192.168.10.31", + "192.168.10.10" + ], + "related.user": [ + "RonHD" + ], "server.address": "192.168.10.10", "service.type": "zeek", "source.address": "192.168.10.31", @@ -22,6 +40,8 @@ "tags": [ "zeek.kerberos" ], + "user.domain": "CONTOSO.LOCAL", + "user.name": "RonHD", "zeek.kerberos.cipher": "aes256-cts-hmac-sha1-96", "zeek.kerberos.client": "RonHD/CONTOSO.LOCAL", "zeek.kerberos.forwardable": true, diff --git a/x-pack/filebeat/module/zeek/modbus/config/modbus.yml b/x-pack/filebeat/module/zeek/modbus/config/modbus.yml index fec2b954224..6dc8c3004d4 100644 --- a/x-pack/filebeat/module/zeek/modbus/config/modbus.yml +++ b/x-pack/filebeat/module/zeek/modbus/config/modbus.yml @@ -39,10 +39,35 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.modbus.function", to: "event.action"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol + - if: + has_fields: ['zeek.modbus.exception'] + then: + - add_fields: + target: event + fields: + outcome: failure + else: + - add_fields: + target: event + fields: + outcome: success {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/modbus/ingest/pipeline.json b/x-pack/filebeat/module/zeek/modbus/ingest/pipeline.json deleted file mode 100644 index 78026f2dc87..00000000000 --- a/x-pack/filebeat/module/zeek/modbus/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek modbus.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.modbus.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.modbus.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/modbus/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/modbus/ingest/pipeline.yml new file mode 100644 index 00000000000..d053a541ef5 --- /dev/null +++ b/x-pack/filebeat/module/zeek/modbus/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek modbus.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.modbus.ts + formats: + - UNIX +- remove: + field: zeek.modbus.ts +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/modbus/manifest.yml b/x-pack/filebeat/module/zeek/modbus/manifest.yml index 98e51ae2bec..e20412fadc6 100644 --- a/x-pack/filebeat/module/zeek/modbus/manifest.yml +++ b/x-pack/filebeat/module/zeek/modbus/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/modbus.yml diff --git a/x-pack/filebeat/module/zeek/modbus/test/modbus-json.log-expected.json b/x-pack/filebeat/module/zeek/modbus/test/modbus-json.log-expected.json index 9817176e098..ba9034a3621 100644 --- a/x-pack/filebeat/module/zeek/modbus/test/modbus-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/modbus/test/modbus-json.log-expected.json @@ -4,15 +4,29 @@ "destination.address": "192.168.1.164", "destination.ip": "192.168.1.164", "destination.port": 502, + "event.action": "READ_COILS", + "event.category": [ + "network" + ], "event.dataset": "zeek.modbus", "event.id": "CpIIXl4DFGswmjH2bl", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "modbus", "input.type": "log", "log.offset": 0, "network.community_id": "1:jEXbR2FqHyMgLJgyYyFQN3yxbpc=", "network.protocol": "modbus", "network.transport": "tcp", + "related.ip": [ + "192.168.1.10", + "192.168.1.164" + ], "service.type": "zeek", "source.address": "192.168.1.10", "source.ip": "192.168.1.10", diff --git a/x-pack/filebeat/module/zeek/mysql/config/mysql.yml b/x-pack/filebeat/module/zeek/mysql/config/mysql.yml index fcd226131bc..b28262b5bd5 100644 --- a/x-pack/filebeat/module/zeek/mysql/config/mysql.yml +++ b/x-pack/filebeat/module/zeek/mysql/config/mysql.yml @@ -36,10 +36,37 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.mysql.cmd", to: "event.action"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - database + - network + type: + - connection + - protocol + - if: + equals: + zeek.mysql.success: true + then: + - add_fields: + target: event + fields: + outcome: success + else: + - add_fields: + target: event + fields: + outcome: failure {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/mysql/ingest/pipeline.json b/x-pack/filebeat/module/zeek/mysql/ingest/pipeline.json deleted file mode 100644 index ec55df982d7..00000000000 --- a/x-pack/filebeat/module/zeek/mysql/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek mysql.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.mysql.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.mysql.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/mysql/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/mysql/ingest/pipeline.yml new file mode 100644 index 00000000000..ca2c6c57172 --- /dev/null +++ b/x-pack/filebeat/module/zeek/mysql/ingest/pipeline.yml @@ -0,0 +1,83 @@ +description: Pipeline for normalizing Zeek mysql.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.mysql.ts + formats: + - UNIX +- remove: + field: zeek.mysql.ts +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: event.type + value: access + if: "ctx?.zeek?.mysql?.cmd != null && (ctx.zeek.mysql.cmd == 'connect' || ctx.zeek.mysql.cmd == 'connect_out')" +- append: + field: event.type + value: change + if: "ctx?.zeek?.mysql?.cmd != null && (ctx.zeek.mysql.cmd == 'init_db' || ctx.zeek.mysql.cmd == 'change_user' || ctx.zeek.mysql.cmd == 'set_option' || ctx.zeek.mysql.cmd == 'drop_db' || ctx.zeek.mysql.cmd == 'create_db' || ctx.zeek.mysql.cmd == 'process_kill' || ctx.zeek.mysql.cmd == 'delayed_insert')" +- append: + field: event.type + value: info + if: "ctx?.zeek?.mysql?.cmd != null && ctx.zeek.mysql.cmd != 'init_db' && ctx.zeek.mysql.cmd != 'change_user' && ctx.zeek.mysql.cmd != 'set_option' && ctx.zeek.mysql.cmd != 'drop_db' && ctx.zeek.mysql.cmd != 'create_db' && ctx.zeek.mysql.cmd != 'process_kill' && ctx.zeek.mysql.cmd != 'delayed_insert' && ctx.zeek.mysql.cmd != 'connect' && ctx.zeek.mysql.cmd != 'connect_out'" +- append: + field: event.type + value: start + if: "ctx?.zeek?.mysql?.cmd != null && ctx.zeek.mysql.cmd == 'connect'" +- append: + field: event.type + value: end + if: "ctx?.zeek?.mysql?.cmd != null && ctx.zeek.mysql.cmd == 'connect_out'" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/mysql/manifest.yml b/x-pack/filebeat/module/zeek/mysql/manifest.yml index a16c6092cc7..1b7ec4edb19 100644 --- a/x-pack/filebeat/module/zeek/mysql/manifest.yml +++ b/x-pack/filebeat/module/zeek/mysql/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/mysql.yml diff --git a/x-pack/filebeat/module/zeek/mysql/test/mysql-json.log-expected.json b/x-pack/filebeat/module/zeek/mysql/test/mysql-json.log-expected.json index 279b1019404..bf68cae48fe 100644 --- a/x-pack/filebeat/module/zeek/mysql/test/mysql-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/mysql/test/mysql-json.log-expected.json @@ -4,15 +4,31 @@ "destination.address": "192.168.0.254", "destination.ip": "192.168.0.254", "destination.port": 3306, + "event.action": "query", + "event.category": [ + "database", + "network" + ], "event.dataset": "zeek.mysql", "event.id": "C5Hol527kLMUw36hj3", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "connection", + "protocol", + "info" + ], "fileset.name": "mysql", "input.type": "log", "log.offset": 0, "network.community_id": "1:0HUQbshhYbATQXDHv/ysOs0DlZA=", "network.protocol": "mysql", "network.transport": "tcp", + "related.ip": [ + "192.168.0.254", + "192.168.0.254" + ], "service.type": "zeek", "source.address": "192.168.0.254", "source.ip": "192.168.0.254", diff --git a/x-pack/filebeat/module/zeek/notice/config/notice.yml b/x-pack/filebeat/module/zeek/notice/config/notice.yml index 7f5c9c0869c..32ab849b6b5 100644 --- a/x-pack/filebeat/module/zeek/notice/config/notice.yml +++ b/x-pack/filebeat/module/zeek/notice/config/notice.yml @@ -78,9 +78,25 @@ processors: - drop_fields: fields: ["zeek.notice.remote_location", "zeek.notice.f"] + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.notice.file.total_bytes", to: "file.size"} + - {from: "zeek.notice.file.mime_type", to: "file.mime_type"} + - {from: "zeek.notice.note", to: "rule.name"} + - {from: "zeek.notice.msg", to: "rule.description"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: alert + category: + - intrusion_detection + type: + - info {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/notice/ingest/pipeline.json b/x-pack/filebeat/module/zeek/notice/ingest/pipeline.json deleted file mode 100644 index b343068d6c6..00000000000 --- a/x-pack/filebeat/module/zeek/notice/ingest/pipeline.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek notice.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.notice.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.notice.ts" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}", - "if": "ctx.destination?.address != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}", - "if": "ctx.source?.address != null" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "geoip": { - "field": "destination.ip", - "target_field": "destination.geo", - "ignore_missing": true - } - }, - { - "geoip": { - "field": "source.ip", - "target_field": "source.geo", - "ignore_missing": true - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "source.ip", - "target_field": "source.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "geoip": { - "database_file": "GeoLite2-ASN.mmdb", - "field": "destination.ip", - "target_field": "destination.as", - "properties": [ - "asn", - "organization_name" - ], - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.asn", - "target_field": "source.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "source.as.organization_name", - "target_field": "source.as.organization.name", - "ignore_missing": true - } - }, - { - "rename": { - "field": "destination.as.asn", - "target_field": "destination.as.number", - "ignore_missing": true - } - }, - { - "rename": { - "field": "destination.as.organization_name", - "target_field": "destination.as.organization.name", - "ignore_missing": true - } - } - ], - "on_failure": [{ - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/notice/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/notice/ingest/pipeline.yml new file mode 100644 index 00000000000..c4dee6b78f2 --- /dev/null +++ b/x-pack/filebeat/module/zeek/notice/ingest/pipeline.yml @@ -0,0 +1,71 @@ +description: Pipeline for normalizing Zeek notice.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.notice.ts + formats: + - UNIX +- remove: + field: zeek.notice.ts +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: event.type + value: allowed + if: "ctx?.zeek?.notice?.dropped == false" +- append: + field: event.type + value: denied + if: "ctx?.zeek?.notice?.dropped == true" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/notice/manifest.yml b/x-pack/filebeat/module/zeek/notice/manifest.yml index 7b98a8efefc..e2bdf695027 100644 --- a/x-pack/filebeat/module/zeek/notice/manifest.yml +++ b/x-pack/filebeat/module/zeek/notice/manifest.yml @@ -13,7 +13,7 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/notice.yml requires.processors: diff --git a/x-pack/filebeat/module/zeek/notice/test/notice-json.log-expected.json b/x-pack/filebeat/module/zeek/notice/test/notice-json.log-expected.json index 58a59ab4d7b..a5838e9f3f1 100644 --- a/x-pack/filebeat/module/zeek/notice/test/notice-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/notice/test/notice-json.log-expected.json @@ -1,11 +1,24 @@ [ { "@timestamp": "2011-11-04T19:44:35.879Z", + "event.category": [ + "intrusion_detection" + ], "event.dataset": "zeek.notice", + "event.kind": "alert", "event.module": "zeek", + "event.type": [ + "info", + "allowed" + ], "fileset.name": "notice", "input.type": "log", "log.offset": 0, + "related.ip": [ + "172.16.238.1" + ], + "rule.description": "172.16.238.1 appears to be guessing SSH passwords (seen in 30 connections).", + "rule.name": "SSH::Password_Guessing", "service.type": "zeek", "source.address": "172.16.238.1", "source.ip": "172.16.238.1", @@ -32,11 +45,25 @@ "destination.geo.region_iso_code": "DE-HE", "destination.geo.region_name": "Hesse", "destination.ip": "207.154.238.205", + "event.category": [ + "intrusion_detection" + ], "event.dataset": "zeek.notice", + "event.kind": "alert", "event.module": "zeek", + "event.type": [ + "info", + "allowed" + ], "fileset.name": "notice", "input.type": "log", "log.offset": 357, + "related.ip": [ + "8.42.77.171", + "207.154.238.205" + ], + "rule.description": "8.42.77.171 scanned at least 15 unique ports of host 207.154.238.205 in 0m0s", + "rule.name": "Scan::Port_Scan", "service.type": "zeek", "source.address": "8.42.77.171", "source.as.number": 393552, diff --git a/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml b/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml index 76cfecaaf54..55a6795b6fa 100644 --- a/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml +++ b/x-pack/filebeat/module/zeek/ntlm/config/ntlm.yml @@ -48,10 +48,39 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.ntlm.username", to: "user.name"} + - {from: "zeek.ntlm.domain", to: "user.domain"} + - add_fields: + target: event + fields: + kind: event + category: + - authentication + - network + type: + - info + - connection + - if: + equals: + zeek.ntlm.success: true + then: + - add_fields: + target: event + fields: + outcome: success + - if: + equals: + zeek.ntlm.success: false + then: + - add_fields: + target: event + fields: + outcome: failure {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/ntlm/ingest/pipeline.json b/x-pack/filebeat/module/zeek/ntlm/ingest/pipeline.json deleted file mode 100644 index 680ea8815e0..00000000000 --- a/x-pack/filebeat/module/zeek/ntlm/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek ntlm.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.ntlm.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.ntlm.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/ntlm/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/ntlm/ingest/pipeline.yml new file mode 100644 index 00000000000..9f76d461392 --- /dev/null +++ b/x-pack/filebeat/module/zeek/ntlm/ingest/pipeline.yml @@ -0,0 +1,67 @@ +description: Pipeline for normalizing Zeek ntlm.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.ntlm.ts + formats: + - UNIX +- remove: + field: zeek.ntlm.ts +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/ntlm/manifest.yml b/x-pack/filebeat/module/zeek/ntlm/manifest.yml index 0248af27d3b..545bef85aaa 100644 --- a/x-pack/filebeat/module/zeek/ntlm/manifest.yml +++ b/x-pack/filebeat/module/zeek/ntlm/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/ntlm.yml diff --git a/x-pack/filebeat/module/zeek/ntlm/test/ntlm-json.log-expected.json b/x-pack/filebeat/module/zeek/ntlm/test/ntlm-json.log-expected.json index 90aebbec10b..c85d3127476 100644 --- a/x-pack/filebeat/module/zeek/ntlm/test/ntlm-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/ntlm/test/ntlm-json.log-expected.json @@ -4,15 +4,31 @@ "destination.address": "192.168.10.31", "destination.ip": "192.168.10.31", "destination.port": 445, + "event.category": [ + "authentication", + "network" + ], "event.dataset": "zeek.ntlm", "event.id": "CHphiNUKDC20fsy09", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "info", + "connection" + ], "fileset.name": "ntlm", "input.type": "log", "log.offset": 0, "network.community_id": "1:zxnXAE/Cme5fQhh6sJLs7GItc08=", "network.protocol": "ntlm", "network.transport": "tcp", + "related.ip": [ + "192.168.10.50", + "192.168.10.31" + ], + "related.user": [ + "JeffV" + ], "service.type": "zeek", "source.address": "192.168.10.50", "source.ip": "192.168.10.50", @@ -20,6 +36,8 @@ "tags": [ "zeek.ntlm" ], + "user.domain": "contoso.local", + "user.name": "JeffV", "zeek.ntlm.domain": "contoso.local", "zeek.ntlm.hostname": "ybaARon55QykXrgu", "zeek.ntlm.server.name.dns": "Victim-PC.contoso.local", diff --git a/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml b/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml index a6a74d6d05e..f6298a36d1e 100644 --- a/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml +++ b/x-pack/filebeat/module/zeek/ocsp/config/ocsp.yml @@ -56,3 +56,7 @@ processors: ignore_missing: true fail_on_error: false + - add_fields: + target: event + fields: + kind: event diff --git a/x-pack/filebeat/module/zeek/ocsp/ingest/pipeline.json b/x-pack/filebeat/module/zeek/ocsp/ingest/pipeline.json deleted file mode 100644 index e56642bd4a8..00000000000 --- a/x-pack/filebeat/module/zeek/ocsp/ingest/pipeline.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek ocsp.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.ocsp.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.ocsp.ts" - } - }, - { - "date": { - "field": "zeek.ocsp.revoke.date", - "target_field": "zeek.ocsp.revoke.date", - "formats": ["UNIX"], - "if": "ctx.zeek.ocsp.revoke?.date != null" - } - }, - { - "date": { - "field": "zeek.ocsp.update.this", - "target_field": "zeek.ocsp.update.this", - "formats": ["UNIX"], - "if": "ctx.zeek.ocsp.update?.this != null" - } - }, - { - "date": { - "field": "zeek.ocsp.update.next", - "target_field": "zeek.ocsp.update.next", - "formats": ["UNIX"], - "if": "ctx.zeek.ocsp.update?.next != null" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/ocsp/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/ocsp/ingest/pipeline.yml new file mode 100644 index 00000000000..63a878825d7 --- /dev/null +++ b/x-pack/filebeat/module/zeek/ocsp/ingest/pipeline.yml @@ -0,0 +1,41 @@ +description: Pipeline for normalizing Zeek ocsp.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.ocsp.ts + formats: + - UNIX +- remove: + field: zeek.ocsp.ts +- date: + field: zeek.ocsp.revoke.date + target_field: zeek.ocsp.revoke.date + formats: + - UNIX + if: ctx.zeek.ocsp.revoke?.date != null +- date: + field: zeek.ocsp.update.this + target_field: zeek.ocsp.update.this + formats: + - UNIX + if: ctx.zeek.ocsp.update?.this != null +- date: + field: zeek.ocsp.update.next + target_field: zeek.ocsp.update.next + formats: + - UNIX + if: ctx.zeek.ocsp.update?.next != null +- append: + field: related.hash + value: "{{zeek.ocsp.issuerNameHash}}" + if: "ctx?.zeek?.ocsp?.issuerNameHash != null" +- append: + field: related.hash + value: "{{zeek.ocsp.issuerKeyHash}}" + if: "ctx?.zeek?.ocsp?.issuerKeyHash != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/ocsp/manifest.yml b/x-pack/filebeat/module/zeek/ocsp/manifest.yml index 739873d645f..35bcfccdcb6 100644 --- a/x-pack/filebeat/module/zeek/ocsp/manifest.yml +++ b/x-pack/filebeat/module/zeek/ocsp/manifest.yml @@ -11,5 +11,5 @@ var: - name: tags default: [zeek.ocsp] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/ocsp.yml diff --git a/x-pack/filebeat/module/zeek/pe/config/pe.yml b/x-pack/filebeat/module/zeek/pe/config/pe.yml index ee4c78bb8cc..cf5f54396ad 100644 --- a/x-pack/filebeat/module/zeek/pe/config/pe.yml +++ b/x-pack/filebeat/module/zeek/pe/config/pe.yml @@ -21,3 +21,11 @@ processors: ignore_missing: true fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - file + type: + - info diff --git a/x-pack/filebeat/module/zeek/pe/ingest/pipeline.json b/x-pack/filebeat/module/zeek/pe/ingest/pipeline.json deleted file mode 100644 index f950772464c..00000000000 --- a/x-pack/filebeat/module/zeek/pe/ingest/pipeline.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek pe.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.pe.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.pe.ts" - } - }, - { - "date": { - "field": "zeek.pe.compile_time", - "target_field": "zeek.pe.compile_time", - "formats": ["UNIX"], - "if": "ctx.zeek.pe.compile_time != null" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/pe/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/pe/ingest/pipeline.yml new file mode 100644 index 00000000000..6a7fa7dca87 --- /dev/null +++ b/x-pack/filebeat/module/zeek/pe/ingest/pipeline.yml @@ -0,0 +1,21 @@ +description: Pipeline for normalizing Zeek pe.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.pe.ts + formats: + - UNIX +- remove: + field: zeek.pe.ts +- date: + field: zeek.pe.compile_time + target_field: zeek.pe.compile_time + formats: + - UNIX + if: ctx.zeek.pe.compile_time != null +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/pe/manifest.yml b/x-pack/filebeat/module/zeek/pe/manifest.yml index 02a352c5dfd..16dfe2e4634 100644 --- a/x-pack/filebeat/module/zeek/pe/manifest.yml +++ b/x-pack/filebeat/module/zeek/pe/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/pe.yml diff --git a/x-pack/filebeat/module/zeek/pe/test/pe-json.log-expected.json b/x-pack/filebeat/module/zeek/pe/test/pe-json.log-expected.json index ccad0e8e2fc..3356f0ef793 100644 --- a/x-pack/filebeat/module/zeek/pe/test/pe-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/pe/test/pe-json.log-expected.json @@ -1,8 +1,15 @@ [ { "@timestamp": "2017-10-09T16:13:19.578Z", + "event.category": [ + "file" + ], "event.dataset": "zeek.pe", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "info" + ], "fileset.name": "pe", "input.type": "log", "log.offset": 0, diff --git a/x-pack/filebeat/module/zeek/radius/config/radius.yml b/x-pack/filebeat/module/zeek/radius/config/radius.yml index fdbb468450c..38338b1c84f 100644 --- a/x-pack/filebeat/module/zeek/radius/config/radius.yml +++ b/x-pack/filebeat/module/zeek/radius/config/radius.yml @@ -36,10 +36,23 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.radius.username", to: "user.name"} + - {from: "zeek.radius.result", to: "event.outcome"} + - add_fields: + target: event + fields: + kind: event + category: + - authentication + - network + type: + - info + - connection {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/radius/ingest/pipeline.json b/x-pack/filebeat/module/zeek/radius/ingest/pipeline.json deleted file mode 100644 index 72f645dd651..00000000000 --- a/x-pack/filebeat/module/zeek/radius/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek radius.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.radius.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.radius.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/radius/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/radius/ingest/pipeline.yml new file mode 100644 index 00000000000..c69dfaefbb4 --- /dev/null +++ b/x-pack/filebeat/module/zeek/radius/ingest/pipeline.yml @@ -0,0 +1,67 @@ +description: Pipeline for normalizing Zeek radius.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.radius.ts + formats: + - UNIX +- remove: + field: zeek.radius.ts +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/radius/manifest.yml b/x-pack/filebeat/module/zeek/radius/manifest.yml index 505abcbbbd6..f881f404d7a 100644 --- a/x-pack/filebeat/module/zeek/radius/manifest.yml +++ b/x-pack/filebeat/module/zeek/radius/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/radius.yml diff --git a/x-pack/filebeat/module/zeek/radius/test/radius-json.log-expected.json b/x-pack/filebeat/module/zeek/radius/test/radius-json.log-expected.json index 9b4ddfa91f2..894b85f435f 100644 --- a/x-pack/filebeat/module/zeek/radius/test/radius-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/radius/test/radius-json.log-expected.json @@ -4,15 +4,32 @@ "destination.address": "10.0.0.100", "destination.ip": "10.0.0.100", "destination.port": 1812, + "event.category": [ + "authentication", + "network" + ], "event.dataset": "zeek.radius", "event.id": "CRe9VD3flCDWbPmpIh", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "info", + "connection" + ], "fileset.name": "radius", "input.type": "log", "log.offset": 0, "network.community_id": "1:3SdDgWXPnheV2oGfVmxQjfwtr8E=", "network.protocol": "radius", "network.transport": "udp", + "related.ip": [ + "10.0.0.1", + "10.0.0.100" + ], + "related.user": [ + "John.McGuirk" + ], "service.type": "zeek", "source.address": "10.0.0.1", "source.ip": "10.0.0.1", @@ -20,6 +37,7 @@ "tags": [ "zeek.radius" ], + "user.name": "John.McGuirk", "zeek.radius.mac": "00:14:22:e9:54:5e", "zeek.radius.result": "success", "zeek.radius.username": "John.McGuirk", diff --git a/x-pack/filebeat/module/zeek/rdp/config/rdp.yml b/x-pack/filebeat/module/zeek/rdp/config/rdp.yml index d9dac8f2e9b..b9b19e79dd7 100644 --- a/x-pack/filebeat/module/zeek/rdp/config/rdp.yml +++ b/x-pack/filebeat/module/zeek/rdp/config/rdp.yml @@ -69,10 +69,20 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - protocol + - info {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/rdp/ingest/pipeline.json b/x-pack/filebeat/module/zeek/rdp/ingest/pipeline.json deleted file mode 100644 index ae56b98801f..00000000000 --- a/x-pack/filebeat/module/zeek/rdp/ingest/pipeline.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek rdp.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.rdp.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.rdp.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "convert": { - "field": "zeek.rdp.ssl", - "target_field": "tls.established", - "type": "boolean", - "ignore_missing": true - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/rdp/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/rdp/ingest/pipeline.yml new file mode 100644 index 00000000000..d6b70dd92e6 --- /dev/null +++ b/x-pack/filebeat/module/zeek/rdp/ingest/pipeline.yml @@ -0,0 +1,68 @@ +description: Pipeline for normalizing Zeek rdp.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.rdp.ts + formats: + - UNIX +- remove: + field: zeek.rdp.ts +- convert: + field: zeek.rdp.ssl + target_field: tls.established + type: boolean + ignore_missing: true +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/rdp/manifest.yml b/x-pack/filebeat/module/zeek/rdp/manifest.yml index 044352bb2fd..b0c76c9f3a3 100644 --- a/x-pack/filebeat/module/zeek/rdp/manifest.yml +++ b/x-pack/filebeat/module/zeek/rdp/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/rdp.yml diff --git a/x-pack/filebeat/module/zeek/rdp/test/rdp-json.log-expected.json b/x-pack/filebeat/module/zeek/rdp/test/rdp-json.log-expected.json index 6d39caef60b..878eb3e2050 100644 --- a/x-pack/filebeat/module/zeek/rdp/test/rdp-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/rdp/test/rdp-json.log-expected.json @@ -4,15 +4,27 @@ "destination.address": "192.168.131.131", "destination.ip": "192.168.131.131", "destination.port": 3389, + "event.category": [ + "network" + ], "event.dataset": "zeek.rdp", "event.id": "C2PcYV7D3ntaHm056", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "protocol", + "info" + ], "fileset.name": "rdp", "input.type": "log", "log.offset": 0, "network.community_id": "1:PsQu6lSZioPVi0A5K7UaeGsVqS0=", "network.protocol": "rdp", "network.transport": "tcp", + "related.ip": [ + "192.168.131.1", + "192.168.131.131" + ], "service.type": "zeek", "source.address": "192.168.131.1", "source.ip": "192.168.131.1", diff --git a/x-pack/filebeat/module/zeek/rfb/config/rfb.yml b/x-pack/filebeat/module/zeek/rfb/config/rfb.yml index 61e984131cd..f9a2618b02b 100644 --- a/x-pack/filebeat/module/zeek/rfb/config/rfb.yml +++ b/x-pack/filebeat/module/zeek/rfb/config/rfb.yml @@ -54,10 +54,20 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - info {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/rfb/ingest/pipeline.json b/x-pack/filebeat/module/zeek/rfb/ingest/pipeline.json deleted file mode 100644 index 14ae112ffea..00000000000 --- a/x-pack/filebeat/module/zeek/rfb/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek rfb.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.rfb.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.rfb.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/rfb/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/rfb/ingest/pipeline.yml new file mode 100644 index 00000000000..8cf2cebdf4d --- /dev/null +++ b/x-pack/filebeat/module/zeek/rfb/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek rfb.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.rfb.ts + formats: + - UNIX +- remove: + field: zeek.rfb.ts +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/rfb/manifest.yml b/x-pack/filebeat/module/zeek/rfb/manifest.yml index 2f96e4f618e..2b9daaab107 100644 --- a/x-pack/filebeat/module/zeek/rfb/manifest.yml +++ b/x-pack/filebeat/module/zeek/rfb/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/rfb.yml diff --git a/x-pack/filebeat/module/zeek/rfb/test/rfb-json.log-expected.json b/x-pack/filebeat/module/zeek/rfb/test/rfb-json.log-expected.json index c860f5377e3..83b5544b655 100644 --- a/x-pack/filebeat/module/zeek/rfb/test/rfb-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/rfb/test/rfb-json.log-expected.json @@ -4,15 +4,27 @@ "destination.address": "192.168.1.10", "destination.ip": "192.168.1.10", "destination.port": 5900, + "event.category": [ + "network" + ], "event.dataset": "zeek.rfb", "event.id": "CXoIzM3wH3fUwXtKN1", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "info" + ], "fileset.name": "rfb", "input.type": "log", "log.offset": 0, "network.community_id": "1:AtPVA5phuztnwqMfO/2142WXVdY=", "network.protocol": "rfb", "network.transport": "tcp", + "related.ip": [ + "192.168.1.123", + "192.168.1.10" + ], "service.type": "zeek", "source.address": "192.168.1.123", "source.ip": "192.168.1.123", diff --git a/x-pack/filebeat/module/zeek/sip/config/sip.yml b/x-pack/filebeat/module/zeek/sip/config/sip.yml index bd22de69672..c94dbe5e40e 100644 --- a/x-pack/filebeat/module/zeek/sip/config/sip.yml +++ b/x-pack/filebeat/module/zeek/sip/config/sip.yml @@ -72,10 +72,24 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.sip.sequence.method", to: "event.action"} + - {from: "zeek.sip.uri", to: "url.full"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/sip/ingest/pipeline.json b/x-pack/filebeat/module/zeek/sip/ingest/pipeline.json deleted file mode 100644 index c3b7eab58fb..00000000000 --- a/x-pack/filebeat/module/zeek/sip/ingest/pipeline.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek sip.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.sip.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.sip.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "grok": { - "field": "zeek.sip.seq", - "patterns": ["%{NUMBER:zeek.sip.sequence.number}"], - "ignore_missing": true - } - }, - { - "remove": { - "field": "zeek.sip.seq", - "ignore_missing": true - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/sip/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/sip/ingest/pipeline.yml new file mode 100644 index 00000000000..9982cb82d87 --- /dev/null +++ b/x-pack/filebeat/module/zeek/sip/ingest/pipeline.yml @@ -0,0 +1,83 @@ +description: Pipeline for normalizing Zeek sip.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.sip.ts + formats: + - UNIX +- remove: + field: zeek.sip.ts +- grok: + field: zeek.sip.seq + patterns: + - '%{NUMBER:zeek.sip.sequence.number}' + ignore_missing: true +- remove: + field: zeek.sip.seq + ignore_missing: true +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: event.type + value: error + if: "ctx?.zeek?.sip?.status?.code != null && ctx.zeek.sip.status.code >= 400" +- set: + field: event.outcome + value: failure + if: "ctx?.zeek?.sip?.status?.code != null && ctx.zeek.sip.status.code >= 400" +- set: + field: event.outcome + value: success + if: "ctx?.zeek?.sip?.status?.code != null && ctx.zeek.sip.status.code < 400" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/sip/manifest.yml b/x-pack/filebeat/module/zeek/sip/manifest.yml index 8b022a943af..8da0cc443dd 100644 --- a/x-pack/filebeat/module/zeek/sip/manifest.yml +++ b/x-pack/filebeat/module/zeek/sip/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/sip.yml diff --git a/x-pack/filebeat/module/zeek/sip/test/sip-json.log-expected.json b/x-pack/filebeat/module/zeek/sip/test/sip-json.log-expected.json index c24f5405435..79b38a0717d 100644 --- a/x-pack/filebeat/module/zeek/sip/test/sip-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/sip/test/sip-json.log-expected.json @@ -2,17 +2,38 @@ { "@timestamp": "2013-02-26T22:02:39.055Z", "destination.address": "74.63.41.218", + "destination.as.number": 29791, + "destination.as.organization.name": "Internap Corporation", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "74.63.41.218", "destination.port": 5060, + "event.action": "REGISTER", + "event.category": [ + "network" + ], "event.dataset": "zeek.sip", "event.id": "CPRLCB4eWHdjP852Bk", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "failure", + "event.type": [ + "connection", + "protocol", + "error" + ], "fileset.name": "sip", "input.type": "log", "log.offset": 0, "network.community_id": "1:t8Jl0amIXPHemzxKgsLjtkB+ewo=", "network.protocol": "sip", "network.transport": "udp", + "related.ip": [ + "172.16.133.19", + "74.63.41.218" + ], "service.type": "zeek", "source.address": "172.16.133.19", "source.ip": "172.16.133.19", @@ -20,6 +41,7 @@ "tags": [ "zeek.sip" ], + "url.full": "sip:newyork.voip.ms:5060", "zeek.session_id": "CPRLCB4eWHdjP852Bk", "zeek.sip.call_id": "8694cd7e-976e4fc3-d76f6e38@172.16.133.19", "zeek.sip.request.body_length": 0, @@ -45,24 +67,57 @@ { "@timestamp": "2005-01-14T17:58:02.965Z", "destination.address": "200.57.7.195", + "destination.as.number": 18734, + "destination.as.organization.name": "Operbes, S.A. de C.V.", + "destination.geo.city_name": "Mexico City", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "MX", + "destination.geo.location.lat": 19.4357, + "destination.geo.location.lon": -99.1438, + "destination.geo.region_iso_code": "MX-CMX", + "destination.geo.region_name": "Mexico City", "destination.ip": "200.57.7.195", "destination.port": 5060, + "event.action": "INVITE", + "event.category": [ + "network" + ], "event.dataset": "zeek.sip", "event.id": "ComJz236lSOcuOmix3", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "sip", "input.type": "log", "log.offset": 805, "network.community_id": "1:U/Makwsc8lm6pVKLfRMzoNTI++0=", "network.protocol": "sip", "network.transport": "udp", + "related.ip": [ + "200.57.7.204", + "200.57.7.195" + ], "service.type": "zeek", "source.address": "200.57.7.204", + "source.as.number": 18734, + "source.as.organization.name": "Operbes, S.A. de C.V.", + "source.geo.city_name": "Mexico City", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "MX", + "source.geo.location.lat": 19.4357, + "source.geo.location.lon": -99.1438, + "source.geo.region_iso_code": "MX-CMX", + "source.geo.region_name": "Mexico City", "source.ip": "200.57.7.204", "source.port": 5061, "tags": [ "zeek.sip" ], + "url.full": "sip:francisco@bestel.com:55060", "zeek.session_id": "ComJz236lSOcuOmix3", "zeek.sip.call_id": "12013223@200.57.7.195", "zeek.sip.request.body_length": 229, @@ -91,24 +146,57 @@ { "@timestamp": "2005-01-14T17:58:07.022Z", "destination.address": "200.57.7.195", + "destination.as.number": 18734, + "destination.as.organization.name": "Operbes, S.A. de C.V.", + "destination.geo.city_name": "Mexico City", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "MX", + "destination.geo.location.lat": 19.4357, + "destination.geo.location.lon": -99.1438, + "destination.geo.region_iso_code": "MX-CMX", + "destination.geo.region_name": "Mexico City", "destination.ip": "200.57.7.195", "destination.port": 5060, + "event.action": "REGISTER", + "event.category": [ + "network" + ], "event.dataset": "zeek.sip", "event.id": "CJZDWgixtwqXctWEg", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "sip", "input.type": "log", "log.offset": 1654, "network.community_id": "1:0hvHF/bh5wFKg7nfRXxsno4F198=", "network.protocol": "sip", "network.transport": "udp", + "related.ip": [ + "200.57.7.205", + "200.57.7.195" + ], "service.type": "zeek", "source.address": "200.57.7.205", + "source.as.number": 18734, + "source.as.organization.name": "Operbes, S.A. de C.V.", + "source.geo.city_name": "Mexico City", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "MX", + "source.geo.location.lat": 19.4357, + "source.geo.location.lon": -99.1438, + "source.geo.region_iso_code": "MX-CMX", + "source.geo.region_name": "Mexico City", "source.ip": "200.57.7.205", "source.port": 5061, "tags": [ "zeek.sip" ], + "url.full": "sip:Verso.com", "zeek.session_id": "CJZDWgixtwqXctWEg", "zeek.sip.call_id": "46E1C3CB36304F84A020CF6DD3F96461@Verso.com", "zeek.sip.request.body_length": 0, diff --git a/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml b/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml index d9839c7dc16..ada63493d6f 100644 --- a/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml +++ b/x-pack/filebeat/module/zeek/smb_cmd/config/smb_cmd.yml @@ -78,10 +78,24 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.smb_cmd.command", to: "event.action"} + - {from: "zeek.smb_cmd.username", to: "user.name"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/smb_cmd/ingest/pipeline.json b/x-pack/filebeat/module/zeek/smb_cmd/ingest/pipeline.json deleted file mode 100644 index 6b1f7f1b2af..00000000000 --- a/x-pack/filebeat/module/zeek/smb_cmd/ingest/pipeline.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek smb_cmd.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.smb_cmd.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.smb_cmd.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "remove": { - "field": "zeek.smb_cmd.referenced_file", - "ignore_missing": true - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/smb_cmd/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/smb_cmd/ingest/pipeline.yml new file mode 100644 index 00000000000..838e9f2e8bc --- /dev/null +++ b/x-pack/filebeat/module/zeek/smb_cmd/ingest/pipeline.yml @@ -0,0 +1,82 @@ +description: Pipeline for normalizing Zeek smb_cmd.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.smb_cmd.ts + formats: + - UNIX +- remove: + field: zeek.smb_cmd.ts +- remove: + field: zeek.smb_cmd.referenced_file + ignore_missing: true +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- append: + field: event.type + value: error + if: "ctx?.zeek?.smb_cmd?.status != null && ctx.zeek.smb_cmd.status.toLowerCase() != 'success'" +- set: + field: event.outcome + value: success + if: "ctx?.zeek?.smb_cmd?.status != null && ctx.zeek.smb_cmd.status.toLowerCase() == 'success'" +- set: + field: event.outcome + value: failure + if: "ctx?.zeek?.smb_cmd?.status != null && ctx.zeek.smb_cmd.status.toLowerCase() != 'success'" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/smb_cmd/manifest.yml b/x-pack/filebeat/module/zeek/smb_cmd/manifest.yml index 089269869e8..a4ad3a78ce1 100644 --- a/x-pack/filebeat/module/zeek/smb_cmd/manifest.yml +++ b/x-pack/filebeat/module/zeek/smb_cmd/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/smb_cmd.yml diff --git a/x-pack/filebeat/module/zeek/smb_cmd/test/smb_cmd-json.log-expected.json b/x-pack/filebeat/module/zeek/smb_cmd/test/smb_cmd-json.log-expected.json index 872ce4a8238..e18caef3fd2 100644 --- a/x-pack/filebeat/module/zeek/smb_cmd/test/smb_cmd-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/smb_cmd/test/smb_cmd-json.log-expected.json @@ -4,15 +4,29 @@ "destination.address": "172.16.128.202", "destination.ip": "172.16.128.202", "destination.port": 445, + "event.action": "NT_CREATE_ANDX", + "event.category": [ + "network" + ], "event.dataset": "zeek.smb_cmd", "event.id": "CbT8mpAXseu6Pt4R7", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "success", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "smb_cmd", "input.type": "log", "log.offset": 0, "network.community_id": "1:SJNAD5vtzZuhQjGtfaI8svTnyuw=", "network.protocol": "smb", "network.transport": "tcp", + "related.ip": [ + "172.16.133.6", + "172.16.128.202" + ], "service.type": "zeek", "source.address": "172.16.133.6", "source.ip": "172.16.133.6", diff --git a/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml b/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml index ed5d4cdecbb..8ab5ee36395 100644 --- a/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml +++ b/x-pack/filebeat/module/zeek/smb_files/config/smb_files.yml @@ -36,10 +36,26 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.smb_files.action", to: "event.action"} + - {from: "zeek.smb_files.name", to: "file.name"} + - {from: "zeek.smb_files.size", to: "file.size"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + - file + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/smb_files/ingest/pipeline.json b/x-pack/filebeat/module/zeek/smb_files/ingest/pipeline.json deleted file mode 100644 index b4cfcfaa5b1..00000000000 --- a/x-pack/filebeat/module/zeek/smb_files/ingest/pipeline.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek smb_files.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.smb_files.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.smb_files.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "dot_expander": { - "field": "times.accessed", - "path": "zeek.smb_files" - } - }, - { - "dot_expander": { - "field": "times.changed", - "path": "zeek.smb_files" - } - }, - { - "dot_expander": { - "field": "times.created", - "path": "zeek.smb_files" - } - }, - { - "dot_expander": { - "field": "times.modified", - "path": "zeek.smb_files" - } - }, - { - "date": { - "field": "zeek.smb_files.times.accessed", - "target_field": "zeek.smb_files.times.accessed", - "formats": ["UNIX"], - "if": "ctx.zeek.smb_files.times?.accessed != null" - } - }, - { - "date": { - "field": "zeek.smb_files.times.changed", - "target_field": "zeek.smb_files.times.changed", - "formats": ["UNIX"], - "if": "ctx.zeek.smb_files.times?.accessed != null" - } - }, - { - "date": { - "field": "zeek.smb_files.times.created", - "target_field": "zeek.smb_files.times.created", - "formats": ["UNIX"], - "if": "ctx.zeek.smb_files.times?.accessed != null" - } - }, - { - "date": { - "field": "zeek.smb_files.times.modified", - "target_field": "zeek.smb_files.times.modified", - "formats": ["UNIX"], - "if": "ctx.zeek.smb_files.times?.accessed != null" - } - } - ], - "on_failure": [{ - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/smb_files/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/smb_files/ingest/pipeline.yml new file mode 100644 index 00000000000..b2c7f52a29b --- /dev/null +++ b/x-pack/filebeat/module/zeek/smb_files/ingest/pipeline.yml @@ -0,0 +1,135 @@ +description: Pipeline for normalizing Zeek smb_files.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.smb_files.ts + formats: + - UNIX +- remove: + field: zeek.smb_files.ts +- dot_expander: + field: times.accessed + path: zeek.smb_files +- dot_expander: + field: times.changed + path: zeek.smb_files +- dot_expander: + field: times.created + path: zeek.smb_files +- dot_expander: + field: times.modified + path: zeek.smb_files +- date: + field: zeek.smb_files.times.accessed + target_field: zeek.smb_files.times.accessed + formats: + - UNIX + if: ctx.zeek.smb_files.times?.accessed != null +- set: + field: file.accessed + value: "{{zeek.smb_files.times.accessed}}" + if: "ctx?.zeek?.smb_files?.times?.accessed != null" +- date: + field: zeek.smb_files.times.changed + target_field: zeek.smb_files.times.changed + formats: + - UNIX + if: ctx.zeek.smb_files.times?.accessed != null +- set: + field: file.ctime + value: "{{zeek.smb_files.times.changed}}" + if: "ctx?.zeek?.smb_files?.times?.changed != null" +- date: + field: zeek.smb_files.times.created + target_field: zeek.smb_files.times.created + formats: + - UNIX + if: ctx.zeek.smb_files.times?.accessed != null +- set: + field: file.created + value: "{{zeek.smb_files.times.created}}" + if: "ctx?.zeek?.smb_files?.times?.created != null" +- date: + field: zeek.smb_files.times.modified + target_field: zeek.smb_files.times.modified + formats: + - UNIX + if: ctx.zeek.smb_files.times?.accessed != null +- set: + field: file.mtime + value: "{{zeek.smb_files.times.modified}}" + if: "ctx?.zeek?.smb_files?.times?.modified != null" +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- set: + field: file.path + value: "{{zeek.smb_files.path}}\\{{zeek.smb_files.name}}" + if: "ctx?.zeek?.smb_files?.path != null && ctx?.zeek?.smb_files?.name != null" +- append: + field: event.type + value: deletion + if: "ctx?.zeek?.smb_files?.action == 'SMB::FILE_DELETE'" +- append: + field: event.type + value: change + if: "ctx?.zeek?.smb_files?.action == 'SMB::FILE_RENAME' || ctx?.zeek?.smb_files?.action == 'SMB::FILE_SET_ATTRIBUTE'" +- append: + field: event.type + value: info + if: "ctx?.zeek?.smb_files?.action != null && ctx.zeek.smb_files != 'SMB::FILE_DELETE' && ctx.zeek.smb_files != 'SMB::FILE_RENAME' && ctx.zeek.smb_files != 'SMB::FILE_SET_ATTRIBUTE'" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/smb_files/manifest.yml b/x-pack/filebeat/module/zeek/smb_files/manifest.yml index 154b445e765..f59a04153a5 100644 --- a/x-pack/filebeat/module/zeek/smb_files/manifest.yml +++ b/x-pack/filebeat/module/zeek/smb_files/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/smb_files.yml diff --git a/x-pack/filebeat/module/zeek/smb_files/test/smb_files-json.log-expected.json b/x-pack/filebeat/module/zeek/smb_files/test/smb_files-json.log-expected.json index fc7b8496d08..c7d5ab98b78 100644 --- a/x-pack/filebeat/module/zeek/smb_files/test/smb_files-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/smb_files/test/smb_files-json.log-expected.json @@ -4,15 +4,37 @@ "destination.address": "192.168.10.30", "destination.ip": "192.168.10.30", "destination.port": 445, + "event.action": "SMB::FILE_OPEN", + "event.category": [ + "network", + "file" + ], "event.dataset": "zeek.smb_files", "event.id": "C9YAaEzWLL62yWMn5", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol", + "info" + ], + "file.accessed": "2017-10-09T16:13:19.607Z", + "file.created": "2017-10-09T16:13:19.607Z", + "file.ctime": "2017-10-09T16:13:19.607Z", + "file.mtime": "2017-10-09T16:13:19.607Z", + "file.name": "PSEXESVC.exe", + "file.path": "\\\\\\\\admin-pc\\\\ADMIN$\\PSEXESVC.exe", + "file.size": 0, "fileset.name": "smb_files", "input.type": "log", "log.offset": 0, "network.community_id": "1:k308wDxRMx/FIEzeh+YwD86zgoA=", "network.protocol": "smb", "network.transport": "tcp", + "related.ip": [ + "192.168.10.31", + "192.168.10.30" + ], "service.type": "zeek", "source.address": "192.168.10.31", "source.ip": "192.168.10.31", diff --git a/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml b/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml index 72ea3647344..0d0934c62c8 100644 --- a/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml +++ b/x-pack/filebeat/module/zeek/smb_mapping/config/smb_mapping.yml @@ -36,10 +36,22 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/smb_mapping/ingest/pipeline.json b/x-pack/filebeat/module/zeek/smb_mapping/ingest/pipeline.json deleted file mode 100644 index c15ad371ed3..00000000000 --- a/x-pack/filebeat/module/zeek/smb_mapping/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek smb_mapping.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.smb_mapping.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.smb_mapping.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure": [{ - "set": { - "field": "error.message", - "value": "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/smb_mapping/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/smb_mapping/ingest/pipeline.yml new file mode 100644 index 00000000000..b5752120267 --- /dev/null +++ b/x-pack/filebeat/module/zeek/smb_mapping/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek smb_mapping.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.smb_mapping.ts + formats: + - UNIX +- remove: + field: zeek.smb_mapping.ts +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/smb_mapping/manifest.yml b/x-pack/filebeat/module/zeek/smb_mapping/manifest.yml index 403d2951c0c..7382e529b27 100644 --- a/x-pack/filebeat/module/zeek/smb_mapping/manifest.yml +++ b/x-pack/filebeat/module/zeek/smb_mapping/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/smb_mapping.yml diff --git a/x-pack/filebeat/module/zeek/smb_mapping/test/smb_mapping-json.log-expected.json b/x-pack/filebeat/module/zeek/smb_mapping/test/smb_mapping-json.log-expected.json index fbd3dd29693..71efd1e51ac 100644 --- a/x-pack/filebeat/module/zeek/smb_mapping/test/smb_mapping-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/smb_mapping/test/smb_mapping-json.log-expected.json @@ -4,15 +4,27 @@ "destination.address": "192.168.10.30", "destination.ip": "192.168.10.30", "destination.port": 445, + "event.category": [ + "network" + ], "event.dataset": "zeek.smb_mapping", "event.id": "C9YAaEzWLL62yWMn5", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "smb_mapping", "input.type": "log", "log.offset": 0, "network.community_id": "1:k308wDxRMx/FIEzeh+YwD86zgoA=", "network.protocol": "smb", "network.transport": "tcp", + "related.ip": [ + "192.168.10.31", + "192.168.10.30" + ], "service.type": "zeek", "source.address": "192.168.10.31", "source.ip": "192.168.10.31", diff --git a/x-pack/filebeat/module/zeek/smtp/config/smtp.yml b/x-pack/filebeat/module/zeek/smtp/config/smtp.yml index af4855948ea..fc8c3b0074f 100644 --- a/x-pack/filebeat/module/zeek/smtp/config/smtp.yml +++ b/x-pack/filebeat/module/zeek/smtp/config/smtp.yml @@ -45,10 +45,23 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.smtp.tls", to: "tls.established", type: boolean} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/smtp/ingest/pipeline.json b/x-pack/filebeat/module/zeek/smtp/ingest/pipeline.json deleted file mode 100644 index 44bc0b189aa..00000000000 --- a/x-pack/filebeat/module/zeek/smtp/ingest/pipeline.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek smtp.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.smtp.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.smtp.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "convert": { - "field": "zeek.smtp.tls", - "target_field": "tls.established", - "type": "boolean", - "ignore_missing": true - } - }, - { - "date": { - "field": "zeek.smtp.date", - "target_field": "zeek.smtp.date", - "formats": ["EEE, d MMM yyyy HH:mm:ss Z"], - "if": "ctx.zeek.smtp.date != null" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/smtp/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/smtp/ingest/pipeline.yml new file mode 100644 index 00000000000..4424d3674ff --- /dev/null +++ b/x-pack/filebeat/module/zeek/smtp/ingest/pipeline.yml @@ -0,0 +1,69 @@ +description: Pipeline for normalizing Zeek smtp.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.smtp.ts + formats: + - UNIX +- remove: + field: zeek.smtp.ts +- date: + field: zeek.smtp.date + target_field: zeek.smtp.date + formats: + - EEE, d MMM yyyy HH:mm:ss Z + if: ctx.zeek.smtp.date != null +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/smtp/manifest.yml b/x-pack/filebeat/module/zeek/smtp/manifest.yml index 489c984b1c4..6d69b3b5e3e 100644 --- a/x-pack/filebeat/module/zeek/smtp/manifest.yml +++ b/x-pack/filebeat/module/zeek/smtp/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/smtp.yml diff --git a/x-pack/filebeat/module/zeek/smtp/test/smtp-json.log-expected.json b/x-pack/filebeat/module/zeek/smtp/test/smtp-json.log-expected.json index 3d4bd56ac4a..61e1be27bf6 100644 --- a/x-pack/filebeat/module/zeek/smtp/test/smtp-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/smtp/test/smtp-json.log-expected.json @@ -4,15 +4,27 @@ "destination.address": "192.168.1.9", "destination.ip": "192.168.1.9", "destination.port": 25, + "event.category": [ + "network" + ], "event.dataset": "zeek.smtp", "event.id": "CWWzPB3RjqhFf528c", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "smtp", "input.type": "log", "log.offset": 0, "network.community_id": "1:38H0puTqOoHT/5r2bKFUVSXifQw=", "network.protocol": "smtp", "network.transport": "tcp", + "related.ip": [ + "192.168.1.10", + "192.168.1.9" + ], "service.type": "zeek", "source.address": "192.168.1.10", "source.ip": "192.168.1.10", diff --git a/x-pack/filebeat/module/zeek/snmp/config/snmp.yml b/x-pack/filebeat/module/zeek/snmp/config/snmp.yml index 76ff0c05f93..3431a990e0f 100644 --- a/x-pack/filebeat/module/zeek/snmp/config/snmp.yml +++ b/x-pack/filebeat/module/zeek/snmp/config/snmp.yml @@ -48,10 +48,22 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/snmp/ingest/pipeline.json b/x-pack/filebeat/module/zeek/snmp/ingest/pipeline.json deleted file mode 100644 index 646b7edf845..00000000000 --- a/x-pack/filebeat/module/zeek/snmp/ingest/pipeline.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek snmp.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.snmp.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.snmp.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "date": { - "field": "zeek.snmp.up_since", - "target_field": "zeek.snmp.up_since", - "formats": ["UNIX"], - "if": "ctx.zeek.snmp.up_since != null" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/snmp/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/snmp/ingest/pipeline.yml new file mode 100644 index 00000000000..f0070ef790d --- /dev/null +++ b/x-pack/filebeat/module/zeek/snmp/ingest/pipeline.yml @@ -0,0 +1,69 @@ +description: Pipeline for normalizing Zeek snmp.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.snmp.ts + formats: + - UNIX +- remove: + field: zeek.snmp.ts +- date: + field: zeek.snmp.up_since + target_field: zeek.snmp.up_since + formats: + - UNIX + if: ctx.zeek.snmp.up_since != null +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/snmp/manifest.yml b/x-pack/filebeat/module/zeek/snmp/manifest.yml index c11cd0b3491..b980b6fb82e 100644 --- a/x-pack/filebeat/module/zeek/snmp/manifest.yml +++ b/x-pack/filebeat/module/zeek/snmp/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/snmp.yml diff --git a/x-pack/filebeat/module/zeek/snmp/test/snmp-json.log-expected.json b/x-pack/filebeat/module/zeek/snmp/test/snmp-json.log-expected.json index 44cd6c16319..65345db7957 100644 --- a/x-pack/filebeat/module/zeek/snmp/test/snmp-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/snmp/test/snmp-json.log-expected.json @@ -4,15 +4,27 @@ "destination.address": "192.168.1.1", "destination.ip": "192.168.1.1", "destination.port": 161, + "event.category": [ + "network" + ], "event.dataset": "zeek.snmp", "event.id": "CnKW1B4w9fpRa6Nkf2", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "snmp", "input.type": "log", "log.offset": 0, "network.community_id": "1:X15ey/8/tEH+tlelK6P+GfgwBPc=", "network.protocol": "snmp", "network.transport": "udp", + "related.ip": [ + "192.168.1.2", + "192.168.1.1" + ], "service.type": "zeek", "source.address": "192.168.1.2", "source.ip": "192.168.1.2", diff --git a/x-pack/filebeat/module/zeek/socks/config/socks.yml b/x-pack/filebeat/module/zeek/socks/config/socks.yml index 5bf93e22f91..ddbcd51d0b0 100644 --- a/x-pack/filebeat/module/zeek/socks/config/socks.yml +++ b/x-pack/filebeat/module/zeek/socks/config/socks.yml @@ -45,10 +45,23 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.socks.user", to: "user.name"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/socks/ingest/pipeline.json b/x-pack/filebeat/module/zeek/socks/ingest/pipeline.json deleted file mode 100644 index eabb2837d82..00000000000 --- a/x-pack/filebeat/module/zeek/socks/ingest/pipeline.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek socks.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.socks.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.socks.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - }, - { - "dot_expander": { - "field": "bound.host", - "path": "zeek.socks" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/socks/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/socks/ingest/pipeline.yml new file mode 100644 index 00000000000..04a84b13177 --- /dev/null +++ b/x-pack/filebeat/module/zeek/socks/ingest/pipeline.yml @@ -0,0 +1,82 @@ +description: Pipeline for normalizing Zeek socks.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.socks.ts + formats: + - UNIX +- remove: + field: zeek.socks.ts +- dot_expander: + field: bound.host + path: zeek.socks +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- append: + field: related.user + value: "{{user.name}}" + if: "ctx?.user?.name != null" +- append: + field: event.type + value: error + if: "ctx?.zeek?.socks?.status != null && ctx.zeek.socks.status != 'succeeded'" +- append: + field: event.outcome + value: success + if: "ctx?.zeek?.socks?.status != null && ctx.zeek.socks.status == 'succeeded'" +- append: + field: event.outcome + value: failure + if: "ctx?.zeek?.socks?.status != null && ctx.zeek.socks.status != 'succeeded'" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/socks/manifest.yml b/x-pack/filebeat/module/zeek/socks/manifest.yml index c24b9aae6db..68fea837fde 100644 --- a/x-pack/filebeat/module/zeek/socks/manifest.yml +++ b/x-pack/filebeat/module/zeek/socks/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/socks.yml diff --git a/x-pack/filebeat/module/zeek/socks/test/socks-json.log-expected.json b/x-pack/filebeat/module/zeek/socks/test/socks-json.log-expected.json index cf2a629e475..c8172d23d1a 100644 --- a/x-pack/filebeat/module/zeek/socks/test/socks-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/socks/test/socks-json.log-expected.json @@ -4,15 +4,30 @@ "destination.address": "127.0.0.1", "destination.ip": "127.0.0.1", "destination.port": 8080, + "event.category": [ + "network" + ], "event.dataset": "zeek.socks", "event.id": "Cmz4Cb4qCw1hGqYw1c", + "event.kind": "event", "event.module": "zeek", + "event.outcome": [ + "success" + ], + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "socks", "input.type": "log", "log.offset": 0, "network.community_id": "1:1Hp/o0hOC62lAwrV+a0ZKDE3rrs=", "network.protocol": "socks", "network.transport": "tcp", + "related.ip": [ + "127.0.0.1", + "127.0.0.1" + ], "service.type": "zeek", "source.address": "127.0.0.1", "source.ip": "127.0.0.1", diff --git a/x-pack/filebeat/module/zeek/ssh/config/ssh.yml b/x-pack/filebeat/module/zeek/ssh/config/ssh.yml index f463b62e895..e33f4e0e29e 100644 --- a/x-pack/filebeat/module/zeek/ssh/config/ssh.yml +++ b/x-pack/filebeat/module/zeek/ssh/config/ssh.yml @@ -57,10 +57,20 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/ssh/ingest/pipeline.json b/x-pack/filebeat/module/zeek/ssh/ingest/pipeline.json deleted file mode 100644 index 2eefd208860..00000000000 --- a/x-pack/filebeat/module/zeek/ssh/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek ssh.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.ssh.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.ssh.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/ssh/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/ssh/ingest/pipeline.yml new file mode 100644 index 00000000000..019a44b89e0 --- /dev/null +++ b/x-pack/filebeat/module/zeek/ssh/ingest/pipeline.yml @@ -0,0 +1,71 @@ +description: Pipeline for normalizing Zeek ssh.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.ssh.ts + formats: + - UNIX +- remove: + field: zeek.ssh.ts +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +- set: + field: event.outcome + value: failure + if: "ctx?.zeek?.ssh?.auth?.success != null && ctx.zeek.ssh.auth.success == false" +- set: + field: event.outcome + value: success + if: "ctx?.zeek?.ssh?.auth?.success != null && ctx.zeek.ssh.auth.success == true" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/ssh/manifest.yml b/x-pack/filebeat/module/zeek/ssh/manifest.yml index da635a43771..60249e25c21 100644 --- a/x-pack/filebeat/module/zeek/ssh/manifest.yml +++ b/x-pack/filebeat/module/zeek/ssh/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/ssh.yml diff --git a/x-pack/filebeat/module/zeek/ssh/test/ssh-json.log-expected.json b/x-pack/filebeat/module/zeek/ssh/test/ssh-json.log-expected.json index 8ab4788abc7..343aa7392e5 100644 --- a/x-pack/filebeat/module/zeek/ssh/test/ssh-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/ssh/test/ssh-json.log-expected.json @@ -4,15 +4,28 @@ "destination.address": "192.168.1.1", "destination.ip": "192.168.1.1", "destination.port": 22, + "event.category": [ + "network" + ], "event.dataset": "zeek.ssh", "event.id": "CajWfz1b3qnnWT0BU9", + "event.kind": "event", "event.module": "zeek", + "event.outcome": "failure", + "event.type": [ + "connection", + "protocol" + ], "fileset.name": "ssh", "input.type": "log", "log.offset": 0, "network.community_id": "1:42tg9bemt74qgrdvJOy2n5Veg4A=", "network.protocol": "ssh", "network.transport": "tcp", + "related.ip": [ + "192.168.1.2", + "192.168.1.1" + ], "service.type": "zeek", "source.address": "192.168.1.2", "source.ip": "192.168.1.2", diff --git a/x-pack/filebeat/module/zeek/ssl/config/ssl.yml b/x-pack/filebeat/module/zeek/ssl/config/ssl.yml index 878267f549a..88bfcc4b53e 100644 --- a/x-pack/filebeat/module/zeek/ssl/config/ssl.yml +++ b/x-pack/filebeat/module/zeek/ssl/config/ssl.yml @@ -56,10 +56,24 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "source.address", to: "client.address"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "destination.address", to: "server.address"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + kind: + - connection + - protocol {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.address {{ end }} diff --git a/x-pack/filebeat/module/zeek/ssl/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/ssl/ingest/pipeline.yml index 2a5ebf4ce7a..bbeaa24d1bd 100644 --- a/x-pack/filebeat/module/zeek/ssl/ingest/pipeline.yml +++ b/x-pack/filebeat/module/zeek/ssl/ingest/pipeline.yml @@ -10,22 +10,14 @@ processors: - UNIX - remove: field: zeek.ssl.ts -- set: - field: event.id - value: '{{zeek.session_id}}' - if: ctx.zeek.session_id != null -- set: - field: source.ip - value: '{{source.address}}' -- set: - field: destination.ip - value: '{{destination.address}}' - geoip: field: destination.ip target_field: destination.geo + ignore_missing: true - geoip: field: source.ip target_field: source.geo + ignore_missing: true - geoip: database_file: GeoLite2-ASN.mmdb field: source.ip @@ -248,7 +240,14 @@ processors: ctx.tls.version = parts[1].substring(0,1) + "." + parts[1].substring(1); } ctx.tls.version_protocol = parts[0].toLowerCase(); - +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" on_failure: - set: field: error.message diff --git a/x-pack/filebeat/module/zeek/ssl/test/ssl-json.log-expected.json b/x-pack/filebeat/module/zeek/ssl/test/ssl-json.log-expected.json index d7d7ac33ff9..526a43a350b 100644 --- a/x-pack/filebeat/module/zeek/ssl/test/ssl-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/ssl/test/ssl-json.log-expected.json @@ -1,6 +1,7 @@ [ { "@timestamp": "2019-01-17T01:32:16.805Z", + "client.address": "10.178.98.102", "destination.address": "35.199.178.4", "destination.as.number": 15169, "destination.as.organization.name": "Google LLC", @@ -13,14 +14,26 @@ "destination.geo.region_name": "California", "destination.ip": "35.199.178.4", "destination.port": 9243, + "event.category": [ + "network" + ], "event.dataset": "zeek.ssl", "event.id": "CAOvs1BMFCX2Eh0Y3", + "event.kind": [ + "connection", + "protocol" + ], "event.module": "zeek", "fileset.name": "ssl", "input.type": "log", "log.offset": 0, "network.community_id": "1:1PMhYqOKBIyRAQeMbg/pWiJ198g=", "network.transport": "tcp", + "related.ip": [ + "10.178.98.102", + "35.199.178.4" + ], + "server.address": "35.199.178.4", "service.type": "zeek", "source.address": "10.178.98.102", "source.ip": "10.178.98.102", @@ -59,6 +72,7 @@ }, { "@timestamp": "2019-01-17T01:32:16.805Z", + "client.address": "10.178.98.102", "destination.address": "35.199.178.4", "destination.as.number": 15169, "destination.as.organization.name": "Google LLC", @@ -71,14 +85,26 @@ "destination.geo.region_name": "California", "destination.ip": "35.199.178.4", "destination.port": 9243, + "event.category": [ + "network" + ], "event.dataset": "zeek.ssl", "event.id": "C3mki91FnnNtm0u1ok", + "event.kind": [ + "connection", + "protocol" + ], "event.module": "zeek", "fileset.name": "ssl", "input.type": "log", "log.offset": 635, "network.community_id": "1:zYbLmqRN6PLPB067HNAiAQISqvI=", "network.transport": "tcp", + "related.ip": [ + "10.178.98.102", + "35.199.178.4" + ], + "server.address": "35.199.178.4", "service.type": "zeek", "source.address": "10.178.98.102", "source.ip": "10.178.98.102", diff --git a/x-pack/filebeat/module/zeek/stats/ingest/pipeline.json b/x-pack/filebeat/module/zeek/stats/ingest/pipeline.json deleted file mode 100644 index 6115bc6c1d2..00000000000 --- a/x-pack/filebeat/module/zeek/stats/ingest/pipeline.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek stats.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.stats.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.stats.ts" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/stats/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/stats/ingest/pipeline.yml new file mode 100644 index 00000000000..c0347161190 --- /dev/null +++ b/x-pack/filebeat/module/zeek/stats/ingest/pipeline.yml @@ -0,0 +1,18 @@ +description: Pipeline for normalizing Zeek stats.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.stats.ts + formats: + - UNIX +- remove: + field: zeek.stats.ts +- set: + field: event.kind + value: metric +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/stats/manifest.yml b/x-pack/filebeat/module/zeek/stats/manifest.yml index c4b122a19bf..f63ad40bf33 100644 --- a/x-pack/filebeat/module/zeek/stats/manifest.yml +++ b/x-pack/filebeat/module/zeek/stats/manifest.yml @@ -11,5 +11,5 @@ var: - name: tags default: [zeek.stats] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/stats.yml diff --git a/x-pack/filebeat/module/zeek/stats/test/stats-json.log-expected.json b/x-pack/filebeat/module/zeek/stats/test/stats-json.log-expected.json index a2d8e3ab311..bcb5f24f2a2 100644 --- a/x-pack/filebeat/module/zeek/stats/test/stats-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/stats/test/stats-json.log-expected.json @@ -2,6 +2,7 @@ { "@timestamp": "2016-10-16T08:17:58.714Z", "event.dataset": "zeek.stats", + "event.kind": "metric", "event.module": "zeek", "fileset.name": "stats", "input.type": "log", diff --git a/x-pack/filebeat/module/zeek/syslog/config/syslog.yml b/x-pack/filebeat/module/zeek/syslog/config/syslog.yml index b7accce096d..a8420237af0 100644 --- a/x-pack/filebeat/module/zeek/syslog/config/syslog.yml +++ b/x-pack/filebeat/module/zeek/syslog/config/syslog.yml @@ -41,10 +41,17 @@ processors: ignore_missing: true fail_on_error: false - + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.syslog.facility", to: "log.syslog.facility.name"} + - {from: "zeek.syslog.severity", to: "log.syslog.severity.name"} + - add_fields: + target: event + fields: + kind: event {{ if .community_id }} - community_id: - fields: - source_ip: source.address - destination_ip: destination.addresss {{ end }} diff --git a/x-pack/filebeat/module/zeek/syslog/ingest/pipeline.json b/x-pack/filebeat/module/zeek/syslog/ingest/pipeline.json deleted file mode 100644 index fcb98b1b91d..00000000000 --- a/x-pack/filebeat/module/zeek/syslog/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek syslog.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.syslog.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.syslog.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/syslog/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/syslog/ingest/pipeline.yml new file mode 100644 index 00000000000..7fd848682b1 --- /dev/null +++ b/x-pack/filebeat/module/zeek/syslog/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek syslog.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.syslog.ts + formats: + - UNIX +- remove: + field: zeek.syslog.ts +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/syslog/manifest.yml b/x-pack/filebeat/module/zeek/syslog/manifest.yml index 2d75d440d2f..8db76ab5b36 100644 --- a/x-pack/filebeat/module/zeek/syslog/manifest.yml +++ b/x-pack/filebeat/module/zeek/syslog/manifest.yml @@ -13,5 +13,5 @@ var: - name: community_id default: true -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/syslog.yml diff --git a/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml b/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml index 1cd1a7031fd..8b4b40e0234 100644 --- a/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml +++ b/x-pack/filebeat/module/zeek/traceroute/config/traceroute.yml @@ -27,3 +27,17 @@ processors: ignore_missing: true fail_on_error: false + - convert: + fields: + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - info diff --git a/x-pack/filebeat/module/zeek/traceroute/ingest/pipeline.json b/x-pack/filebeat/module/zeek/traceroute/ingest/pipeline.json deleted file mode 100644 index 9a755fa3913..00000000000 --- a/x-pack/filebeat/module/zeek/traceroute/ingest/pipeline.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek traceroute.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.traceroute.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.traceroute.ts" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/traceroute/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/traceroute/ingest/pipeline.yml new file mode 100644 index 00000000000..6fa5a0bc993 --- /dev/null +++ b/x-pack/filebeat/module/zeek/traceroute/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek traceroute.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.traceroute.ts + formats: + - UNIX +- remove: + field: zeek.traceroute.ts +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/traceroute/manifest.yml b/x-pack/filebeat/module/zeek/traceroute/manifest.yml index c0dd44654df..0761e9b3bf4 100644 --- a/x-pack/filebeat/module/zeek/traceroute/manifest.yml +++ b/x-pack/filebeat/module/zeek/traceroute/manifest.yml @@ -11,5 +11,5 @@ var: - name: tags default: [zeek.traceroute] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/traceroute.yml diff --git a/x-pack/filebeat/module/zeek/traceroute/test/traceroute-json.log-expected.json b/x-pack/filebeat/module/zeek/traceroute/test/traceroute-json.log-expected.json index 90bd0dd4eec..8fdfd983c94 100644 --- a/x-pack/filebeat/module/zeek/traceroute/test/traceroute-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/traceroute/test/traceroute-json.log-expected.json @@ -2,13 +2,30 @@ { "@timestamp": "2013-02-26T22:02:38.650Z", "destination.address": "8.8.8.8", + "destination.as.number": 15169, + "destination.as.organization.name": "Google LLC", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "8.8.8.8", + "event.category": [ + "network" + ], "event.dataset": "zeek.traceroute", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "info" + ], "fileset.name": "traceroute", "input.type": "log", "log.offset": 0, "network.transport": "udp", + "related.ip": [ + "192.168.1.1", + "8.8.8.8" + ], "service.type": "zeek", "source.address": "192.168.1.1", "source.ip": "192.168.1.1", diff --git a/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml b/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml index 3fdd2c1faaa..ed9af2117ad 100644 --- a/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml +++ b/x-pack/filebeat/module/zeek/tunnel/config/tunnel.yml @@ -36,3 +36,19 @@ processors: ignore_missing: true fail_on_error: false + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.tunnel.action", to: "event.action"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: event + category: + - network + type: + - connection diff --git a/x-pack/filebeat/module/zeek/tunnel/ingest/pipeline.json b/x-pack/filebeat/module/zeek/tunnel/ingest/pipeline.json deleted file mode 100644 index bc9eacce8b0..00000000000 --- a/x-pack/filebeat/module/zeek/tunnel/ingest/pipeline.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek tunnel.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.tunnel.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.tunnel.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/tunnel/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/tunnel/ingest/pipeline.yml new file mode 100644 index 00000000000..402bce5fa5d --- /dev/null +++ b/x-pack/filebeat/module/zeek/tunnel/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek tunnel.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.tunnel.ts + formats: + - UNIX +- remove: + field: zeek.tunnel.ts +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/tunnel/manifest.yml b/x-pack/filebeat/module/zeek/tunnel/manifest.yml index ad3d712c33c..a0618a12b7e 100644 --- a/x-pack/filebeat/module/zeek/tunnel/manifest.yml +++ b/x-pack/filebeat/module/zeek/tunnel/manifest.yml @@ -11,5 +11,5 @@ var: - name: tags default: [zeek.tunnel] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/tunnel.yml diff --git a/x-pack/filebeat/module/zeek/tunnel/test/tunnel-json.log-expected.json b/x-pack/filebeat/module/zeek/tunnel/test/tunnel-json.log-expected.json index 9504931de51..1e00e616e36 100644 --- a/x-pack/filebeat/module/zeek/tunnel/test/tunnel-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/tunnel/test/tunnel-json.log-expected.json @@ -2,15 +2,39 @@ { "@timestamp": "2018-12-10T01:34:26.743Z", "destination.address": "132.16.110.133", + "destination.as.number": 427, + "destination.as.organization.name": "Air Force Systems Networking", + "destination.geo.continent_name": "North America", + "destination.geo.country_iso_code": "US", + "destination.geo.location.lat": 37.751, + "destination.geo.location.lon": -97.822, "destination.ip": "132.16.110.133", "destination.port": 8080, + "event.action": "Tunnel::DISCOVER", + "event.category": [ + "network" + ], "event.dataset": "zeek.tunnel", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "connection" + ], "fileset.name": "tunnel", "input.type": "log", "log.offset": 0, + "related.ip": [ + "132.16.146.79", + "132.16.110.133" + ], "service.type": "zeek", "source.address": "132.16.146.79", + "source.as.number": 427, + "source.as.organization.name": "Air Force Systems Networking", + "source.geo.continent_name": "North America", + "source.geo.country_iso_code": "US", + "source.geo.location.lat": 37.751, + "source.geo.location.lon": -97.822, "source.ip": "132.16.146.79", "source.port": 0, "tags": [ diff --git a/x-pack/filebeat/module/zeek/weird/config/weird.yml b/x-pack/filebeat/module/zeek/weird/config/weird.yml index 6f67c90ae4f..1256f96902b 100644 --- a/x-pack/filebeat/module/zeek/weird/config/weird.yml +++ b/x-pack/filebeat/module/zeek/weird/config/weird.yml @@ -36,3 +36,19 @@ processors: ignore_missing: true fail_on_error: false + - convert: + fields: + - {from: "zeek.session_id", to: "event.id"} + - {from: "source.address", to: "source.ip", type: "ip"} + - {from: "destination.address", to: "destination.ip", type: "ip"} + - {from: "zeek.weird.name", to: "rule.name"} + ignore_missing: true + fail_on_error: false + - add_fields: + target: event + fields: + kind: alert + category: + - network + type: + - info diff --git a/x-pack/filebeat/module/zeek/weird/ingest/pipeline.json b/x-pack/filebeat/module/zeek/weird/ingest/pipeline.json deleted file mode 100644 index a97cdeb22bb..00000000000 --- a/x-pack/filebeat/module/zeek/weird/ingest/pipeline.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "description": "Pipeline for normalizing Zeek weird.log", - "processors": [ - { - "set": { - "field": "event.created", - "value": "{{_ingest.timestamp}}" - } - }, - { - "date": { - "field": "zeek.weird.ts", - "formats": ["UNIX"] - } - }, - { - "remove": { - "field": "zeek.weird.ts" - } - }, - { - "set": { - "field": "event.id", - "value": "{{zeek.session_id}}", - "if": "ctx.zeek.session_id != null" - } - }, - { - "set": { - "field": "source.ip", - "value": "{{source.address}}", - "if": "ctx?.source?.address != null" - } - }, - { - "set": { - "field": "destination.ip", - "value": "{{destination.address}}", - "if": "ctx?.destination?.address != null" - } - } - ], - "on_failure" : [{ - "set" : { - "field" : "error.message", - "value" : "{{ _ingest.on_failure_message }}" - } - }] -} diff --git a/x-pack/filebeat/module/zeek/weird/ingest/pipeline.yml b/x-pack/filebeat/module/zeek/weird/ingest/pipeline.yml new file mode 100644 index 00000000000..e0325d9a1c5 --- /dev/null +++ b/x-pack/filebeat/module/zeek/weird/ingest/pipeline.yml @@ -0,0 +1,63 @@ +description: Pipeline for normalizing Zeek weird.log +processors: +- set: + field: event.created + value: '{{_ingest.timestamp}}' +- date: + field: zeek.weird.ts + formats: + - UNIX +- remove: + field: zeek.weird.ts +- geoip: + field: destination.ip + target_field: destination.geo + ignore_missing: true +- geoip: + field: source.ip + target_field: source.geo + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: source.ip + target_field: source.as + properties: + - asn + - organization_name + ignore_missing: true +- geoip: + database_file: GeoLite2-ASN.mmdb + field: destination.ip + target_field: destination.as + properties: + - asn + - organization_name + ignore_missing: true +- rename: + field: source.as.asn + target_field: source.as.number + ignore_missing: true +- rename: + field: source.as.organization_name + target_field: source.as.organization.name + ignore_missing: true +- rename: + field: destination.as.asn + target_field: destination.as.number + ignore_missing: true +- rename: + field: destination.as.organization_name + target_field: destination.as.organization.name + ignore_missing: true +- append: + field: related.ip + value: "{{source.ip}}" + if: "ctx?.source?.ip != null" +- append: + field: related.ip + value: "{{destination.ip}}" + if: "ctx?.destination?.ip != null" +on_failure: +- set: + field: error.message + value: '{{ _ingest.on_failure_message }}' diff --git a/x-pack/filebeat/module/zeek/weird/manifest.yml b/x-pack/filebeat/module/zeek/weird/manifest.yml index 63d48d32ee3..3e91c91c64a 100644 --- a/x-pack/filebeat/module/zeek/weird/manifest.yml +++ b/x-pack/filebeat/module/zeek/weird/manifest.yml @@ -11,5 +11,5 @@ var: - name: tags default: [zeek.weird] -ingest_pipeline: ingest/pipeline.json +ingest_pipeline: ingest/pipeline.yml input: config/weird.yml diff --git a/x-pack/filebeat/module/zeek/weird/test/weird-json.log-expected.json b/x-pack/filebeat/module/zeek/weird/test/weird-json.log-expected.json index f1fdb20678f..cc9f7f49508 100644 --- a/x-pack/filebeat/module/zeek/weird/test/weird-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/weird/test/weird-json.log-expected.json @@ -4,12 +4,24 @@ "destination.address": "192.168.1.2", "destination.ip": "192.168.1.2", "destination.port": 53, + "event.category": [ + "network" + ], "event.dataset": "zeek.weird", "event.id": "C1ralPp062bkwWt4e", + "event.kind": "alert", "event.module": "zeek", + "event.type": [ + "info" + ], "fileset.name": "weird", "input.type": "log", "log.offset": 0, + "related.ip": [ + "192.168.1.1", + "192.168.1.2" + ], + "rule.name": "dns_unmatched_reply", "service.type": "zeek", "source.address": "192.168.1.1", "source.ip": "192.168.1.1", @@ -24,11 +36,19 @@ }, { "@timestamp": "2020-01-28T16:00:59.342Z", + "event.category": [ + "network" + ], "event.dataset": "zeek.weird", + "event.kind": "alert", "event.module": "zeek", + "event.type": [ + "info" + ], "fileset.name": "weird", "input.type": "log", "log.offset": 197, + "rule.name": "non_ip_packet_in_ethernet", "service.type": "zeek", "tags": [ "zeek.weird" diff --git a/x-pack/filebeat/module/zeek/x509/config/x509.yml b/x-pack/filebeat/module/zeek/x509/config/x509.yml index 3bebeab5697..49a670e46e5 100644 --- a/x-pack/filebeat/module/zeek/x509/config/x509.yml +++ b/x-pack/filebeat/module/zeek/x509/config/x509.yml @@ -57,3 +57,9 @@ processors: ignore_missing: true fail_on_error: false + - add_fields: + target: event + fields: + kind: event + type: + - info diff --git a/x-pack/filebeat/module/zeek/x509/test/x509-json.log-expected.json b/x-pack/filebeat/module/zeek/x509/test/x509-json.log-expected.json index 1cff57241ba..fff83c5969e 100644 --- a/x-pack/filebeat/module/zeek/x509/test/x509-json.log-expected.json +++ b/x-pack/filebeat/module/zeek/x509/test/x509-json.log-expected.json @@ -3,7 +3,11 @@ "@timestamp": "2018-12-03T20:00:00.143Z", "event.dataset": "zeek.x509", "event.id": "FxZ6gZ3YR6vFlIocq3", + "event.kind": "event", "event.module": "zeek", + "event.type": [ + "info" + ], "fileset.name": "x509", "input.type": "log", "log.offset": 0, diff --git a/x-pack/filebeat/processors/decode_cef/_meta/fields.yml b/x-pack/filebeat/processors/decode_cef/_meta/fields.yml index 64b0a32cd02..6ac2ce6ea1c 100644 --- a/x-pack/filebeat/processors/decode_cef/_meta/fields.yml +++ b/x-pack/filebeat/processors/decode_cef/_meta/fields.yml @@ -452,7 +452,7 @@ - name: deviceTimeZone type: keyword - description: The timezone for the device generating the event. + description: The time zone for the device generating the event. - name: deviceTranslatedAddress type: ip diff --git a/x-pack/filebeat/processors/decode_cef/decode_cef.go b/x-pack/filebeat/processors/decode_cef/decode_cef.go index dc63c9cd195..1538fe2f417 100644 --- a/x-pack/filebeat/processors/decode_cef/decode_cef.go +++ b/x-pack/filebeat/processors/decode_cef/decode_cef.go @@ -14,7 +14,6 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/libbeat/processors" "github.com/elastic/beats/v7/x-pack/filebeat/processors/decode_cef/cef" @@ -45,8 +44,6 @@ func New(cfg *common.Config) (processors.Processor, error) { } func newDecodeCEF(c config) (*processor, error) { - cfgwarn.Beta("The " + procName + " processor is a beta feature.") - log := logp.NewLogger(logName) if c.ID != "" { log = log.With("instance_id", c.ID) diff --git a/x-pack/filebeat/processors/decode_cef/docs/decode_cef.asciidoc b/x-pack/filebeat/processors/decode_cef/docs/decode_cef.asciidoc index dcde727efba..3078bf3477b 100644 --- a/x-pack/filebeat/processors/decode_cef/docs/decode_cef.asciidoc +++ b/x-pack/filebeat/processors/decode_cef/docs/decode_cef.asciidoc @@ -6,8 +6,6 @@ decode_cef ++++ -beta[] - The `decode_cef` processor decodes Common Event Format (CEF) messages. This processor is available in Filebeat. @@ -18,11 +16,11 @@ renaming it to `event.original`. It is best to rename `message` to [source,yaml] ---- processors: -- rename: - fields: - - {from: "message", to: "event.original"} -- decode_cef: - field: event.original + - rename: + fields: + - {from: "message", to: "event.original"} + - decode_cef: + field: event.original ---- The `decode_cef` processor has the following configuration settings. diff --git a/x-pack/filebeat/processors/decode_cef/fields.go b/x-pack/filebeat/processors/decode_cef/fields.go index d885d6210e1..dd5080f8251 100644 --- a/x-pack/filebeat/processors/decode_cef/fields.go +++ b/x-pack/filebeat/processors/decode_cef/fields.go @@ -19,5 +19,5 @@ func init() { // AssetDecodeCef returns asset data. // This is the base64 encoded gzipped contents of processors/decode_cef. func AssetDecodeCef() string { - return "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" + return "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" } diff --git a/x-pack/functionbeat/Makefile b/x-pack/functionbeat/Makefile index fe15d9fe5c4..60d069da395 100644 --- a/x-pack/functionbeat/Makefile +++ b/x-pack/functionbeat/Makefile @@ -7,7 +7,7 @@ ES_BEATS?=../../ # # Includes # -include $(ES_BEATS)/dev-tools/make/xpack.mk +include $(ES_BEATS)/dev-tools/make/mage.mk .PHONY: test-gcp-functions test-gcp-functions: mage diff --git a/x-pack/functionbeat/_meta/beat.reference.yml b/x-pack/functionbeat/_meta/config/beat.reference.yml.tmpl similarity index 99% rename from x-pack/functionbeat/_meta/beat.reference.yml rename to x-pack/functionbeat/_meta/config/beat.reference.yml.tmpl index dc0b0832bd9..c306fb0ac2a 100644 --- a/x-pack/functionbeat/_meta/beat.reference.yml +++ b/x-pack/functionbeat/_meta/config/beat.reference.yml.tmpl @@ -6,8 +6,8 @@ # # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/functionbeat/index.html -# -#============================ Provider =============================== + +{{header "Provider"}} # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # diff --git a/x-pack/functionbeat/_meta/beat.yml b/x-pack/functionbeat/_meta/config/beat.yml.tmpl similarity index 99% rename from x-pack/functionbeat/_meta/beat.yml rename to x-pack/functionbeat/_meta/config/beat.yml.tmpl index ae1c7ee97dc..533d33dc599 100644 --- a/x-pack/functionbeat/_meta/beat.yml +++ b/x-pack/functionbeat/_meta/config/beat.yml.tmpl @@ -8,7 +8,7 @@ # https://www.elastic.co/guide/en/beats/functionbeat/index.html # -#============================ Provider =============================== +{{header "Provider"}} # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # @@ -337,7 +337,7 @@ functionbeat.provider.gcp.functions: # Define custom processors for this function. #processors: # - dissect: - # tokenizer: "%{key1} %{key2}" + # tokenizer: "%{key1} %{key2}" #==================== Elasticsearch template setting ========================== setup.template.settings: diff --git a/x-pack/functionbeat/docs/fields.asciidoc b/x-pack/functionbeat/docs/fields.asciidoc index 06912ba9e88..f12ef78112a 100644 --- a/x-pack/functionbeat/docs/fields.asciidoc +++ b/x-pack/functionbeat/docs/fields.asciidoc @@ -33,7 +33,8 @@ Contains common beat fields available in all event types. *`agent.hostname`*:: + -- -Hostname of the agent. +Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. + type: keyword diff --git a/x-pack/functionbeat/functionbeat.reference.yml b/x-pack/functionbeat/functionbeat.reference.yml index 0e945a8dea2..7815f35077b 100644 --- a/x-pack/functionbeat/functionbeat.reference.yml +++ b/x-pack/functionbeat/functionbeat.reference.yml @@ -6,8 +6,8 @@ # # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/functionbeat/index.html -# -#============================ Provider =============================== + +# ================================== Provider ================================== # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # @@ -384,7 +384,7 @@ functionbeat.provider.gcp.functions: # - dissect: # tokenizer: "%{key1} %{key2}" -#================================ General ====================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -492,7 +492,7 @@ functionbeat.provider.gcp.functions: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -509,153 +509,153 @@ functionbeat.provider.gcp.functions: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Functionbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -668,11 +668,11 @@ functionbeat.provider.gcp.functions: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -792,7 +792,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -906,7 +927,11 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#================================= Paths ====================================== + + + + +# =================================== Paths ==================================== # The home path for the Functionbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -932,11 +957,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -980,8 +1007,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1035,7 +1061,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1049,13 +1075,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'functionbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "functionbeat" +#setup.ilm.rollover_alias: 'functionbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'functionbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1070,7 +1096,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1125,9 +1151,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1194,8 +1219,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Functionbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1305,6 +1329,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1316,7 +1361,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1340,12 +1386,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/x-pack/functionbeat/functionbeat.yml b/x-pack/functionbeat/functionbeat.yml index f26658b15e6..bb8322840c8 100644 --- a/x-pack/functionbeat/functionbeat.yml +++ b/x-pack/functionbeat/functionbeat.yml @@ -8,7 +8,7 @@ # https://www.elastic.co/guide/en/beats/functionbeat/index.html # -#============================ Provider =============================== +# ================================== Provider ================================== # Configure functions to run on AWS Lambda, currently we assume that the credentials # are present in the environment to correctly create the function when using the CLI. # @@ -337,7 +337,7 @@ functionbeat.provider.gcp.functions: # Define custom processors for this function. #processors: # - dissect: - # tokenizer: "%{key1} %{key2}" + # tokenizer: "%{key1} %{key2}" #==================== Elasticsearch template setting ========================== setup.template.settings: @@ -345,7 +345,7 @@ setup.template.settings: #index.codec: best_compression #_source.enabled: false -#================================ General ===================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -360,8 +360,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -373,7 +372,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -390,7 +389,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Functionbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -403,11 +402,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -420,7 +419,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -435,7 +434,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -443,7 +442,8 @@ processors: - add_host_metadata: ~ - add_cloud_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -454,8 +454,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# functionbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Functionbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -476,7 +476,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/x-pack/functionbeat/include/fields.go b/x-pack/functionbeat/include/fields.go index 884bb2e208e..bf181ef89d3 100644 --- a/x-pack/functionbeat/include/fields.go +++ b/x-pack/functionbeat/include/fields.go @@ -19,5 +19,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/x-pack/functionbeat/scripts/mage/config.go b/x-pack/functionbeat/scripts/mage/config.go index 576fc765b12..7fb4395e2bc 100644 --- a/x-pack/functionbeat/scripts/mage/config.go +++ b/x-pack/functionbeat/scripts/mage/config.go @@ -10,21 +10,14 @@ import ( // XPackConfigFileParams returns the configuration of sample and reference configuration data. func XPackConfigFileParams() devtools.ConfigFileParams { - return devtools.ConfigFileParams{ - ShortParts: []string{ - devtools.OSSBeatDir("_meta/beat.yml"), - devtools.LibbeatDir("_meta/config.yml.tmpl"), - }, - ReferenceParts: []string{ - devtools.OSSBeatDir("_meta/beat.reference.yml"), - devtools.LibbeatDir("_meta/config.reference.yml.tmpl"), - }, - ExtraVars: map[string]interface{}{ - "ExcludeConsole": true, - "ExcludeFileOutput": true, - "ExcludeKafka": true, - "ExcludeRedis": true, - "UseDockerMetadataProcessor": false, - }, + p := devtools.DefaultConfigFileParams() + p.Templates = append(p.Templates, "_meta/config/*.tmpl") + p.ExtraVars = map[string]interface{}{ + "ExcludeConsole": true, + "ExcludeFileOutput": true, + "ExcludeKafka": true, + "ExcludeRedis": true, + "UseDockerMetadataProcessor": false, } + return p } diff --git a/x-pack/libbeat/autodiscover/providers/aws/ec2/provider.go b/x-pack/libbeat/autodiscover/providers/aws/ec2/provider.go index 15d8015e52c..4d457c46a8c 100644 --- a/x-pack/libbeat/autodiscover/providers/aws/ec2/provider.go +++ b/x-pack/libbeat/autodiscover/providers/aws/ec2/provider.go @@ -15,6 +15,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/cfgwarn" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" awsauto "github.com/elastic/beats/v7/x-pack/libbeat/autodiscover/providers/aws" awscommon "github.com/elastic/beats/v7/x-pack/libbeat/common/aws" @@ -33,10 +34,11 @@ type Provider struct { stopListener bus.Listener watcher *watcher uuid uuid.UUID + keystore keystore.Keystore } // AutodiscoverBuilder is the main builder for this provider. -func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodiscover.Provider, error) { +func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config, keystore keystore.Keystore) (autodiscover.Provider, error) { cfgwarn.Experimental("aws_ec2 autodiscover is experimental") config := awsauto.DefaultConfig() @@ -78,12 +80,12 @@ func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodis config.AWSConfig.Endpoint, "ec2", region, awsCfg))) } - return internalBuilder(uuid, bus, config, newAPIFetcher(clients)) + return internalBuilder(uuid, bus, config, newAPIFetcher(clients), keystore) } // internalBuilder is mainly intended for testing via mocks and stubs. // it can be configured to use a fetcher that doesn't actually hit the AWS API. -func internalBuilder(uuid uuid.UUID, bus bus.Bus, config *awsauto.Config, fetcher fetcher) (*Provider, error) { +func internalBuilder(uuid uuid.UUID, bus bus.Bus, config *awsauto.Config, fetcher fetcher, keystore keystore.Keystore) (*Provider, error) { mapper, err := template.NewConfigMapper(config.Templates) if err != nil { return nil, err @@ -94,6 +96,7 @@ func internalBuilder(uuid uuid.UUID, bus bus.Bus, config *awsauto.Config, fetche bus: bus, templates: &mapper, uuid: uuid, + keystore: keystore, } p.watcher = newWatcher( diff --git a/x-pack/libbeat/autodiscover/providers/aws/ec2/provider_test.go b/x-pack/libbeat/autodiscover/providers/aws/ec2/provider_test.go index 255ae2e141b..b22321eeb23 100644 --- a/x-pack/libbeat/autodiscover/providers/aws/ec2/provider_test.go +++ b/x-pack/libbeat/autodiscover/providers/aws/ec2/provider_test.go @@ -15,6 +15,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" awsauto "github.com/elastic/beats/v7/x-pack/libbeat/autodiscover/providers/aws" "github.com/elastic/beats/v7/x-pack/libbeat/autodiscover/providers/aws/test" @@ -33,7 +34,8 @@ func Test_internalBuilder(t *testing.T) { } uuid, _ := uuid.NewV4() - provider, err := internalBuilder(uuid, pBus, cfg, fetcher) + k, _ := keystore.NewFileKeystore("test") + provider, err := internalBuilder(uuid, pBus, cfg, fetcher, k) require.NoError(t, err) startListener := pBus.Subscribe("start") diff --git a/x-pack/libbeat/autodiscover/providers/aws/elb/provider.go b/x-pack/libbeat/autodiscover/providers/aws/elb/provider.go index 8513e0f9d2a..522b5ba9a4f 100644 --- a/x-pack/libbeat/autodiscover/providers/aws/elb/provider.go +++ b/x-pack/libbeat/autodiscover/providers/aws/elb/provider.go @@ -15,6 +15,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" "github.com/elastic/beats/v7/libbeat/common/cfgwarn" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" awsauto "github.com/elastic/beats/v7/x-pack/libbeat/autodiscover/providers/aws" awscommon "github.com/elastic/beats/v7/x-pack/libbeat/common/aws" @@ -35,10 +36,11 @@ type Provider struct { stopListener bus.Listener watcher *watcher uuid uuid.UUID + keystore keystore.Keystore } // AutodiscoverBuilder is the main builder for this provider. -func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodiscover.Provider, error) { +func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config, keystore keystore.Keystore) (autodiscover.Provider, error) { cfgwarn.Experimental("aws_elb autodiscover is experimental") config := awsauto.DefaultConfig() @@ -85,12 +87,12 @@ func AutodiscoverBuilder(bus bus.Bus, uuid uuid.UUID, c *common.Config) (autodis config.AWSConfig.Endpoint, "elasticloadbalancing", region, awsCfg))) } - return internalBuilder(uuid, bus, config, newAPIFetcher(clients)) + return internalBuilder(uuid, bus, config, newAPIFetcher(clients), keystore) } // internalBuilder is mainly intended for testing via mocks and stubs. // it can be configured to use a fetcher that doesn't actually hit the AWS API. -func internalBuilder(uuid uuid.UUID, bus bus.Bus, config *awsauto.Config, fetcher fetcher) (*Provider, error) { +func internalBuilder(uuid uuid.UUID, bus bus.Bus, config *awsauto.Config, fetcher fetcher, keystore keystore.Keystore) (*Provider, error) { mapper, err := template.NewConfigMapper(config.Templates) if err != nil { return nil, err @@ -101,6 +103,7 @@ func internalBuilder(uuid uuid.UUID, bus bus.Bus, config *awsauto.Config, fetche bus: bus, templates: &mapper, uuid: uuid, + keystore: keystore, } p.watcher = newWatcher( diff --git a/x-pack/libbeat/autodiscover/providers/aws/elb/provider_test.go b/x-pack/libbeat/autodiscover/providers/aws/elb/provider_test.go index e6012c39d33..d6f9a918377 100644 --- a/x-pack/libbeat/autodiscover/providers/aws/elb/provider_test.go +++ b/x-pack/libbeat/autodiscover/providers/aws/elb/provider_test.go @@ -16,6 +16,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/bus" + "github.com/elastic/beats/v7/libbeat/keystore" "github.com/elastic/beats/v7/libbeat/logp" awsauto "github.com/elastic/beats/v7/x-pack/libbeat/autodiscover/providers/aws" ) @@ -74,7 +75,8 @@ func Test_internalBuilder(t *testing.T) { } uuid, _ := uuid.NewV4() - provider, err := internalBuilder(uuid, pBus, cfg, fetcher) + k, _ := keystore.NewFileKeystore("test") + provider, err := internalBuilder(uuid, pBus, cfg, fetcher, k) require.NoError(t, err) startListener := pBus.Subscribe("start") diff --git a/x-pack/libbeat/licenser/elastic_fetcher.go b/x-pack/libbeat/licenser/elastic_fetcher.go index 6ffa5f6fa37..b2c855df6b7 100644 --- a/x-pack/libbeat/licenser/elastic_fetcher.go +++ b/x-pack/libbeat/licenser/elastic_fetcher.go @@ -18,9 +18,9 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" ) -const xPackURL = "/_license" +const licenseURL = "/_license" -// params defaults query parameters to send to the '_xpack' endpoint by default we only need +// params defaults query parameters to send to the '_license' endpoint by default we only need // machine parseable data. var params = map[string]string{ "human": "false", @@ -88,12 +88,12 @@ func NewElasticFetcher(client esclient) *ElasticFetcher { return &ElasticFetcher{client: client, log: logp.NewLogger("elasticfetcher")} } -// Fetch retrieves the license information from an Elasticsearch Client, it will call the `_xpack` -// end point and will return a parsed license. If the `_xpack` endpoint is unreacheable we will +// Fetch retrieves the license information from an Elasticsearch Client, it will call the `_license` +// endpoint and will return a parsed license. If the `_license` endpoint is unreacheable we will // return the OSS License otherwise we return an error. func (f *ElasticFetcher) Fetch() (*License, error) { - status, body, err := f.client.Request("GET", xPackURL, "", params, nil) - // When we are running an OSS release of elasticsearch the _xpack endpoint will return a 405, + status, body, err := f.client.Request("GET", licenseURL, "", params, nil) + // When we are running an OSS release of elasticsearch the _license endpoint will return a 405, // "Method Not Allowed", so we return the default OSS license. if status == http.StatusBadRequest { f.log.Debug("Received 'Bad request' (400) response from server, fallback to OSS license") diff --git a/x-pack/libbeat/licenser/es_callback.go b/x-pack/libbeat/licenser/es_callback.go index 0ca99db8f5f..06cc055083c 100644 --- a/x-pack/libbeat/licenser/es_callback.go +++ b/x-pack/libbeat/licenser/es_callback.go @@ -30,7 +30,7 @@ func Enforce(name string, checks ...CheckFunc) { license, err := fetcher.Fetch() if err != nil { - return errors.Wrapf(err, "cannot retrieve the elasticsearch license from the /_xpack endpoint, "+ + return errors.Wrapf(err, "cannot retrieve the elasticsearch license from the /_license endpoint, "+ "%s requires the default distribution of Elasticsearch. Please make the endpoint accessible "+ "to %s so it can verify the license.", name, name) } diff --git a/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc b/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc index 8de150048fd..7c5b2daba96 100644 --- a/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc +++ b/x-pack/libbeat/processors/add_cloudfoundry_metadata/docs/add_cloudfoundry_metadata.asciidoc @@ -26,17 +26,17 @@ Each event is annotated with: [source,yaml] ------------------------------------------------------------------------------- processors: -- add_cloudfoundry_metadata: - api_address: https://api.dev.cfdev.sh - client_id: uaa-filebeat - client_secret: verysecret - ssl: - verification_mode: none - # To connect to Cloud Foundry over verified TLS you can specify a client and CA certificate. - #ssl: - # certificate_authorities: ["/etc/pki/cf/ca.pem"] - # certificate: "/etc/pki/cf/cert.pem" - # key: "/etc/pki/cf/cert.key" + - add_cloudfoundry_metadata: + api_address: https://api.dev.cfdev.sh + client_id: uaa-filebeat + client_secret: verysecret + ssl: + verification_mode: none + # To connect to Cloud Foundry over verified TLS you can specify a client and CA certificate. + #ssl: + # certificate_authorities: ["/etc/pki/cf/ca.pem"] + # certificate: "/etc/pki/cf/cert.pem" + # key: "/etc/pki/cf/cert.key" ------------------------------------------------------------------------------- It has the following settings: diff --git a/x-pack/metricbeat/Makefile b/x-pack/metricbeat/Makefile index 56633e2b3e5..c77d4c68517 100644 --- a/x-pack/metricbeat/Makefile +++ b/x-pack/metricbeat/Makefile @@ -1,3 +1,8 @@ ES_BEATS ?= ../.. -include $(ES_BEATS)/dev-tools/make/xpack.mk +include $(ES_BEATS)/dev-tools/make/mage.mk + +# Creates a new metricset. Requires the params MODULE and METRICSET +.PHONY: create-metricset +create-metricset: + mage createMetricset diff --git a/x-pack/metricbeat/docker-compose.yml b/x-pack/metricbeat/docker-compose.yml index c89b558605d..83ac016ffd7 100644 --- a/x-pack/metricbeat/docker-compose.yml +++ b/x-pack/metricbeat/docker-compose.yml @@ -10,6 +10,7 @@ services: volumes: - ${PWD}/../..:/go/src/github.com/elastic/beats/ - /var/run/docker.sock:/var/run/docker.sock + - ${HOME}/.docker:/root/.docker:ro network_mode: host command: make diff --git a/x-pack/metricbeat/magefile.go b/x-pack/metricbeat/magefile.go index 1821f21a435..ee5ecc9e0f0 100644 --- a/x-pack/metricbeat/magefile.go +++ b/x-pack/metricbeat/magefile.go @@ -25,6 +25,8 @@ import ( "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" // mage:import "github.com/elastic/beats/v7/dev-tools/mage/target/test" + // mage:import + _ "github.com/elastic/beats/v7/metricbeat/scripts/mage/target/metricset" ) func init() { @@ -104,7 +106,7 @@ func moduleFieldsGo() error { return devtools.GenerateModuleFieldsGo("module") } -// fieldsYML generates a fields.yml based on filebeat + x-pack/filebeat/modules. +// fieldsYML generates a fields.yml based on metricbeat + x-pack/metricbeat/modules. func fieldsYML() error { return devtools.GenerateFieldsYAML(devtools.OSSBeatDir("module"), "module") } @@ -132,8 +134,6 @@ func Update() { // IntegTest executes integration tests (it uses Docker to run the tests). func IntegTest() { - devtools.AddIntegTestUsage() - defer devtools.StopIntegTestEnv() mg.SerialDeps(GoIntegTest, PythonIntegTest) } @@ -143,6 +143,9 @@ func IntegTest() { // Use TEST_TAGS=tag1,tag2 to add additional build tags. // Use MODULE=module to run only tests for `module`. func GoIntegTest(ctx context.Context) error { + if !devtools.IsInIntegTestEnv() { + mg.SerialDeps(Fields, Dashboards) + } return devtools.GoTestIntegrationForModule(ctx) } @@ -155,8 +158,12 @@ func PythonIntegTest(ctx context.Context) error { if !devtools.IsInIntegTestEnv() { mg.SerialDeps(Fields, Dashboards) } - return devtools.RunIntegTest("pythonIntegTest", func() error { + runner, err := devtools.NewDockerIntegrationRunner(devtools.ListMatchingEnvVars("NOSE_")...) + if err != nil { + return err + } + return runner.Test("pythonIntegTest", func() error { mg.Deps(devtools.BuildSystemTestBinary) return devtools.PythonNoseTest(devtools.DefaultPythonTestIntegrationArgs()) - }, devtools.ListMatchingEnvVars("NOSE_")...) + }) } diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index c843bc364d7..d2293995c5a 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -53,6 +53,7 @@ metricbeat.max_start_delay: 10s #timeseries.enabled: false + #========================== Modules configuration ============================= metricbeat.modules: @@ -498,13 +499,21 @@ metricbeat.modules: - module: googlecloud metricsets: - compute + region: "us-central1" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 300s + +- module: googlecloud + metricsets: - pubsub - loadbalancing zone: "us-central1-a" project_id: "your project id" credentials_file_path: "your JSON credentials file path" exclude_labels: false - period: 300s + period: 60s - module: googlecloud metricsets: @@ -515,6 +524,15 @@ metricbeat.modules: exclude_labels: false period: 300s +- module: googlecloud + metricsets: + - compute + region: "us-" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s + #------------------------------- Graphite Module ------------------------------- - module: graphite metricsets: ["server"] @@ -826,7 +844,7 @@ metricbeat.modules: #--------------------------------- Kvm Module --------------------------------- - module: kvm - metricsets: ["dommemstat"] + metricsets: ["dommemstat", "status"] enabled: true period: 10s hosts: ["unix:///var/run/libvirt/libvirt-sock"] @@ -975,7 +993,7 @@ metricbeat.modules: metricsets: ["tablespace", "performance"] enabled: true period: 10s - hosts: ["oracle://user:pass@localhost:1521/ORCLPDB1.localdomain?sysdba=1"] + hosts: ["user:pass@0.0.0.0:1521/ORCLPDB1.localdomain"] # username: "" # password: "" @@ -1279,7 +1297,8 @@ metricbeat.modules: -#================================ General ====================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -1387,7 +1406,7 @@ metricbeat.modules: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -1404,153 +1423,153 @@ metricbeat.modules: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Metricbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -1563,11 +1582,11 @@ metricbeat.modules: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -1687,7 +1706,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -1801,7 +1841,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -1955,6 +1995,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -1977,7 +2020,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -2095,7 +2138,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -2129,7 +2172,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -2142,7 +2185,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Metricbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -2168,11 +2211,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -2216,8 +2261,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -2271,7 +2315,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -2285,13 +2329,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'metricbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "metricbeat" +#setup.ilm.rollover_alias: 'metricbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'metricbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -2306,7 +2350,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -2361,9 +2405,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -2430,8 +2473,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Metricbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -2541,6 +2583,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -2552,7 +2615,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -2576,12 +2640,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/x-pack/metricbeat/metricbeat.yml b/x-pack/metricbeat/metricbeat.yml index 5bd19f3030c..96975ee6027 100644 --- a/x-pack/metricbeat/metricbeat.yml +++ b/x-pack/metricbeat/metricbeat.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/metricbeat/index.html -#========================== Modules configuration ============================ +# =========================== Modules configuration ============================ metricbeat.config.modules: # Glob pattern for configuration loading @@ -19,14 +19,15 @@ metricbeat.config.modules: # Period on which files under path should be checked for changes #reload.period: 10s -#==================== Elasticsearch template setting ========================== +# ======================= Elasticsearch template setting ======================= setup.template.settings: index.number_of_shards: 1 index.codec: best_compression #_source.enabled: false -#================================ General ===================================== + +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -41,8 +42,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -54,7 +54,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -71,7 +71,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Metricbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -84,11 +84,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -101,7 +101,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -116,7 +116,7 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== +# ================================= Processors ================================= # Configure processors to enhance or manipulate events generated by the beat. @@ -126,7 +126,8 @@ processors: - add_docker_metadata: ~ - add_kubernetes_metadata: ~ -#================================ Logging ===================================== + +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -137,8 +138,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# metricbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Metricbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -159,7 +160,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true + diff --git a/x-pack/metricbeat/module/aws/_meta/config.epr.yml b/x-pack/metricbeat/module/aws/_meta/config.epr.yml new file mode 100644 index 00000000000..9cc299673b2 --- /dev/null +++ b/x-pack/metricbeat/module/aws/_meta/config.epr.yml @@ -0,0 +1,89 @@ +- module: aws + period: 12h + aws_access_key_id: "" + aws_secret_access_key: "" + aws_session_token: "" + credential_profile_name: "" + role_arn: "" + metricsets: + - billing +- module: aws + period: 1m + aws_access_key_id: "" + aws_secret_access_key: "" + aws_session_token: "" + credential_profile_name: "" + role_arn: "" + metricsets: + - natgateway + - transitgateway + - usage + - vpn +- module: aws + period: 5m + aws_access_key_id: "" + aws_secret_access_key: "" + aws_session_token: "" + credential_profile_name: "" + role_arn: "" + metricsets: + - sqs +- module: aws + period: 24h + aws_access_key_id: "" + aws_secret_access_key: "" + aws_session_token: "" + credential_profile_name: "" + role_arn: "" + metricsets: + - s3_daily_storage + - s3_request +- module: aws + period: 1m + aws_access_key_id: "" + aws_secret_access_key: "" + aws_session_token: "" + credential_profile_name: "" + role_arn: "" + metricsets: + - elb + - rds + tags_filter: + - key: "" + value: "" +- module: aws + period: 5m + aws_access_key_id: "" + aws_secret_access_key: "" + aws_session_token: "" + credential_profile_name: "" + role_arn: "" + metricsets: + - dynamodb + - ebs + - ec2 + - lambda + - sns + tags_filter: + - key: "" + value: "" +- module: aws + period: 300s + aws_access_key_id: "" + aws_secret_access_key: "" + aws_session_token: "" + credential_profile_name: "" + role_arn: "" + metricsets: + - cloudwatch + metrics: + - namespace: AWS/EC2 + name: [""] + tags.resource_type_filter: "" + dimensions: + - name: "" + value: "" + statistic: [""] + tags: + - key: "" + value: "" diff --git a/x-pack/metricbeat/module/aws/aws.go b/x-pack/metricbeat/module/aws/aws.go index dcf047da87d..5476f0dae5f 100644 --- a/x-pack/metricbeat/module/aws/aws.go +++ b/x-pack/metricbeat/module/aws/aws.go @@ -103,6 +103,7 @@ func NewMetricSet(base mb.BaseMetricSet) (*MetricSet, error) { // collecting the first one. if output.AccountAliases != nil { metricSet.AccountName = output.AccountAliases[0] + base.Logger().Debug("AWS Credentials belong to account name: ", metricSet.AccountName) } } @@ -115,6 +116,7 @@ func NewMetricSet(base mb.BaseMetricSet) (*MetricSet, error) { base.Logger().Warn("failed to get caller identity, please check permission setting: ", err) } else { metricSet.AccountID = *outputIdentity.Account + base.Logger().Debug("AWS Credentials belong to account ID: ", metricSet.AccountID) } // Construct MetricSet with a full regions list diff --git a/x-pack/metricbeat/module/aws/billing/_meta/fields.yml b/x-pack/metricbeat/module/aws/billing/_meta/fields.yml index 94b06a2f151..2b246415653 100644 --- a/x-pack/metricbeat/module/aws/billing/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/billing/_meta/fields.yml @@ -4,3 +4,9 @@ `billing` contains the estimated charges for your AWS account in Cloudwatch. release: beta fields: + - name: metrics + type: group + fields: + - name: EstimatedCharges.max + type: long + description: Maximum estimated charges for AWS acccount. diff --git a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go index 05c9b21cd3a..5674a45eccd 100644 --- a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go +++ b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch.go @@ -18,6 +18,7 @@ import ( "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/metricbeat/mb" awscommon "github.com/elastic/beats/v7/x-pack/libbeat/common/aws" "github.com/elastic/beats/v7/x-pack/metricbeat/module/aws" @@ -51,6 +52,7 @@ func init() { // interface methods except for Fetch. type MetricSet struct { *aws.MetricSet + logger *logp.Logger CloudwatchConfigs []Config `config:"metrics" validate:"nonzero,required"` } @@ -113,6 +115,7 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return &MetricSet{ MetricSet: metricSet, + logger: logp.NewLogger(metricsetName), CloudwatchConfigs: config.CloudwatchMetrics, }, nil } @@ -132,10 +135,13 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { // Get listMetricDetailTotal and namespaceDetailTotal from configuration listMetricDetailTotal, namespaceDetailTotal := m.readCloudwatchConfig() + m.logger.Debugf("listMetricDetailTotal = %s", listMetricDetailTotal) + m.logger.Debugf("namespaceDetailTotal = %s", namespaceDetailTotal) // Create events based on listMetricDetailTotal from configuration if len(listMetricDetailTotal.metricsWithStats) != 0 { for _, regionName := range m.MetricSet.RegionsList { + m.logger.Debugf("Collecting metrics from AWS region %s", regionName) awsConfig := m.MetricSet.AwsConfig.Copy() awsConfig.Region = regionName @@ -150,6 +156,8 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { return errors.Wrap(err, "createEvents failed for region "+regionName) } + m.logger.Debugf("Collected metrics of metrics = %d", len(eventsWithIdentifier)) + err = reportEvents(eventsWithIdentifier, report) if err != nil { return errors.Wrap(err, "reportEvents failed") @@ -158,6 +166,7 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { } for _, regionName := range m.MetricSet.RegionsList { + m.logger.Debugf("Collecting metrics from AWS region %s", regionName) awsConfig := m.MetricSet.AwsConfig.Copy() awsConfig.Region = regionName @@ -169,9 +178,11 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { // Create events based on namespaceDetailTotal from configuration for namespace, namespaceDetails := range namespaceDetailTotal { + m.logger.Debugf("Collected metrics from namespace %s", namespace) + listMetricsOutput, err := aws.GetListMetricsOutput(namespace, regionName, svcCloudwatch) if err != nil { - m.Logger().Info(err.Error()) + m.logger.Info(err.Error()) continue } @@ -189,6 +200,8 @@ func (m *MetricSet) Fetch(report mb.ReporterV2) error { return errors.Wrap(err, "createEvents failed for region "+regionName) } + m.logger.Debugf("Collected number of metrics = %d", len(eventsWithIdentifier)) + err = reportEvents(eventsWithIdentifier, report) if err != nil { return errors.Wrap(err, "reportEvents failed") @@ -434,12 +447,14 @@ func (m *MetricSet) createEvents(svcCloudwatch cloudwatchiface.ClientAPI, svcRes // Construct metricDataQueries metricDataQueries := createMetricDataQueries(listMetricWithStatsTotal, m.Period) + m.logger.Debugf("Number of MetricDataQueries = %d", len(metricDataQueries)) if len(metricDataQueries) == 0 { return events, nil } // Use metricDataQueries to make GetMetricData API calls metricDataResults, err := aws.GetMetricDataResults(metricDataQueries, svcCloudwatch, startTime, endTime) + m.logger.Debugf("Number of metricDataResults = %d", len(metricDataResults)) if err != nil { return events, errors.Wrap(err, "GetMetricDataResults failed") } @@ -482,7 +497,7 @@ func (m *MetricSet) createEvents(svcCloudwatch cloudwatchiface.ClientAPI, svcRes resourceTagMap, err := aws.GetResourcesTags(svcResourceAPI, []string{resourceType}) if err != nil { // If GetResourcesTags failed, continue report event just without tags. - m.Logger().Info(errors.Wrap(err, "getResourcesTags failed, skipping region "+regionName)) + m.logger.Info(errors.Wrap(err, "getResourcesTags failed, skipping region "+regionName)) } if len(tagsFilter) != 0 && len(resourceTagMap) == 0 { diff --git a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go index 92016c1cbc9..0b8cc468c06 100644 --- a/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go +++ b/x-pack/metricbeat/module/aws/cloudwatch/cloudwatch_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/metricbeat/mb" awssdk "github.com/aws/aws-sdk-go-v2/aws" @@ -1232,6 +1233,7 @@ func TestCreateEventsWithIdentifier(t *testing.T) { m := MetricSet{} m.CloudwatchConfigs = []Config{{Statistic: []string{"Average"}}} m.MetricSet = &aws.MetricSet{Period: 5} + m.logger = logp.NewLogger("test") mockTaggingSvc := &MockResourceGroupsTaggingClient{} mockCloudwatchSvc := &MockCloudWatchClient{} @@ -1272,6 +1274,7 @@ func TestCreateEventsWithoutIdentifier(t *testing.T) { m := MetricSet{} m.CloudwatchConfigs = []Config{{Statistic: []string{"Average"}}} m.MetricSet = &aws.MetricSet{Period: 5, AccountID: accountID} + m.logger = logp.NewLogger("test") mockTaggingSvc := &MockResourceGroupsTaggingClient{} mockCloudwatchSvc := &MockCloudWatchClientWithoutDim{} diff --git a/x-pack/metricbeat/module/aws/ebs/_meta/fields.yml b/x-pack/metricbeat/module/aws/ebs/_meta/fields.yml index cd6f0ada0e3..eac4fd48d70 100644 --- a/x-pack/metricbeat/module/aws/ebs/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/ebs/_meta/fields.yml @@ -4,3 +4,39 @@ `ebs` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS EBS. release: ga fields: + - name: metrics + type: group + fields: + - name: VolumeReadBytes.avg + type: double + description: Average size of each read operation during the period, except on volumes attached to a Nitro-based instance, where the average represents the average over the specified period. + - name: VolumeWriteBytes.avg + type: double + description: Average size of each write operation during the period, except on volumes attached to a Nitro-based instance, where the average represents the average over the specified period. + - name: VolumeReadOps.avg + type: double + description: The total number of read operations in a specified period of time. + - name: VolumeWriteOps.avg + type: double + description: The total number of write operations in a specified period of time. + - name: VolumeQueueLength.avg + type: double + description: The number of read and write operation requests waiting to be completed in a specified period of time. + - name: VolumeThroughputPercentage.avg + type: double + description: The percentage of I/O operations per second (IOPS) delivered of the total IOPS provisioned for an Amazon EBS volume. Used with Provisioned IOPS SSD volumes only. + - name: VolumeConsumedReadWriteOps.avg + type: double + description: The total amount of read and write operations (normalized to 256K capacity units) consumed in a specified period of time. Used with Provisioned IOPS SSD volumes only. + - name: BurstBalance.avg + type: double + description: Used with General Purpose SSD (gp2), Throughput Optimized HDD (st1), and Cold HDD (sc1) volumes only. Provides information about the percentage of I/O credits (for gp2) or throughput credits (for st1 and sc1) remaining in the burst bucket. + - name: VolumeTotalReadTime.sum + type: double + description: The total number of seconds spent by all read operations that completed in a specified period of time. + - name: VolumeTotalWriteTime.sum + type: double + description: The total number of seconds spent by all write operations that completed in a specified period of time. + - name: VolumeIdleTime.sum + type: double + description: The total number of seconds in a specified period of time when no read or write operations were submitted. diff --git a/x-pack/metricbeat/module/aws/elb/_meta/docs.asciidoc b/x-pack/metricbeat/module/aws/elb/_meta/docs.asciidoc index 3975a252e88..8f242e8867a 100644 --- a/x-pack/metricbeat/module/aws/elb/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/aws/elb/_meta/docs.asciidoc @@ -98,17 +98,25 @@ https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-cl |Metric Name|Statistic Method |ActiveFlowCount | Average |ActiveFlowCount_TLS | Average -|ClientTLSNegotiationErrorCount | Sum +|ActiveFlowCount_TCP | Average +|ActiveFlowCount_UDP | Average |ConsumedLCUs | Average -|HealthyHostCount | Maximum +|ConsumedLCUs_TCP | Average +|ConsumedLCUs_TLS | Average +|ConsumedLCUs_UDP | Average +|ClientTLSNegotiationErrorCount | Sum |NewFlowCount | Sum |NewFlowCount_TLS | Sum +|NewFlowCount_TCP | Sum +|NewFlowCount_UDP | Sum |ProcessedBytes | Sum +|ProcessedBytes_TCP | Sum |ProcessedBytes_TLS | Sum +|ProcessedBytes_UDP| Sum |TargetTLSNegotiationErrorCount | Sum |TCP_Client_Reset_Count | Sum |TCP_ELB_Reset_Count | Sum |TCP_Target_Reset_Count | Sum -|UnHealthyHostCount | Average -|EstimatedALBConsumedLCUs | Maximum +|UnHealthyHostCount | Maximum +|HealthyHostCount | Maximum |=== diff --git a/x-pack/metricbeat/module/aws/elb/_meta/fields.yml b/x-pack/metricbeat/module/aws/elb/_meta/fields.yml index 634bc04774d..c49b5a7caeb 100644 --- a/x-pack/metricbeat/module/aws/elb/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/elb/_meta/fields.yml @@ -4,3 +4,183 @@ `elb` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS ELB. release: ga fields: + - name: metrics + type: group + fields: + - name: BackendConnectionErrors.sum + type: long + description: The number of connections that were not successfully established between the load balancer and the registered instances. + - name: HTTPCode_Backend_2XX.sum + type: long + description: The number of HTTP 2XX response code generated by registered instances. + - name: HTTPCode_Backend_3XX.sum + type: long + description: The number of HTTP 3XX response code generated by registered instances. + - name: HTTPCode_Backend_4XX.sum + type: long + description: The number of HTTP 4XX response code generated by registered instances. + - name: HTTPCode_Backend_5XX.sum + type: long + description: The number of HTTP 5XX response code generated by registered instances. + - name: HTTPCode_ELB_4XX.sum + type: long + description: The number of HTTP 4XX client error codes generated by the load balancer. + - name: HTTPCode_ELB_5XX.sum + type: long + description: The number of HTTP 5XX server error codes generated by the load balancer. + - name: RequestCount.sum + type: long + description: The number of requests completed or connections made during the specified interval. + - name: SpilloverCount.sum + type: long + description: The total number of requests that were rejected because the surge queue is full. + - name: HealthyHostCount.max + type: long + description: The number of healthy instances registered with your load balancer. + - name: SurgeQueueLength.max + type: long + description: The total number of requests (HTTP listener) or connections (TCP listener) that are pending routing to a healthy instance. + - name: UnHealthyHostCount.max + type: long + description: The number of unhealthy instances registered with your load balancer. + - name: Latency.avg + type: double + description: The total time elapsed, in seconds, from the time the load balancer sent the request to a registered instance until the instance started to send the response headers. + - name: EstimatedALBActiveConnectionCount.avg + type: double + description: The estimated number of concurrent TCP connections active from clients to the load balancer and from the load balancer to targets. + - name: EstimatedALBConsumedLCUs.avg + type: double + description: The estimated number of load balancer capacity units (LCU) used by an Application Load Balancer. + - name: EstimatedALBNewConnectionCount.avg + type: double + description: The estimated number of new TCP connections established from clients to the load balancer and from the load balancer to targets. + - name: EstimatedProcessedBytes.avg + type: double + description: The estimated number of bytes processed by an Application Load Balancer. +- name: applicationelb + type: group + description: > + `applicationelb` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS ApplicationELB. + release: ga + fields: + - name: metrics + type: group + fields: + - name: ActiveConnectionCount.sum + type: long + description: The total number of concurrent TCP connections active from clients to the load balancer and from the load balancer to targets. + - name: ClientTLSNegotiationErrorCount.sum + type: long + description: The number of TLS connections initiated by the client that did not establish a session with the load balancer due to a TLS error. + - name: HTTP_Fixed_Response_Count.sum + type: long + description: The number of fixed-response actions that were successful. + - name: HTTP_Redirect_Count.sum + type: long + description: The number of redirect actions that were successful. + - name: HTTP_Redirect_Url_Limit_Exceeded_Count.sum + type: long + description: The number of redirect actions that couldn't be completed because the URL in the response location header is larger than 8K. + - name: HTTPCode_ELB_3XX_Count.sum + type: long + description: The number of HTTP 3XX redirection codes that originate from the load balancer. + - name: HTTPCode_ELB_4XX_Count.sum + type: long + description: The number of HTTP 4XX client error codes that originate from the load balancer. + - name: HTTPCode_ELB_5XX_Count.sum + type: long + description: The number of HTTP 5XX server error codes that originate from the load balancer. + - name: HTTPCode_ELB_500_Count.sum + type: long + description: The number of HTTP 500 error codes that originate from the load balancer. + - name: HTTPCode_ELB_502_Count.sum + type: long + description: The number of HTTP 502 error codes that originate from the load balancer. + - name: HTTPCode_ELB_503_Count.sum + type: long + description: The number of HTTP 503 error codes that originate from the load balancer. + - name: HTTPCode_ELB_504_Count.sum + type: long + description: The number of HTTP 504 error codes that originate from the load balancer. + - name: IPv6ProcessedBytes.sum + type: long + description: The total number of bytes processed by the load balancer over IPv6. + - name: IPv6RequestCount.sum + type: long + description: The number of IPv6 requests received by the load balancer. + - name: NewConnectionCount.sum + type: long + description: The total number of new TCP connections established from clients to the load balancer and from the load balancer to targets. + - name: ProcessedBytes.sum + type: long + description: The total number of bytes processed by the load balancer over IPv4 and IPv6. + - name: RejectedConnectionCount.sum + type: long + description: The number of connections that were rejected because the load balancer had reached its maximum number of connections. + - name: RequestCount.sum + type: long + description: The number of requests processed over IPv4 and IPv6. + - name: RuleEvaluations.sum + type: long + description: The number of rules processed by the load balancer given a request rate averaged over an hour. + - name: ConsumedLCUs.avg + type: double + description: The number of load balancer capacity units (LCU) used by your load balancer. +- name: networkelb + type: group + description: > + `networkelb` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS NetworkELB. + release: ga + fields: + - name: metrics + type: group + fields: + - name: ActiveFlowCount.avg + type: double + description: The total number of concurrent flows (or connections) from clients to targets. + - name: ActiveFlowCount_TCP.avg + type: double + description: The total number of concurrent TCP flows (or connections) from clients to targets. + - name: ActiveFlowCount_TLS.avg + type: double + description: The total number of concurrent TLS flows (or connections) from clients to targets. + - name: ActiveFlowCount_UDP.avg + type: double + description: The total number of concurrent UDP flows (or connections) from clients to targets. + - name: ConsumedLCUs.avg + type: double + description: The number of load balancer capacity units (LCU) used by your load balancer. + - name: ClientTLSNegotiationErrorCount.sum + type: long + description: The total number of TLS handshakes that failed during negotiation between a client and a TLS listener. + - name: NewFlowCount.sum + type: long + description: The total number of new flows (or connections) established from clients to targets in the time period. + - name: NewFlowCount_TLS.sum + type: long + description: The total number of new TLS flows (or connections) established from clients to targets in the time period. + - name: ProcessedBytes.sum + type: long + description: The total number of bytes processed by the load balancer, including TCP/IP headers. + - name: ProcessedBytes_TLS.sum + type: long + description: The total number of bytes processed by TLS listeners. + - name: TargetTLSNegotiationErrorCount.sum + type: long + description: The total number of TLS handshakes that failed during negotiation between a TLS listener and a target. + - name: TCP_Client_Reset_Count.sum + type: long + description: The total number of reset (RST) packets sent from a client to a target. + - name: TCP_ELB_Reset_Count.sum + type: long + description: The total number of reset (RST) packets generated by the load balancer. + - name: TCP_Target_Reset_Count.sum + type: long + description: The total number of reset (RST) packets sent from a target to a client. + - name: HealthyHostCount.max + type: long + description: The number of targets that are considered healthy. + - name: UnHealthyHostCount.max + type: long + description: The number of targets that are considered unhealthy. diff --git a/x-pack/metricbeat/module/aws/elb/manifest.yml b/x-pack/metricbeat/module/aws/elb/manifest.yml index 211a42c63bd..daa6a34fffd 100644 --- a/x-pack/metricbeat/module/aws/elb/manifest.yml +++ b/x-pack/metricbeat/module/aws/elb/manifest.yml @@ -32,12 +32,12 @@ input: tags.resource_type_filter: elasticloadbalancing - namespace: AWS/NetworkELB statistic: ["Average"] - name: ["ActiveFlowCount", "ActiveFlowCount_TLS", "ConsumedLCUs"] + name: ["ActiveFlowCount", "ActiveFlowCount_TCP", "ActiveFlowCount_TLS", "ActiveFlowCount_UDP", "ConsumedLCUs", "ConsumedLCUs_TCP", "ConsumedLCUs_TLS", "ConsumedLCUs_UDP"] tags.resource_type_filter: elasticloadbalancing - namespace: AWS/NetworkELB statistic: ["Sum"] - name: ["ClientTLSNegotiationErrorCount", "NewFlowCount", "NewFlowCount_TLS", - "ProcessedBytes", "ProcessedBytes_TLS", "TargetTLSNegotiationErrorCount", + name: ["ClientTLSNegotiationErrorCount", "NewFlowCount", "NewFlowCount_TLS", "NewFlowCount_TCP", "NewFlowCount_UDP", + "ProcessedBytes", "ProcessedBytes_TCP", "ProcessedBytes_TLS", "ProcessedBytes_UDP", "TargetTLSNegotiationErrorCount", "TCP_Client_Reset_Count", "TCP_ELB_Reset_Count", "TCP_Target_Reset_Count"] tags.resource_type_filter: elasticloadbalancing - namespace: AWS/NetworkELB diff --git a/x-pack/metricbeat/module/aws/fields.go b/x-pack/metricbeat/module/aws/fields.go index d50507f86c0..cf96de84a03 100644 --- a/x-pack/metricbeat/module/aws/fields.go +++ b/x-pack/metricbeat/module/aws/fields.go @@ -19,5 +19,5 @@ func init() { // AssetAws returns asset data. // This is the base64 encoded gzipped contents of module/aws. func AssetAws() string { - return "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" + return "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" } diff --git a/x-pack/metricbeat/module/aws/lambda/_meta/docs.asciidoc b/x-pack/metricbeat/module/aws/lambda/_meta/docs.asciidoc index 1d1f8e22a7e..70f1263823c 100644 --- a/x-pack/metricbeat/module/aws/lambda/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/aws/lambda/_meta/docs.asciidoc @@ -43,6 +43,7 @@ https://docs.aws.amazon.com/lambda/latest/dg/monitoring-functions-metrics.html[l |Invocations | Average |Errors | Average |DeadLetterErrors | Average +|DestinationDeliveryFailures | Average |Duration | Average |Throttles | Average |IteratorAge | Average diff --git a/x-pack/metricbeat/module/aws/lambda/_meta/fields.yml b/x-pack/metricbeat/module/aws/lambda/_meta/fields.yml index 1cebeff318f..91becec6fef 100644 --- a/x-pack/metricbeat/module/aws/lambda/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/lambda/_meta/fields.yml @@ -4,3 +4,45 @@ `lambda` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS Lambda. release: beta fields: + - name: metrics + type: group + fields: + - name: Invocations.avg + type: double + description: The number of times your function code is executed, including successful executions and executions that result in a function error. + - name: Errors.avg + type: double + description: The number of invocations that result in a function error. + - name: DeadLetterErrors.avg + type: double + description: For asynchronous invocation, the number of times Lambda attempts to send an event to a dead-letter queue but fails. + - name: DestinationDeliveryFailures.avg + type: double + description: For asynchronous invocation, the number of times Lambda attempts to send an event to a destination but fails. + - name: Duration.avg + type: double + description: The amount of time that your function code spends processing an event. + - name: Throttles.avg + type: double + description: The number of invocation requests that are throttled. + - name: IteratorAge.avg + type: double + description: For event source mappings that read from streams, the age of the last record in the event. + - name: ConcurrentExecutions.avg + type: double + description: The number of function instances that are processing events. + - name: UnreservedConcurrentExecutions.avg + type: double + description: For an AWS Region, the number of events that are being processed by functions that don't have reserved concurrency. + - name: ProvisionedConcurrentExecutions.max + type: long + description: The number of function instances that are processing events on provisioned concurrency. + - name: ProvisionedConcurrencyUtilization.max + type: long + description: For a version or alias, the value of ProvisionedConcurrentExecutions divided by the total amount of provisioned concurrency allocated. + - name: ProvisionedConcurrencyInvocations.sum + type: long + description: The number of times your function code is executed on provisioned concurrency. + - name: ProvisionedConcurrencySpilloverInvocations.sum + type: long + description: The number of times your function code is executed on standard concurrency when all provisioned concurrency is in use. diff --git a/x-pack/metricbeat/module/aws/lambda/manifest.yml b/x-pack/metricbeat/module/aws/lambda/manifest.yml index 71fd0b7ef1c..f1f18fd8783 100644 --- a/x-pack/metricbeat/module/aws/lambda/manifest.yml +++ b/x-pack/metricbeat/module/aws/lambda/manifest.yml @@ -6,7 +6,7 @@ input: metrics: - namespace: AWS/Lambda statistic: ["Average"] - name: ["Invocations", "Errors", "DeadLetterErrors", "Duration", + name: ["Invocations", "Errors", "DeadLetterErrors", "DestinationDeliveryFailures", "Duration", "Throttles", "IteratorAge", "ConcurrentExecutions", "UnreservedConcurrentExecutions"] tags.resource_type_filter: lambda diff --git a/x-pack/metricbeat/module/aws/natgateway/_meta/fields.yml b/x-pack/metricbeat/module/aws/natgateway/_meta/fields.yml index d6694c4c656..8ff5a3a2edd 100644 --- a/x-pack/metricbeat/module/aws/natgateway/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/natgateway/_meta/fields.yml @@ -4,3 +4,48 @@ `natgateway` contains the metrics from Cloudwatch to track usage of NAT gateway related resources. release: beta fields: + - name: metrics + type: group + fields: + - name: BytesInFromDestination.sum + type: long + description: The number of bytes received by the NAT gateway from the destination. + - name: BytesInFromSource.sum + type: long + description: The number of bytes received by the NAT gateway from clients in your VPC. + - name: BytesOutToDestination.sum + type: long + description: The number of bytes sent out through the NAT gateway to the destination. + - name: BytesOutToSource.sum + type: long + description: The number of bytes sent through the NAT gateway to the clients in your VPC. + - name: ConnectionAttemptCount.sum + type: long + description: The number of connection attempts made through the NAT gateway. + - name: ConnectionEstablishedCount.sum + type: long + description: The number of connections established through the NAT gateway. + - name: ErrorPortAllocation.sum + type: long + description: The number of times the NAT gateway could not allocate a source port. + - name: IdleTimeoutCount.sum + type: long + description: The number of connections that transitioned from the active state to the idle state. + - name: PacketsDropCount.sum + type: long + description: The number of packets dropped by the NAT gateway. + - name: PacketsInFromDestination.sum + type: long + description: The number of packets received by the NAT gateway from the destination. + - name: PacketsInFromSource.sum + type: long + description: The number of packets received by the NAT gateway from clients in your VPC. + - name: PacketsOutToDestination.sum + type: long + description: The number of packets sent out through the NAT gateway to the destination. + - name: PacketsOutToSource.sum + type: long + description: The number of packets sent through the NAT gateway to the clients in your VPC. + - name: ActiveConnectionCount.max + type: long + description: The total number of concurrent active TCP connections through the NAT gateway. diff --git a/x-pack/metricbeat/module/aws/sns/_meta/fields.yml b/x-pack/metricbeat/module/aws/sns/_meta/fields.yml index a70ffa1568c..df9c3f2c551 100644 --- a/x-pack/metricbeat/module/aws/sns/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/sns/_meta/fields.yml @@ -4,3 +4,39 @@ `sns` contains the metrics that were scraped from AWS CloudWatch which contains monitoring metrics sent by AWS SNS. release: beta fields: + - name: metrics + type: group + fields: + - name: PublishSize.avg + type: double + description: The size of messages published. + - name: SMSSuccessRate.avg + type: double + description: The rate of successful SMS message deliveries. + - name: NumberOfMessagesPublished.sum + type: long + description: The number of messages published to your Amazon SNS topics. + - name: NumberOfNotificationsDelivered.sum + type: long + description: The number of messages successfully delivered from your Amazon SNS topics to subscribing endpoints. + - name: NumberOfNotificationsFailed.sum + type: long + description: The number of messages that Amazon SNS failed to deliver. + - name: NumberOfNotificationsFilteredOut.sum + type: long + description: The number of messages that were rejected by subscription filter policies. + - name: NumberOfNotificationsFilteredOut-InvalidAttributes.sum + type: long + description: The number of messages that were rejected by subscription filter policies because the messages' attributes are invalid - for example, because the attribute JSON is incorrectly formatted. + - name: NumberOfNotificationsFilteredOut-NoMessageAttributes.sum + type: long + description: The number of messages that were rejected by subscription filter policies because the messages have no attributes. + - name: NumberOfNotificationsRedrivenToDlq.sum + type: long + description: The number of messages that have been moved to a dead-letter queue. + - name: NumberOfNotificationsFailedToRedriveToDlq.sum + type: long + description: The number of messages that couldn't be moved to a dead-letter queue. + - name: SMSMonthToDateSpentUSD.sum + type: long + description: The charges you have accrued since the start of the current calendar month for sending SMS messages. diff --git a/x-pack/metricbeat/module/aws/terraform.tf b/x-pack/metricbeat/module/aws/terraform.tf new file mode 100644 index 00000000000..e767a028ab1 --- /dev/null +++ b/x-pack/metricbeat/module/aws/terraform.tf @@ -0,0 +1,48 @@ +provider "aws" { + version = "~> 2.58" +} + +provider "random" { + version = "~> 2.2" +} + +resource "random_id" "suffix" { + byte_length = 4 +} + +resource "random_password" "db" { + length = 16 + special = false +} + +resource "aws_db_instance" "test" { + identifier = "metricbeat-test-${random_id.suffix.hex}" + allocated_storage = 20 // Gigabytes + engine = "mysql" + instance_class = "db.t2.micro" + name = "metricbeattest" + username = "foo" + password = random_password.db.result + skip_final_snapshot = true // Required for cleanup +} + +resource "aws_sqs_queue" "test" { + name = "metricbeat-test-${random_id.suffix.hex}" + receive_wait_time_seconds = 10 +} + +resource "aws_s3_bucket" "test" { + bucket = "metricbeat-test-${random_id.suffix.hex}" + force_destroy = true // Required for cleanup +} + +resource "aws_s3_bucket_metric" "test" { + bucket = aws_s3_bucket.test.id + name = "EntireBucket" +} + +resource "aws_s3_bucket_object" "test" { + key = "someobject" + bucket = aws_s3_bucket.test.id + content = "something" +} diff --git a/x-pack/metricbeat/module/aws/transitgateway/_meta/fields.yml b/x-pack/metricbeat/module/aws/transitgateway/_meta/fields.yml index ed376cd296d..e687ae973d0 100644 --- a/x-pack/metricbeat/module/aws/transitgateway/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/transitgateway/_meta/fields.yml @@ -4,3 +4,24 @@ `transitgateway` contains the metrics from Cloudwatch to track usage of transit gateway related resources. release: beta fields: + - name: metrics + type: group + fields: + - name: BytesIn.sum + type: long + description: The number of bytes received by the transit gateway. + - name: BytesOut.sum + type: long + description: The number of bytes sent from the transit gateway. + - name: PacketsIn.sum + type: long + description: The number of packets received by the transit gateway. + - name: PacketsOut.sum + type: long + description: The number of packets sent by the transit gateway. + - name: PacketDropCountBlackhole.sum + type: long + description: The number of packets dropped because they matched a blackhole route. + - name: PacketDropCountNoRoute.sum + type: long + description: The number of packets dropped because they did not match a route. diff --git a/x-pack/metricbeat/module/aws/usage/_meta/fields.yml b/x-pack/metricbeat/module/aws/usage/_meta/fields.yml index aa138f70eec..6b69118adc6 100644 --- a/x-pack/metricbeat/module/aws/usage/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/usage/_meta/fields.yml @@ -4,3 +4,12 @@ `usage` contains the metrics from Cloudwatch to track usage of some AWS resources. release: beta fields: + - name: metrics + type: group + fields: + - name: CallCount.sum + type: long + description: The number of specified API operations performed in your account. + - name: ResourceCount.sum + type: long + description: The number of the specified resources running in your account. The resources are defined by the dimensions associated with the metric. diff --git a/x-pack/metricbeat/module/aws/vpn/_meta/fields.yml b/x-pack/metricbeat/module/aws/vpn/_meta/fields.yml index 9d1ae32eebb..4569fcc1a57 100644 --- a/x-pack/metricbeat/module/aws/vpn/_meta/fields.yml +++ b/x-pack/metricbeat/module/aws/vpn/_meta/fields.yml @@ -4,3 +4,15 @@ `vpn` contains the metrics from Cloudwatch to track usage of VPN related resources. release: beta fields: + - name: metrics + type: group + fields: + - name: TunnelState.avg + type: double + description: The state of the tunnel. For static VPNs, 0 indicates DOWN and 1 indicates UP. For BGP VPNs, 1 indicates ESTABLISHED and 0 is used for all other states. + - name: TunnelDataIn.sum + type: double + description: The bytes received through the VPN tunnel. + - name: TunnelDataOut.sum + type: double + description: The bytes sent through the VPN tunnel. diff --git a/x-pack/metricbeat/module/azure/_meta/kibana/7/dashboard/Metricbeat-azure-database-account-overview.json b/x-pack/metricbeat/module/azure/_meta/kibana/7/dashboard/Metricbeat-azure-database-account-overview.json new file mode 100644 index 00000000000..32baee889f8 --- /dev/null +++ b/x-pack/metricbeat/module/azure/_meta/kibana/7/dashboard/Metricbeat-azure-database-account-overview.json @@ -0,0 +1,974 @@ +{ + "objects": [ + { + "attributes": { + "description": "This Azure Database Account dashboard visualizes the most important database account metrics.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 4, + "i": "fe2125b1-526c-4293-b488-86d8c15ea3fb", + "w": 9, + "x": 0, + "y": 0 + }, + "panelIndex": "fe2125b1-526c-4293-b488-86d8c15ea3fb", + "panelRefName": "panel_0", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Data Usage" + }, + "gridData": { + "h": 18, + "i": "db895503-5a84-4b26-b889-c3c7ca964643", + "w": 18, + "x": 9, + "y": 0 + }, + "panelIndex": "db895503-5a84-4b26-b889-c3c7ca964643", + "panelRefName": "panel_1", + "title": "Data Usage", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Index Usage" + }, + "gridData": { + "h": 18, + "i": "0b986285-a5d0-4a31-bdb8-0f29cd50c2eb", + "w": 21, + "x": 27, + "y": 0 + }, + "panelIndex": "0b986285-a5d0-4a31-bdb8-0f29cd50c2eb", + "panelRefName": "panel_2", + "title": "Index Usage", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 14, + "i": "7d7b71ea-eb6b-43bf-be61-80235d1d4529", + "w": 9, + "x": 0, + "y": 4 + }, + "panelIndex": "7d7b71ea-eb6b-43bf-be61-80235d1d4529", + "panelRefName": "panel_3", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Total Requests" + }, + "gridData": { + "h": 15, + "i": "f6ce8b48-a876-4031-a43f-3ca3704a4ad6", + "w": 24, + "x": 0, + "y": 18 + }, + "panelIndex": "f6ce8b48-a876-4031-a43f-3ca3704a4ad6", + "panelRefName": "panel_4", + "title": "Total Requests", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Document Count" + }, + "gridData": { + "h": 15, + "i": "c604014a-647f-4a1d-b2e9-0304fdacc363", + "w": 24, + "x": 24, + "y": 18 + }, + "panelIndex": "c604014a-647f-4a1d-b2e9-0304fdacc363", + "panelRefName": "panel_5", + "title": "Document Count", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Available Storage" + }, + "gridData": { + "h": 15, + "i": "b0cbeaf7-8a12-4efa-b63b-5479c2cb39a9", + "w": 24, + "x": 0, + "y": 33 + }, + "panelIndex": "b0cbeaf7-8a12-4efa-b63b-5479c2cb39a9", + "panelRefName": "panel_6", + "title": "Available Storage", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Requests Per Status Code" + }, + "gridData": { + "h": 15, + "i": "5e755432-3e55-405c-91cf-3348d5067a3a", + "w": 24, + "x": 24, + "y": 33 + }, + "panelIndex": "5e755432-3e55-405c-91cf-3348d5067a3a", + "panelRefName": "panel_7", + "title": "Requests Per Status Code", + "version": "7.6.0" + } + ], + "timeRestore": false, + "title": "[Metricbeat Azure] Database Account Overview", + "version": 1 + }, + "id": "b232c220-8481-11ea-b181-4b1a9e0110f9", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "4177aab0-83cc-11ea-be84-f5d4d6b9a792", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "a49b4e20-8490-11ea-b181-4b1a9e0110f9", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "d2801d70-8490-11ea-b181-4b1a9e0110f9", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "674c1d70-83cc-11ea-be84-f5d4d6b9a792", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "a16b5900-8492-11ea-b181-4b1a9e0110f9", + "name": "panel_4", + "type": "visualization" + }, + { + "id": "d3ac7d90-8492-11ea-b181-4b1a9e0110f9", + "name": "panel_5", + "type": "visualization" + }, + { + "id": "81f16b40-32ea-11ea-a83e-25b8612d00cc", + "name": "panel_6", + "type": "visualization" + }, + { + "id": "037382e0-856e-11ea-91bc-ab084c7ec0e7", + "name": "panel_7", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-04-23T14:26:12.702Z", + "version": "WzczNTAsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": {} + }, + "title": "Navigation Database Account Overview [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "fontSize": 12, + "markdown": "### Azure Database Accounts\n", + "openLinksInNewTab": false + }, + "title": "Navigation Database Account Overview [Metricbeat Azure]", + "type": "markdown" + } + }, + "id": "4177aab0-83cc-11ea-be84-f5d4d6b9a792", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-21T12:33:08.312Z", + "version": "WzY4OTcsMTFd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Database Account Data Usage [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "azure.resource.type : \"Microsoft.DocumentDb/databaseAccounts\" " + }, + "id": "e9a40230-32e9-11ea-bda2-69435df36a5c", + "index_pattern": "metricbeat-*", + "interval": "\u003e=5m", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#3185FC", + "fill": "0", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "bytes", + "id": "e9a40231-32e9-11ea-bda2-69435df36a5c", + "label": "Data Usage", + "line_width": "2", + "metrics": [ + { + "field": "azure.database_account.data_usage.total", + "id": "e9a40232-32e9-11ea-bda2-69435df36a5c", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_exclude": "\"\u003cempty\u003e\"", + "terms_field": "azure.dimensions.database_name", + "terms_order_by": "_count", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Database Account Data Usage [Metricbeat Azure]", + "type": "metrics" + } + }, + "id": "a49b4e20-8490-11ea-b181-4b1a9e0110f9", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-23T11:58:07.195Z", + "version": "WzcxODEsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Database Account Index Usage [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "azure.resource.type : \"Microsoft.DocumentDb/databaseAccounts\" " + }, + "id": "e9a40230-32e9-11ea-bda2-69435df36a5c", + "index_pattern": "metricbeat-*", + "interval": "\u003e=5m", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(176,188,0,1)", + "fill": "0", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "bytes", + "id": "e9a40231-32e9-11ea-bda2-69435df36a5c", + "label": "Index Usage", + "line_width": "2", + "metrics": [ + { + "field": "azure.database_account.index_usage.total", + "id": "e9a40232-32e9-11ea-bda2-69435df36a5c", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_exclude": "\"\u003cempty\u003e\"", + "terms_field": "azure.dimensions.database_name", + "terms_order_by": "_count", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Database Account Index Usage [Metricbeat Azure]", + "type": "metrics" + } + }, + "id": "d2801d70-8490-11ea-b181-4b1a9e0110f9", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-23T11:59:31.862Z", + "version": "WzcyMDEsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": {} + }, + "title": "Database Account Filters [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "controls": [ + { + "fieldName": "azure.subscription_id", + "id": "1584710440054", + "indexPatternRefName": "control_0_index_pattern", + "label": "Subscription", + "options": { + "dynamicOptions": true, + "multiselect": false, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "", + "type": "list" + }, + { + "fieldName": "azure.resource.group", + "id": "1584710497045", + "indexPatternRefName": "control_1_index_pattern", + "label": "Resource Group", + "options": { + "dynamicOptions": true, + "multiselect": false, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "", + "type": "list" + }, + { + "fieldName": "cloud.instance.name", + "id": "1584710535722", + "indexPatternRefName": "control_2_index_pattern", + "label": "Resource", + "options": { + "dynamicOptions": true, + "multiselect": false, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "", + "type": "list" + }, + { + "fieldName": "azure.dimensions.database_name", + "id": "1587643606086", + "indexPatternRefName": "control_3_index_pattern", + "label": "Database", + "options": { + "dynamicOptions": true, + "multiselect": true, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "1584710535722", + "type": "list" + } + ], + "pinFilters": false, + "updateFiltersOnChange": true, + "useTimeFilter": false + }, + "title": "Database Account Filters [Metricbeat Azure]", + "type": "input_control_vis" + } + }, + "id": "674c1d70-83cc-11ea-be84-f5d4d6b9a792", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "control_0_index_pattern", + "type": "index-pattern" + }, + { + "id": "metricbeat-*", + "name": "control_1_index_pattern", + "type": "index-pattern" + }, + { + "id": "metricbeat-*", + "name": "control_2_index_pattern", + "type": "index-pattern" + }, + { + "id": "metricbeat-*", + "name": "control_3_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-04-23T12:08:07.587Z", + "version": "WzcyNDAsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Database Account Total Requests [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "azure.resource.type : \"Microsoft.DocumentDb/databaseAccounts\" " + }, + "id": "e9a40230-32e9-11ea-bda2-69435df36a5c", + "index_pattern": "metricbeat-*", + "interval": "\u003e=5m", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,49,21,1)", + "fill": "0", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "e9a40231-32e9-11ea-bda2-69435df36a5c", + "label": "Total Requests", + "line_width": "2", + "metrics": [ + { + "field": "azure.database_account.total_requests.count", + "id": "e9a40232-32e9-11ea-bda2-69435df36a5c", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_exclude": "\"\u003cempty\u003e\"", + "terms_field": "azure.dimensions.database_name", + "terms_order_by": "_count", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Database Account Total Requests [Metricbeat Azure]", + "type": "metrics" + } + }, + "id": "a16b5900-8492-11ea-b181-4b1a9e0110f9", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-23T12:00:24.232Z", + "version": "WzcyMTEsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Database Account Document Count [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "azure.resource.type : \"Microsoft.DocumentDb/databaseAccounts\" " + }, + "id": "e9a40230-32e9-11ea-bda2-69435df36a5c", + "index_pattern": "metricbeat-*", + "interval": "\u003e=5m", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(102,102,102,1)", + "fill": "0", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "e9a40231-32e9-11ea-bda2-69435df36a5c", + "label": "Document Count", + "line_width": "2", + "metrics": [ + { + "field": "azure.database_account.document_count.total", + "id": "e9a40232-32e9-11ea-bda2-69435df36a5c", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_exclude": "\"\u003cempty\u003e\"", + "terms_field": "azure.dimensions.database_name", + "terms_order_by": "_count", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Database Account Document Count [Metricbeat Azure]", + "type": "metrics" + } + }, + "id": "d3ac7d90-8492-11ea-b181-4b1a9e0110f9", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-23T12:05:03.644Z", + "version": "WzcyMjEsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Database Account Available Storage [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "azure.resource.type : \"Microsoft.DocumentDb/databaseAccounts\" " + }, + "id": "e9a40230-32e9-11ea-bda2-69435df36a5c", + "index_pattern": "metricbeat-*", + "interval": "\u003e=5m", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(101,50,148,1)", + "fill": "0", + "formatter": "bytes", + "id": "e9a40231-32e9-11ea-bda2-69435df36a5c", + "label": "Available storage", + "line_width": "2", + "metrics": [ + { + "field": "azure.database_account.available_storage.total", + "id": "e9a40232-32e9-11ea-bda2-69435df36a5c", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_exclude": "\"\u003cempty\u003e\"", + "terms_field": "azure.dimensions.database_name", + "terms_order_by": "e9a40232-32e9-11ea-bda2-69435df36a5c", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Database Account Available Storage [Metricbeat Azure]", + "type": "metrics" + } + }, + "id": "81f16b40-32ea-11ea-a83e-25b8612d00cc", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-23T12:05:58.477Z", + "version": "WzcyMzMsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index" + } + }, + "title": " Database Account Requests By Status Code [Metricbeat Azure]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Total Requests", + "field": "azure.database_account.total_requests.count" + }, + "schema": "metric", + "type": "avg" + }, + { + "enabled": true, + "id": "2", + "params": { + "customLabel": "Status Codes", + "field": "azure.dimensions.status_code", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "_key", + "otherBucket": false, + "otherBucketLabel": "Other", + "size": 5 + }, + "schema": "group", + "type": "terms" + }, + { + "enabled": true, + "id": "3", + "params": { + "customLabel": "Database", + "field": "azure.dimensions.database_name", + "missingBucket": false, + "missingBucketLabel": "Missing", + "order": "desc", + "orderBy": "_key", + "otherBucket": false, + "otherBucketLabel": "Other", + "row": false, + "size": 5 + }, + "schema": "split", + "type": "terms" + } + ], + "params": { + "addLegend": true, + "addTimeMarker": false, + "addTooltip": false, + "categoryAxes": [ + { + "id": "CategoryAxis-1", + "labels": { + "filter": true, + "show": true, + "truncate": 100 + }, + "position": "bottom", + "scale": { + "type": "linear" + }, + "show": false, + "style": {}, + "title": {}, + "type": "category" + } + ], + "dimensions": { + "series": [ + { + "accessor": 0, + "aggType": "terms", + "format": { + "id": "terms", + "params": { + "id": "string", + "missingBucketLabel": "Missing", + "otherBucketLabel": "Other", + "parsedUrl": { + "basePath": "", + "origin": "http://localhost:5601", + "pathname": "/app/kibana" + } + } + }, + "label": "Status Codes", + "params": {} + } + ], + "splitColumn": [ + { + "accessor": 1, + "aggType": "terms", + "format": { + "id": "terms", + "params": { + "id": "string", + "missingBucketLabel": "Missing", + "otherBucketLabel": "Other", + "parsedUrl": { + "basePath": "", + "origin": "http://localhost:5601", + "pathname": "/app/kibana" + } + } + }, + "label": "Database", + "params": {} + } + ], + "x": null, + "y": [ + { + "accessor": 2, + "aggType": "avg", + "format": { + "id": "number", + "params": { + "parsedUrl": { + "basePath": "", + "origin": "http://localhost:5601", + "pathname": "/app/kibana" + } + } + }, + "label": "Total Requests", + "params": {} + } + ] + }, + "grid": { + "categoryLines": false + }, + "labels": { + "show": false + }, + "legendPosition": "right", + "seriesParams": [ + { + "data": { + "id": "1", + "label": "Total Requests" + }, + "drawLinesBetweenPoints": true, + "lineWidth": 2, + "mode": "stacked", + "show": true, + "showCircles": true, + "type": "histogram", + "valueAxis": "ValueAxis-1" + } + ], + "thresholdLine": { + "color": "#E7664C", + "show": false, + "style": "full", + "value": 10, + "width": 1 + }, + "times": [], + "type": "histogram", + "valueAxes": [ + { + "id": "ValueAxis-1", + "labels": { + "filter": false, + "rotate": 0, + "show": true, + "truncate": 100 + }, + "name": "LeftAxis-1", + "position": "left", + "scale": { + "mode": "normal", + "type": "linear" + }, + "show": true, + "style": {}, + "title": { + "text": "Total Requests" + }, + "type": "value" + } + ] + }, + "title": " Database Account Requests By Status Code [Metricbeat Azure]", + "type": "histogram" + } + }, + "id": "037382e0-856e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-04-23T14:23:33.900Z", + "version": "WzczNDMsMTNd" + } + ], + "version": "7.6.0" +} diff --git a/x-pack/metricbeat/module/azure/database_account/_meta/data.json b/x-pack/metricbeat/module/azure/database_account/_meta/data.json index 6749feeab71..68dbb92043c 100644 --- a/x-pack/metricbeat/module/azure/database_account/_meta/data.json +++ b/x-pack/metricbeat/module/azure/database_account/_meta/data.json @@ -16,10 +16,10 @@ "azure" : { "timegrain" : "PT5M", "dimensions" : { - "databasename" : "testdb" + "database_name" : "testdb" }, "database_account" : { - "provisionedthroughput" : { + "provisioned_throughput" : { "max" : 400 } }, diff --git a/x-pack/metricbeat/module/azure/database_account/_meta/docs.asciidoc b/x-pack/metricbeat/module/azure/database_account/_meta/docs.asciidoc index ef085175dac..182fea6b41b 100644 --- a/x-pack/metricbeat/module/azure/database_account/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/azure/database_account/_meta/docs.asciidoc @@ -1,6 +1,8 @@ This is the database_account metricset of the module azure. -This metricset allows users to retrieve all metrics from specified database accounts. +This metricset allows users to retrieve relevant metrics from specified database accounts and comes with a predefined dashboard: + +image::./images/metricbeat-azure-database-account-overview.png[] include::../../_meta/shared-azure.asciidoc[] diff --git a/x-pack/metricbeat/module/azure/database_account/manifest.yml b/x-pack/metricbeat/module/azure/database_account/manifest.yml index d714fccb089..39086f6ff66 100644 --- a/x-pack/metricbeat/module/azure/database_account/manifest.yml +++ b/x-pack/metricbeat/module/azure/database_account/manifest.yml @@ -9,39 +9,71 @@ input: resource_type: "Microsoft.DocumentDb/databaseAccounts" metrics: - name: ["AddRegion", "RemoveRegion", "UpdateAccountReplicationSettings", "UpdateAccountNetworkSettings", "UpdateAccountKeys", "ServiceAvailability", "ReplicationLatency", - "RegionFailover", "DeleteAccount", "CreateAccount", "CassandraConnectionClosures"] + "RegionFailover", "DeleteAccount", "CreateAccount", "CassandraConnectionClosures", "UpdateDiagnosticsSettings"] namespace: "Microsoft.DocumentDb/databaseAccounts" - - name: ["AvailableStorage", "DataUsage","DocumentCount", "DocumentQuota", "IndexUsage", "MetadataRequests", - "MongoRequestCharge", "MongoRequests", "MongoRequestsCount", "MongoRequestsDelete","MongoRequestsDelete", "MongoRequestsQuery", "MongoRequestsUpdate", - "ProvisionedThroughput", "TotalRequestUnits", "TotalRequests"] + - name: ["AvailableStorage", "DataUsage","DocumentCount", "DocumentQuota", "IndexUsage", "MetadataRequests", "MongoRequestCharge", "MongoRequests", "MongoRequestsCount", + "MongoRequestsInsert", "MongoRequestsDelete", "MongoRequestsQuery", "MongoRequestsUpdate","ProvisionedThroughput", "NormalizedRUConsumption"] namespace: "Microsoft.DocumentDb/databaseAccounts" timegrain: "PT5M" dimensions: - name: "DatabaseName" value: "*" + - name: ["TotalRequestUnits", "TotalRequests"] + namespace: "Microsoft.DocumentDb/databaseAccounts" + timegrain: "PT5M" + dimensions: + - name: "DatabaseName" + value: "*" + - name: "StatusCode" + value: "*" - name: ["CassandraRequestCharges", "CassandraRequests"] namespace: "Microsoft.DocumentDb/databaseAccounts" timegrain: "PT1M" dimensions: - name: "DatabaseName" value: "*" + - name: [ "GremlinDatabaseDelete", "GremlinDatabaseThroughputUpdate", "GremlinDatabaseUpdate", "GremlinGraphDelete","GremlinGraphThroughputUpdate", "GremlinGraphUpdate", + "MongoCollectionDelete", "MongoCollectionThroughputUpdate", "MongoCollectionUpdate", "MongoDBDatabaseUpdate", "MongoDatabaseDelete", "MongoDatabaseThroughputUpdate", + "CassandraKeyspaceDelete", "CassandraKeyspaceThroughputUpdate", "CassandraKeyspaceUpdate","CassandraTableDelete", "CassandraTableThroughputUpdate", "CassandraTableUpdate", + "SqlContainerDelete", "SqlContainerThroughputUpdate", "SqlContainerUpdate", "SqlDatabaseDelete", "SqlDatabaseThroughputUpdate", "SqlDatabaseUpdate", "TableTableDelete", + "TableTableThroughputUpdate","TableTableUpdate"] + namespace: "Microsoft.DocumentDb/databaseAccounts" + dimensions: + - name: "ResourceName" + value: "*" - resource_id: "" metrics: - name: ["AddRegion", "RemoveRegion", "UpdateAccountReplicationSettings", "UpdateAccountNetworkSettings", "UpdateAccountKeys", "ServiceAvailability", "ReplicationLatency", - "RegionFailover", "DeleteAccount", "CreateAccount", "CassandraConnectionClosures"] + "RegionFailover", "DeleteAccount", "CreateAccount", "CassandraConnectionClosures", "UpdateDiagnosticsSettings"] namespace: "Microsoft.DocumentDb/databaseAccounts" - - name: ["AvailableStorage", "DataUsage","DocumentCount", "DocumentQuota", "IndexUsage", "MetadataRequests", - "MongoRequestCharge", "MongoRequests", "MongoRequestsCount", "MongoRequestsDelete","MongoRequestsDelete", "MongoRequestsQuery", "MongoRequestsUpdate", - "ProvisionedThroughput", "TotalRequestUnits", "TotalRequests"] + - name: ["AvailableStorage", "DataUsage","DocumentCount", "DocumentQuota", "IndexUsage", "MetadataRequests", "MongoRequestCharge", "MongoRequests", "MongoRequestsCount", + "MongoRequestsInsert", "MongoRequestsDelete", "MongoRequestsQuery", "MongoRequestsUpdate","ProvisionedThroughput", "NormalizedRUConsumption"] namespace: "Microsoft.DocumentDb/databaseAccounts" timegrain: "PT5M" dimensions: - name: "DatabaseName" value: "*" + - name: ["TotalRequestUnits", "TotalRequests"] + namespace: "Microsoft.DocumentDb/databaseAccounts" + timegrain: "PT5M" + dimensions: + - name: "DatabaseName" + value: "*" + - name: "StatusCode" + value: "*" - name: ["CassandraRequestCharges", "CassandraRequests"] namespace: "Microsoft.DocumentDb/databaseAccounts" timegrain: "PT1M" dimensions: - name: "DatabaseName" value: "*" + - name: [ "GremlinDatabaseDelete", "GremlinDatabaseThroughputUpdate", "GremlinDatabaseUpdate", "GremlinGraphDelete","GremlinGraphThroughputUpdate", "GremlinGraphUpdate", + "MongoCollectionDelete", "MongoCollectionThroughputUpdate", "MongoCollectionUpdate", "MongoDBDatabaseUpdate", "MongoDatabaseDelete", "MongoDatabaseThroughputUpdate", + "CassandraKeyspaceDelete", "CassandraKeyspaceThroughputUpdate", "CassandraKeyspaceUpdate","CassandraTableDelete", "CassandraTableThroughputUpdate", "CassandraTableUpdate", + "SqlContainerDelete", "SqlContainerThroughputUpdate", "SqlContainerUpdate", "SqlDatabaseDelete", "SqlDatabaseThroughputUpdate", "SqlDatabaseUpdate", "TableTableDelete", + "TableTableThroughputUpdate","TableTableUpdate"] + namespace: "Microsoft.DocumentDb/databaseAccounts" + dimensions: + - name: "ResourceName" + value: "*" diff --git a/x-pack/metricbeat/module/cockroachdb/status/_meta/docs.asciidoc b/x-pack/metricbeat/module/cockroachdb/status/_meta/docs.asciidoc index e58c7689222..71d2eff78dc 100644 --- a/x-pack/metricbeat/module/cockroachdb/status/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/cockroachdb/status/_meta/docs.asciidoc @@ -6,6 +6,7 @@ WARNING: This metricset collects a large number of metrics, what can significantly impact disk usage. Processors can be used to drop unused metrics before they are stored. For example the following configuration will drop all histogram buckets: + [source,yaml] ------------------------------------------------------------------------------ - module: cockroachdb diff --git a/x-pack/metricbeat/module/googlecloud/_meta/config.yml b/x-pack/metricbeat/module/googlecloud/_meta/config.yml index 5df057bba18..640fd87bc5a 100644 --- a/x-pack/metricbeat/module/googlecloud/_meta/config.yml +++ b/x-pack/metricbeat/module/googlecloud/_meta/config.yml @@ -1,13 +1,21 @@ - module: googlecloud metricsets: - compute + region: "us-central1" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 300s + +- module: googlecloud + metricsets: - pubsub - loadbalancing zone: "us-central1-a" project_id: "your project id" credentials_file_path: "your JSON credentials file path" exclude_labels: false - period: 300s + period: 60s - module: googlecloud metricsets: @@ -17,3 +25,12 @@ credentials_file_path: "your JSON credentials file path" exclude_labels: false period: 300s + +- module: googlecloud + metricsets: + - compute + region: "us-" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s diff --git a/x-pack/metricbeat/module/googlecloud/_meta/docs.asciidoc b/x-pack/metricbeat/module/googlecloud/_meta/docs.asciidoc index ff4b03cb023..5a1e4ed62f3 100644 --- a/x-pack/metricbeat/module/googlecloud/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/googlecloud/_meta/docs.asciidoc @@ -6,18 +6,88 @@ Note: extra GCP charges on Stackdriver Monitoring API requests will be generated == Module config and parameters This is a list of the possible module parameters you can tune: -* *zone*: A single string with the zone you want to monitor like "us-central1-a". If you need to fetch from multiple regions, you have to setup a different configuration for each (but you don't need a new instance of Metricbeat running) -* *region*: A single string with the region you want to monitor like "us-central1". This will enable monitoring for all zones under this region. +* *zone*: A single string with the zone you want to monitor like `us-central1-a`. +Or you can specific a partial zone name like `us-central1-` or `us-central1-*`, +which will monitor all zones start with `us-central1-`: `us-central1-a`, +`us-central1-b`, `us-central1-c` and `us-central1-f`. +Please see https://cloud.google.com/compute/docs/regions-zones#available[GCP zones] +for zones that are available in GCP. + +* *region*: A single string with the region you want to monitor like `us-central1`. +This will enable monitoring for all zones under this region. Or you can specific +a partial region name like `us-east` or `us-east*`, which will monitor all regions start with +`us-east`: `us-east1` and `us-east4`. If both region and zone are configured, +only region will be used. +Please see https://cloud.google.com/compute/docs/regions-zones#available[GCP regions] +for regions that are available in GCP. + * *project_id*: A single string with your GCP Project ID -* *credentials_file_path*: A single string pointing to the JSON file path reachable by Metricbeat that you have created using IAM. -* *exclude_labels*: (`true`/`false` default `false`) Do not extract extra labels and metadata information from Metricsets and fetch metrics onlly. At the moment, *labels and metadata extraction is only supported* in Compute Metricset. + +* *credentials_file_path*: A single string pointing to the JSON file path +reachable by Metricbeat that you have created using IAM. + +* *exclude_labels*: (`true`/`false` default `false`) Do not extract extra labels +and metadata information from metricsets and fetch metrics only. At the moment, +*labels and metadata extraction is only supported* in `compute` metricset. + +* *period*: A single time duration specified for this module collection frequency. + +[float] +== Example configuration +* `compute` metricset is enabled to collect metrics from `us-central1-a` zone +in `elastic-observability` project. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - compute + zone: "us-central1-a" + project_id: "elastic-observability" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s +---- + +* `compute` and `pubsub` metricsets are enabled to collect metrics from all zones +under `us-central1` region in `elastic-observability` project. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - compute + - pubsub + region: "us-central1" + project_id: "elastic-observability" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s +---- + +* `compute` metricset is enabled to collect metrics from all regions starts with +`us-west` in `elastic-observability` project, which includes all zones under +`us-west1`, `us-west2`, `us-west3` and `us-west4`. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - compute + - pubsub + region: "us-west" + project_id: "elastic-observability" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s +---- [float] == Authentication, authorization and permissions. Authentication and authorization in Google Cloud Platform can be achieved in many ways. For the current version of the Google Cloud Platform module for Metricbeat, the only supported method is using Service Account JSON files. A typical JSON with a private key looks like this: [float] -==== Example Credentials +=== Example Credentials [source,json] ---- { @@ -52,7 +122,9 @@ Google Cloud Platform offers the https://cloud.google.com/monitoring/api/metrics If you also want to *extract service labels* (by setting `exclude_labels` to false, which is the default state). You also make a new API check on the corresponding service. Service labels requires a new API call to extract those metrics. In the worst case the number of API calls will be doubled. In the best case, all metrics come from the same GCP entity and 100% of the required information is included in the first API call (which is cached for subsequent calls). -A recommended `period` value between fetches is between 5 and 10 minutes, depending on how granular you want your metrics. GCP restricts information for less than 5 minutes. +If `period` value is set to 5-minute and sample period of the metric type is 60-second, then this module will collect data from this metric type once every 5 minutes with aggregation. +GCP monitoring data has a up to 240 seconds latency, which means latest monitoring data will be up to 4 minutes old. Please see https://cloud.google.com/monitoring/api/v3/latency-n-retention[Latency of GCP Monitoring Metric Data] for more details. +In googlecloud module, metrics are collected based on this ingest delay, which is also obtained from ListMetricDescriptors API. [float] === Rough estimation of the number of API Calls diff --git a/x-pack/metricbeat/module/googlecloud/_meta/fields.yml b/x-pack/metricbeat/module/googlecloud/_meta/fields.yml index 8bbae3bf4fe..41c1c09fad9 100644 --- a/x-pack/metricbeat/module/googlecloud/_meta/fields.yml +++ b/x-pack/metricbeat/module/googlecloud/_meta/fields.yml @@ -4,14 +4,23 @@ description: > GCP module fields: - - name: googlecloud.labels - type: object + - name: googlecloud + type: group fields: - - name: user.* + - name: labels type: object - - name: metadata.* - type: object - - name: metrics.* - type: object - - name: system.* + fields: + - name: user.* + type: object + - name: metadata.* + type: object + - name: metrics.* + type: object + - name: system.* + type: object + - name: "stackdriver.*.*.*.*" type: object + object_type: double + object_type_mapping_type: "*" + description: > + Metrics that returned from StackDriver API query. diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data.json index cd37490aa90..4a3ab08217c 100644 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data.json +++ b/x-pack/metricbeat/module/googlecloud/compute/_meta/data.json @@ -5,8 +5,8 @@ "id": "elastic-observability" }, "instance": { - "id": "9077240380975650630", - "name": "gke-observability-8--observability-8--bc1afd95-cwh3" + "id": "1174463293187628268", + "name": "gke-observability-8--observability-8--bc1afd95-ngmh" }, "machine": { "type": "n1-standard-4" @@ -23,16 +23,24 @@ "compute": { "instance": { "disk": { - "read_bytes_count": 0, - "read_ops_count": 0, - "write_bytes_count": 0, - "write_ops_count": 0 + "read_bytes_count": { + "value": 0 + }, + "read_ops_count": { + "value": 0 + }, + "write_bytes_count": { + "value": 0 + }, + "write_ops_count": { + "value": 0 + } } } }, "labels": { "metrics": { - "device_name": "gke-observability-8-0--pvc-ad47fe58-7bcf-11e9-a839-42010a8401a4", + "device_name": "disk-2", "device_type": "permanent", "storage_type": "pd-standard" }, diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_cpu.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_cpu.json index 8496bfd79b1..1d3a120a218 100644 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_cpu.json +++ b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_cpu.json @@ -1,69 +1,62 @@ { - "@timestamp": "2020-01-08T16:06:00.000Z", - "@metadata": { - "beat": "metricbeat", - "type": "_doc", - "version": "8.0.0" - }, - "host": { - "name": "mcastro", - "id": "54f70115bae545cbac2b150f254472a0", - "containerized": false, - "hostname": "mcastro", - "architecture": "x86_64", - "os": { - "version": "", - "family": "", - "name": "Antergos Linux", - "kernel": "5.4.3-arch1-1", - "platform": "antergos" - } - }, - "agent": { - "ephemeral_id": "8b802033-b611-414b-bcaf-1aa19e5f5901", - "hostname": "mcastro", - "id": "7e36a073-1a32-4a94-b65b-4c7f971fb228", - "version": "8.0.0", - "type": "metricbeat" - }, + "@timestamp": "2017-10-12T08:05:34.853Z", "cloud": { "account": { - "id": "elastic-metricbeat" + "id": "elastic-observability" }, - "provider": "googlecloud", "instance": { - "name": "instance-1", - "id": "4503798379141677974" + "id": "1174463293187628268", + "name": "gke-observability-8--observability-8--bc1afd95-ngmh" }, "machine": { - "type": "f1-micro" + "type": "n1-standard-4" }, - "availability_zone": "us-central1-a" + "provider": "googlecloud" }, + "cloud.availability_zone": "europe-west1-c", "event": { - "duration": 1398412653, "dataset": "googlecloud.compute", + "duration": 115000, "module": "googlecloud" }, - "metricset": { - "name": "compute", - "period": 300000 - }, "googlecloud": { "compute": { + "firewall": { + "dropped_bytes_count": { + "value": 181 + }, + "dropped_packets_count": { + "value": 3 + } + }, "instance": { "cpu": { - "reserved_cores": 0.2, - "utilization": 0.005524845140497596 + "reserved_cores": { + "value": 4 + }, + "usage_time": { + "value": 63.478293027728796 + }, + "utilization": { + "value": 0.26449288761553663 + } + }, + "uptime": { + "value": 60 } } }, - "labels": {} + "labels": { + "user": { + "goog-gke-node": "" + } + } + }, + "metricset": { + "name": "compute", + "period": 10000 }, "service": { "type": "googlecloud" - }, - "ecs": { - "version": "1.2.0" } -} +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk.json new file mode 100644 index 00000000000..8da39b6ab7d --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk.json @@ -0,0 +1,59 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "cloud": { + "account": { + "id": "elastic-observability" + }, + "instance": { + "id": "8390997210852978465", + "name": "gke-observability-7--observability-7--3dd3e39b-0jm5" + }, + "machine": { + "type": "n1-standard-4" + }, + "provider": "googlecloud" + }, + "cloud.availability_zone": "europe-west1-c", + "event": { + "dataset": "googlecloud.compute", + "duration": 115000, + "module": "googlecloud" + }, + "googlecloud": { + "compute": { + "instance": { + "disk": { + "read_bytes_count": { + "value": 0 + }, + "read_ops_count": { + "value": 0 + }, + "write_bytes_count": { + "value": 0 + }, + "write_ops_count": { + "value": 0 + } + } + } + }, + "labels": { + "metrics": { + "device_name": "gke-observability-7-1--pvc-65581044-7d5d-11ea-8cd9-42010af0011c", + "device_type": "permanent", + "storage_type": "pd-standard" + }, + "user": { + "goog-gke-node": "" + } + } + }, + "metricset": { + "name": "compute", + "period": 10000 + }, + "service": { + "type": "googlecloud" + } +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk_01.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk_01.json deleted file mode 100644 index 038c451d934..00000000000 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk_01.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "@timestamp": "2020-01-08T16:05:00.000Z", - "@metadata": { - "beat": "metricbeat", - "type": "_doc", - "version": "8.0.0" - }, - "agent": { - "hostname": "mcastro", - "id": "7e36a073-1a32-4a94-b65b-4c7f971fb228", - "version": "8.0.0", - "type": "metricbeat", - "ephemeral_id": "8b802033-b611-414b-bcaf-1aa19e5f5901" - }, - "ecs": { - "version": "1.2.0" - }, - "googlecloud": { - "labels": { - "metrics": { - "device_type": "permanent", - "storage_type": "pd-standard", - "device_name": "instance-1" - } - }, - "compute": { - "instance": { - "disk": { - "write_bytes_count": 945853 - } - } - } - }, - "service": { - "type": "googlecloud" - }, - "cloud": { - "account": { - "id": "elastic-metricbeat" - }, - "provider": "googlecloud", - "instance": { - "name": "instance-1", - "id": "4503798379141677974" - }, - "machine": { - "type": "f1-micro" - }, - "availability_zone": "us-central1-a" - }, - "metricset": { - "name": "compute", - "period": 300000 - }, - "event": { - "module": "googlecloud", - "duration": 1398637364, - "dataset": "googlecloud.compute" - }, - "host": { - "containerized": false, - "hostname": "mcastro", - "architecture": "x86_64", - "os": { - "platform": "antergos", - "version": "", - "family": "", - "name": "Antergos Linux", - "kernel": "5.4.3-arch1-1" - }, - "name": "mcastro", - "id": "54f70115bae545cbac2b150f254472a0" - } -} diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk_02.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk_02.json deleted file mode 100644 index d3d0bc9c5ba..00000000000 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_disk_02.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "@timestamp": "2020-01-08T16:04:00.000Z", - "@metadata": { - "beat": "metricbeat", - "type": "_doc", - "version": "8.0.0" - }, - "service": { - "type": "googlecloud" - }, - "ecs": { - "version": "1.2.0" - }, - "host": { - "os": { - "name": "Antergos Linux", - "kernel": "5.4.3-arch1-1", - "platform": "antergos", - "version": "", - "family": "" - }, - "id": "54f70115bae545cbac2b150f254472a0", - "containerized": false, - "name": "mcastro", - "hostname": "mcastro", - "architecture": "x86_64" - }, - "agent": { - "ephemeral_id": "8b802033-b611-414b-bcaf-1aa19e5f5901", - "hostname": "mcastro", - "id": "7e36a073-1a32-4a94-b65b-4c7f971fb228", - "version": "8.0.0", - "type": "metricbeat" - }, - "cloud": { - "availability_zone": "us-central1-a", - "account": { - "id": "elastic-metricbeat" - }, - "provider": "googlecloud", - "instance": { - "id": "4503798379141677974", - "name": "instance-1" - }, - "machine": { - "type": "f1-micro" - } - }, - "metricset": { - "name": "compute", - "period": 300000 - }, - "event": { - "dataset": "googlecloud.compute", - "module": "googlecloud", - "duration": 1398743696 - }, - "googlecloud": { - "labels": { - "metrics": { - "device_name": "instance-1", - "device_type": "permanent", - "storage_type": "pd-standard" - } - }, - "compute": { - "instance": { - "disk": { - "write_ops_count": 140, - "read_ops_count": 2897, - "read_bytes_count": 71574649, - "write_bytes_count": 2557677 - } - } - } - } -} diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_firewall.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_firewall.json index ee219ec74e0..04b750b8b7d 100644 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_firewall.json +++ b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_firewall.json @@ -1,75 +1,62 @@ { - "@timestamp": "2020-01-08T16:05:00.000Z", - "@metadata": { - "beat": "metricbeat", - "type": "_doc", - "version": "8.0.0" - }, - "ecs": { - "version": "1.2.0" - }, - "host": { - "containerized": false, - "name": "mcastro", - "hostname": "mcastro", - "architecture": "x86_64", - "os": { - "version": "", - "family": "", - "name": "Antergos Linux", - "kernel": "5.4.3-arch1-1", - "platform": "antergos" - }, - "id": "54f70115bae545cbac2b150f254472a0" - }, - "agent": { - "type": "metricbeat", - "ephemeral_id": "8b802033-b611-414b-bcaf-1aa19e5f5901", - "hostname": "mcastro", - "id": "7e36a073-1a32-4a94-b65b-4c7f971fb228", - "version": "8.0.0" - }, + "@timestamp": "2017-10-12T08:05:34.853Z", "cloud": { - "availability_zone": "us-central1-a", "account": { - "id": "elastic-metricbeat" + "id": "elastic-observability" }, - "provider": "googlecloud", "instance": { - "id": "4503798379141677974", - "name": "instance-1" + "id": "2528596280375797115", + "name": "gke-dev-next-oblt-dev-next-oblt-pool-404d7f0c-cpj6" }, "machine": { - "type": "f1-micro" - } + "type": "n1-standard-4" + }, + "provider": "googlecloud" }, + "cloud.availability_zone": "europe-west1-c", "event": { "dataset": "googlecloud.compute", - "module": "googlecloud", - "duration": 1397755844 - }, - "metricset": { - "name": "compute", - "period": 300000 + "duration": 115000, + "module": "googlecloud" }, "googlecloud": { - "labels": {}, "compute": { + "firewall": { + "dropped_bytes_count": { + "value": 386 + }, + "dropped_packets_count": { + "value": 7 + } + }, "instance": { - "uptime": 60.00000000000001, "cpu": { - "reserved_cores": 0.2, - "utilization": 0.38202685489490784, - "usage_time": 0.06629814168597115 + "reserved_cores": { + "value": 4 + }, + "usage_time": { + "value": 106.88293868489563 + }, + "utilization": { + "value": 0.4453455778537318 + } + }, + "uptime": { + "value": 60 } - }, - "firewall": { - "dropped_packets_count": 3 + } + }, + "labels": { + "user": { + "goog-gke-node": "" } } }, + "metricset": { + "name": "compute", + "period": 10000 + }, "service": { "type": "googlecloud" } -} - +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_instance.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_instance.json deleted file mode 100644 index 4306273f73c..00000000000 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_instance.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "@timestamp": "2020-01-08T16:04:00.000Z", - "@metadata": { - "beat": "metricbeat", - "type": "_doc", - "version": "8.0.0" - }, - "ecs": { - "version": "1.2.0" - }, - "host": { - "os": { - "platform": "antergos", - "version": "", - "family": "", - "name": "Antergos Linux", - "kernel": "5.4.3-arch1-1" - }, - "id": "54f70115bae545cbac2b150f254472a0", - "containerized": false, - "hostname": "mcastro", - "name": "mcastro", - "architecture": "x86_64" - }, - "agent": { - "ephemeral_id": "8b802033-b611-414b-bcaf-1aa19e5f5901", - "hostname": "mcastro", - "id": "7e36a073-1a32-4a94-b65b-4c7f971fb228", - "version": "8.0.0", - "type": "metricbeat" - }, - "cloud": { - "provider": "googlecloud", - "instance": { - "id": "4503798379141677974", - "name": "instance-1" - }, - "machine": { - "type": "f1-micro" - }, - "availability_zone": "us-central1-a", - "account": { - "id": "elastic-metricbeat" - } - }, - "event": { - "module": "googlecloud", - "duration": 1397750508, - "dataset": "googlecloud.compute" - }, - "metricset": { - "period": 300000, - "name": "compute" - }, - "googlecloud": { - "labels": {}, - "compute": { - "firewall": { - "dropped_bytes_count": 0, - "dropped_packets_count": 0 - }, - "instance": { - "uptime": 46.181442, - "cpu": { - "usage_time": 4.5843222587388945 - } - } - } - }, - "service": { - "type": "googlecloud" - } -} diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network.json new file mode 100644 index 00000000000..d543fc2382f --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network.json @@ -0,0 +1,54 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "cloud": { + "account": { + "id": "elastic-observability" + }, + "instance": { + "id": "7208038667777737825", + "name": "gke-dev-next-oblt-dev-next-oblt-pool-404d7f0c-fgxk" + }, + "machine": { + "type": "n1-standard-4" + }, + "provider": "googlecloud" + }, + "cloud.availability_zone": "europe-west1-c", + "event": { + "dataset": "googlecloud.compute", + "duration": 115000, + "module": "googlecloud" + }, + "googlecloud": { + "compute": { + "instance": { + "network": { + "received_bytes_count": { + "value": 17913 + }, + "received_packets_count": { + "value": 128 + }, + "sent_bytes_count": { + "value": 841 + } + } + } + }, + "labels": { + "metrics": { + "loadbalanced": "true" + }, + "user": { + "goog-gke-node": "" + } + } + }, + "metricset": { + "name": "compute", + "period": 10000 + }, + "service": { + "type": "googlecloud" + } +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network_01.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network_01.json deleted file mode 100644 index 26c21d62295..00000000000 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network_01.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "@timestamp": "2020-01-08T16:04:00.000Z", - "@metadata": { - "beat": "metricbeat", - "type": "_doc", - "version": "8.0.0" - }, - "cloud": { - "machine": { - "type": "f1-micro" - }, - "availability_zone": "us-central1-a", - "account": { - "id": "elastic-metricbeat" - }, - "provider": "googlecloud", - "instance": { - "id": "4503798379141677974", - "name": "instance-1" - } - }, - "ecs": { - "version": "1.2.0" - }, - "host": { - "name": "mcastro", - "hostname": "mcastro", - "architecture": "x86_64", - "os": { - "kernel": "5.4.3-arch1-1", - "platform": "antergos", - "version": "", - "family": "", - "name": "Antergos Linux" - }, - "id": "54f70115bae545cbac2b150f254472a0", - "containerized": false - }, - "agent": { - "hostname": "mcastro", - "id": "7e36a073-1a32-4a94-b65b-4c7f971fb228", - "version": "8.0.0", - "type": "metricbeat", - "ephemeral_id": "8b802033-b611-414b-bcaf-1aa19e5f5901" - }, - "event": { - "duration": 1397728251, - "dataset": "googlecloud.compute", - "module": "googlecloud" - }, - "metricset": { - "name": "compute", - "period": 300000 - }, - "googlecloud": { - "labels": { - "metrics": { - "loadbalanced": "false" - } - }, - "compute": { - "instance": { - "network": { - "received_bytes_count": 3846, - "sent_bytes_count": 1750, - "received_packets_count": 17 - } - } - } - }, - "service": { - "type": "googlecloud" - } -} diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network_02.json b/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network_02.json deleted file mode 100644 index cc71eda5683..00000000000 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/data_network_02.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "@timestamp": "2020-01-08T16:05:00.000Z", - "@metadata": { - "beat": "metricbeat", - "type": "_doc", - "version": "8.0.0" - }, - "event": { - "dataset": "googlecloud.compute", - "module": "googlecloud", - "duration": 1398297740 - }, - "metricset": { - "name": "compute", - "period": 300000 - }, - "googlecloud": { - "labels": { - "metrics": { - "loadbalanced": "false" - } - }, - "compute": { - "instance": { - "network": { - "sent_bytes_count": 3977 - } - } - } - }, - "service": { - "type": "googlecloud" - }, - "ecs": { - "version": "1.2.0" - }, - "host": { - "containerized": false, - "name": "mcastro", - "hostname": "mcastro", - "architecture": "x86_64", - "os": { - "family": "", - "name": "Antergos Linux", - "kernel": "5.4.3-arch1-1", - "platform": "antergos", - "version": "" - }, - "id": "54f70115bae545cbac2b150f254472a0" - }, - "agent": { - "id": "7e36a073-1a32-4a94-b65b-4c7f971fb228", - "version": "8.0.0", - "type": "metricbeat", - "ephemeral_id": "8b802033-b611-414b-bcaf-1aa19e5f5901", - "hostname": "mcastro" - }, - "cloud": { - "account": { - "id": "elastic-metricbeat" - }, - "provider": "googlecloud", - "instance": { - "id": "4503798379141677974", - "name": "instance-1" - }, - "machine": { - "type": "f1-micro" - }, - "availability_zone": "us-central1-a" - } -} diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/docs.asciidoc b/x-pack/metricbeat/module/googlecloud/compute/_meta/docs.asciidoc index 8cc6b3f85a2..f72103099ea 100644 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/googlecloud/compute/_meta/docs.asciidoc @@ -1,11 +1,15 @@ -Compute Metricset to fetch metrics from https://cloud.google.com/compute/[Compute Engine] Virtual Machines in Google Cloud Platform. No Monitoring or Logging agent is required in your instances to use this Metricset. +Compute metricset to fetch metrics from https://cloud.google.com/compute/[Compute Engine] Virtual Machines in Google Cloud Platform. No Monitoring or Logging agent is required in your instances to use this metricset. -The `compute` Metricset contains all metrics exported from the https://cloud.google.com/monitoring/api/metrics_gcp#gcp-compute[Stackdriver API]. The field names have been left untouched for people already familiar with them. +The `compute` metricset contains all metrics exported from the https://cloud.google.com/monitoring/api/metrics_gcp#gcp-compute[Stackdriver API]. The field names have been left untouched for people already familiar with them. Extra labels and metadata are also extracted using the https://cloud.google.com/compute/docs/reference/rest/v1/instances/get[Compute API]. This is enough to get most of the info associated with a metric like Compute labels and metadata and metric specific Labels. [float] -=== Fields and labels +=== Metrics and labels +Here is a list of metrics collected by `compute` metricset: + +[float] +==== firewall * `instance.firewall.dropped_bytes_count`: Incoming bytes dropped by the firewall. - `instance_name`: The name of the VM instance. @@ -13,14 +17,21 @@ Extra labels and metadata are also extracted using the https://cloud.google.com/ * `instance.firewall.dropped_packets_count`: Incoming packets dropped by the firewall. - `instance_name`: The name of the VM instance. +[float] +==== cpu + * `instance.cpu.reserved_cores`: Number of cores reserved on the host of the `instance`. - `instance_name`: The name of the VM instance. * `instance.cpu.utilization`: The fraction of the allocated CPU that is currently in use on the `instance`. - `instance_name`: The name of the VM instance. + * `instance.cpu.usage_time`: Usage for all cores in seconds. - `instance_name`: The name of the VM instance. +[float] +==== disk + * `instance.disk.read_bytes_count`: Count of bytes read from disk. - `instance_name`: The name of the VM instance. - `device_name`: The name of the disk device. @@ -45,9 +56,15 @@ Extra labels and metadata are also extracted using the https://cloud.google.com/ - `storage_type`: The storage type: `pd-standard`, `pd-ssd`, or `local-ssd`. - `device_type`: The disk type: `ephemeral` or `permanent`. +[float] +==== uptime + * `instance.uptime`: How long the VM has been running, in seconds - `instance_name`: The name of the VM instance. +[float] +==== network + * `instance.network.received_bytes_count`: Count of bytes received from the network - `instance_name`: The name of the VM instance. - `loadBalanced`: Whether traffic was sent from an L3 loadbalanced IP address assigned to the VM. Traffic that is externally routed from the VM's standard internal or external IP address, such as L7 loadbalanced traffic, is not considered to be loadbalanced in this metric. diff --git a/x-pack/metricbeat/module/googlecloud/compute/_meta/fields.yml b/x-pack/metricbeat/module/googlecloud/compute/_meta/fields.yml index 01be5ebf386..5cbdfb3ea56 100644 --- a/x-pack/metricbeat/module/googlecloud/compute/_meta/fields.yml +++ b/x-pack/metricbeat/module/googlecloud/compute/_meta/fields.yml @@ -9,54 +9,54 @@ - name: firewall type: group fields: - - name: dropped_bytes_count + - name: dropped_bytes_count.value type: long description: Incoming bytes dropped by the firewall - - name: dropped_packets_count + - name: dropped_packets_count.value type: long description: Incoming packets dropped by the firewall - name: cpu type: group fields: - - name: reserved_cores + - name: reserved_cores.value type: double description: Number of cores reserved on the host of the instance - - name: utilization + - name: utilization.value type: double description: The fraction of the allocated CPU that is currently in use on the instance - - name: usage_time + - name: usage_time.value type: double description: Usage for all cores in seconds - name: disk type: group fields: - - name: read_bytes_count + - name: read_bytes_count.value type: long description: Count of bytes read from disk - - name: read_ops_count + - name: read_ops_count.value type: long description: Count of disk read IO operations - - name: write_bytes_count + - name: write_bytes_count.value type: long description: Count of bytes written to disk - - name: write_ops_count + - name: write_ops_count.value type: long description: Count of disk write IO operations - - name: uptime + - name: uptime.value type: long description: How long the VM has been running, in seconds - name: network type: group fields: - - name: received_bytes_count + - name: received_bytes_count.value type: long description: Count of bytes received from the network - - name: received_packets_count + - name: received_packets_count.value type: long description: Count of packets received from the network - - name: sent_bytes_count + - name: sent_bytes_count.value type: long description: Count of bytes sent over the network - - name: sent_packets_count + - name: sent_packets_count.value type: long description: Count of packets sent over the network diff --git a/x-pack/metricbeat/module/googlecloud/compute/compute_integration_test.go b/x-pack/metricbeat/module/googlecloud/compute/compute_integration_test.go index ae9400fdeb3..be2dd08cdec 100644 --- a/x-pack/metricbeat/module/googlecloud/compute/compute_integration_test.go +++ b/x-pack/metricbeat/module/googlecloud/compute/compute_integration_test.go @@ -8,14 +8,39 @@ package compute import ( + "fmt" "testing" + "github.com/elastic/beats/v7/libbeat/common" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" - "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" + "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud/stackdriver" ) func TestData(t *testing.T) { - config := googlecloud.GetConfigForTest(t, "compute") - metricSet := mbtest.NewFetcher(t, config) - metricSet.WriteEvents(t, "/") + metricPrefixIs := func(metricPrefix string) func(e common.MapStr) bool { + return func(e common.MapStr) bool { + v, err := e.GetValue(metricPrefix) + return err == nil && v != nil + } + } + + dataFiles := []struct { + metricPrefix string + path string + }{ + {"googlecloud.compute.instance", "./_meta/data.json"}, + {"googlecloud.compute.instance.disk", "./_meta/data_disk.json"}, + {"googlecloud.compute.instance.network", "./_meta/data_network.json"}, + {"googlecloud.compute.instance.cpu", "./_meta/data_cpu.json"}, + {"googlecloud.compute.firewall", "./_meta/data_firewall.json"}, + } + + config := stackdriver.GetConfigForTest(t, "compute") + + for _, df := range dataFiles { + metricSet := mbtest.NewFetcher(t, config) + t.Run(fmt.Sprintf("metric prefix: %s", df.metricPrefix), func(t *testing.T) { + metricSet.WriteEventsCond(t, df.path, metricPrefixIs(df.metricPrefix)) + }) + } } diff --git a/x-pack/metricbeat/module/googlecloud/compute/manifest.yml b/x-pack/metricbeat/module/googlecloud/compute/manifest.yml index 03b16dcd440..34210db8c0e 100644 --- a/x-pack/metricbeat/module/googlecloud/compute/manifest.yml +++ b/x-pack/metricbeat/module/googlecloud/compute/manifest.yml @@ -6,16 +6,17 @@ input: stackdriver: service: compute metrics: - - "compute.googleapis.com/firewall/dropped_bytes_count" - - "compute.googleapis.com/firewall/dropped_packets_count" - - "compute.googleapis.com/instance/cpu/reserved_cores" - - "compute.googleapis.com/instance/cpu/usage_time" - - "compute.googleapis.com/instance/cpu/utilization" - - "compute.googleapis.com/instance/disk/read_bytes_count" - - "compute.googleapis.com/instance/disk/read_ops_count" - - "compute.googleapis.com/instance/disk/write_bytes_count" - - "compute.googleapis.com/instance/disk/write_ops_count" - - "compute.googleapis.com/instance/network/received_bytes_count" - - "compute.googleapis.com/instance/network/received_packets_count" - - "compute.googleapis.com/instance/network/sent_bytes_count" - - "compute.googleapis.com/instance/uptime" + - metric_types: + - "compute.googleapis.com/firewall/dropped_bytes_count" + - "compute.googleapis.com/firewall/dropped_packets_count" + - "compute.googleapis.com/instance/cpu/reserved_cores" + - "compute.googleapis.com/instance/cpu/usage_time" + - "compute.googleapis.com/instance/cpu/utilization" + - "compute.googleapis.com/instance/disk/read_bytes_count" + - "compute.googleapis.com/instance/disk/read_ops_count" + - "compute.googleapis.com/instance/disk/write_bytes_count" + - "compute.googleapis.com/instance/disk/write_ops_count" + - "compute.googleapis.com/instance/network/received_bytes_count" + - "compute.googleapis.com/instance/network/received_packets_count" + - "compute.googleapis.com/instance/network/sent_bytes_count" + - "compute.googleapis.com/instance/uptime" diff --git a/x-pack/metricbeat/module/googlecloud/constants.go b/x-pack/metricbeat/module/googlecloud/constants.go index b5ded8bf9bc..19b7e27c53d 100644 --- a/x-pack/metricbeat/module/googlecloud/constants.go +++ b/x-pack/metricbeat/module/googlecloud/constants.go @@ -4,16 +4,14 @@ package googlecloud +import monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" + const ( // ModuleName in Metricbeat ModuleName = "googlecloud" - // MinTimeIntervalDataWindowMinutes is the minimum time in minutes that we allow the user to specify when requesting past metrics. Less than 5 minutes - // usually return no results. - MinTimeIntervalDataWindowMinutes = 5 - - // MaxTimeIntervalDataWindowMinutes is the max time in minutes that we allow the user to specify when requesting past metrics. - MaxTimeIntervalDataWindowMinutes = 60 + // MonitoringMetricsSamplingRate (in second) refers to how frequent monitoring collects measurement in GCP. + MonitoringMetricsSamplingRate = 60 ) // Metricsets / GCP services names @@ -73,3 +71,53 @@ const ( LabelUser = "user" LabelMetadata = "metadata" ) + +// Available perSeriesAligner map +// https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.alertPolicies#Aligner +var AlignersMapToGCP = map[string]monitoringpb.Aggregation_Aligner{ + "ALIGN_NONE": monitoringpb.Aggregation_ALIGN_NONE, + "ALIGN_DELTA": monitoringpb.Aggregation_ALIGN_DELTA, + "ALIGN_RATE": monitoringpb.Aggregation_ALIGN_RATE, + "ALIGN_INTERPOLATE": monitoringpb.Aggregation_ALIGN_INTERPOLATE, + "ALIGN_NEXT_OLDER": monitoringpb.Aggregation_ALIGN_NEXT_OLDER, + "ALIGN_MIN": monitoringpb.Aggregation_ALIGN_MIN, + "ALIGN_MAX": monitoringpb.Aggregation_ALIGN_MAX, + "ALIGN_MEAN": monitoringpb.Aggregation_ALIGN_MEAN, + "ALIGN_COUNT": monitoringpb.Aggregation_ALIGN_COUNT, + "ALIGN_SUM": monitoringpb.Aggregation_ALIGN_SUM, + "ALIGN_STDDEV": monitoringpb.Aggregation_ALIGN_STDDEV, + "ALIGN_COUNT_TRUE": monitoringpb.Aggregation_ALIGN_COUNT_TRUE, + "ALIGN_COUNT_FALSE": monitoringpb.Aggregation_ALIGN_COUNT_FALSE, + "ALIGN_FRACTION_TRUE": monitoringpb.Aggregation_ALIGN_FRACTION_TRUE, + "ALIGN_PERCENTILE_99": monitoringpb.Aggregation_ALIGN_PERCENTILE_99, + "ALIGN_PERCENTILE_95": monitoringpb.Aggregation_ALIGN_PERCENTILE_95, + "ALIGN_PERCENTILE_50": monitoringpb.Aggregation_ALIGN_PERCENTILE_50, + "ALIGN_PERCENTILE_05": monitoringpb.Aggregation_ALIGN_PERCENTILE_05, + "ALIGN_PERCENT_CHANGE": monitoringpb.Aggregation_ALIGN_PERCENT_CHANGE, +} + +const ( + DefaultAligner = "ALIGN_NONE" +) + +var AlignersMapToSuffix = map[string]string{ + "ALIGN_NONE": ".value", + "ALIGN_DELTA": ".delta", + "ALIGN_RATE": ".rate", + "ALIGN_INTERPOLATE": ".interpolate", + "ALIGN_NEXT_OLDER": ".next_older", + "ALIGN_MIN": ".min", + "ALIGN_MAX": ".max", + "ALIGN_MEAN": ".avg", + "ALIGN_COUNT": ".count", + "ALIGN_SUM": ".sum", + "ALIGN_STDDEV": ".stddev", + "ALIGN_COUNT_TRUE": ".count_true", + "ALIGN_COUNT_FALSE": ".count_false", + "ALIGN_FRACTION_TRUE": ".fraction_true", + "ALIGN_PERCENTILE_99": ".percentile_99", + "ALIGN_PERCENTILE_95": ".percentile_95", + "ALIGN_PERCENTILE_50": ".percentile_50", + "ALIGN_PERCENTILE_05": ".percentile_05", + "ALIGN_PERCENT_CHANGE": ".percent_change", +} diff --git a/x-pack/metricbeat/module/googlecloud/fields.go b/x-pack/metricbeat/module/googlecloud/fields.go index 8fac4947787..6d629ce05c2 100644 --- a/x-pack/metricbeat/module/googlecloud/fields.go +++ b/x-pack/metricbeat/module/googlecloud/fields.go @@ -19,5 +19,5 @@ func init() { // AssetGooglecloud returns asset data. // This is the base64 encoded gzipped contents of module/googlecloud. func AssetGooglecloud() string { - return "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" + return "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" } diff --git a/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/data.json b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/data.json index 2106cb1e277..97a9192732a 100644 --- a/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/data.json +++ b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/data.json @@ -14,120 +14,30 @@ "googlecloud": { "labels": { "metrics": { - "cache_result": "DISABLED", - "proxy_continent": "Europe", - "response_code": "502", - "response_code_class": "500" + "client_network": "ocp-be-c5kjr-network", + "client_subnetwork": "ocp-be-c5kjr-worker-subnet", + "client_zone": "us-central1-a" }, "resource": { - "backend_name": "INVALID_BACKEND", - "backend_scope": "INVALID_BACKEND", - "backend_scope_type": "INVALID_BACKEND", - "backend_target_name": "test1-backend-ks", + "backend_name": "ocp-be-c5kjr-master-us-central1-a", + "backend_scope": "us-central1-a", + "backend_scope_type": "ZONE", + "backend_subnetwork_name": "ocp-be-c5kjr-master-subnet", + "backend_target_name": "ocp-be-c5kjr-api-internal", "backend_target_type": "BACKEND_SERVICE", - "backend_type": "INVALID_BACKEND", - "forwarding_rule_name": "test-lb-ks-forwarding-rule", - "matched_url_path_rule": "UNMATCHED", - "region": "global", - "target_proxy_name": "test-lb-ks-target-proxy", - "url_map_name": "test-lb-ks" + "backend_type": "INSTANCE_GROUP", + "forwarding_rule_name": "ocp-be-c5kjr-api-internal", + "load_balancer_name": "ocp-be-c5kjr-api-internal", + "network_name": "ocp-be-c5kjr-network", + "region": "us-central1" } }, "loadbalancing": { - "https": { - "backend_latencies": { - "count": 4, - "mean": 97.927, - "bucket_options": { - "Options": { - "ExponentialBuckets": { - "num_finite_buckets": 66, - "growth_factor": 1.4, - "scale": 1 - } - } - }, - "bucket_counts": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 4 - ] - }, - "backend_request_bytes_count": 736, - "backend_request_count": 4, - "backend_response_bytes_count": 1952, - "frontend_tcp_rtt": { - "count": 4, - "mean": 50, - "bucket_options": { - "Options": { - "ExponentialBuckets": { - "num_finite_buckets": 66, - "growth_factor": 1.4, - "scale": 1 - } - } - }, - "bucket_counts": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 4 - ] - }, - "request_bytes_count": 736, - "request_count": 4, - "response_bytes_count": 1952, - "total_latencies": { - "count": 4, - "mean": 98.423, - "bucket_options": { - "Options": { - "ExponentialBuckets": { - "num_finite_buckets": 66, - "growth_factor": 1.4, - "scale": 1 - } - } - }, - "bucket_counts": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 4 - ] + "l3": { + "internal": { + "egress_packets_count": { + "value": 0 + } } } } @@ -139,4 +49,4 @@ "service": { "type": "googlecloud" } -} \ No newline at end of file +} diff --git a/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/data_l3.json b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/data_l3.json new file mode 100644 index 00000000000..9a58a5ebc5b --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/data_l3.json @@ -0,0 +1,50 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "cloud": { + "account": { + "id": "elastic-observability" + }, + "provider": "googlecloud" + }, + "event": { + "dataset": "googlecloud.loadbalancing", + "duration": 115000, + "module": "googlecloud" + }, + "googlecloud": { + "labels": { + "metrics": { + "client_network": "UNKNOWN", + "client_subnetwork": "REMOTE_IS_EXTERNAL", + "client_zone": "UNKNOWN" + }, + "resource": { + "backend_name": "ocp-be-c5kjr-master-us-central1-c", + "backend_scope": "us-central1-c", + "backend_scope_type": "ZONE", + "backend_subnetwork_name": "ocp-be-c5kjr-master-subnet", + "backend_target_name": "ocp-be-c5kjr-api-internal", + "backend_target_type": "BACKEND_SERVICE", + "backend_type": "INSTANCE_GROUP", + "forwarding_rule_name": "ocp-be-c5kjr-api-internal", + "load_balancer_name": "ocp-be-c5kjr-api-internal", + "network_name": "ocp-be-c5kjr-network", + "region": "us-central1" + } + }, + "loadbalancing": { + "l3": { + "internal": { + "egress_packets_count": 394 + } + } + } + }, + "metricset": { + "name": "loadbalancing", + "period": 10000 + }, + "service": { + "type": "googlecloud" + } +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/docs.asciidoc b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/docs.asciidoc index 256e744ba6b..2022b44d1c7 100644 --- a/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/docs.asciidoc @@ -3,25 +3,32 @@ Load Balancing metricset to fetch metrics from https://cloud.google.com/load-bal The `loadbalancing` metricset contains all metrics exported from the https://cloud.google.com/monitoring/api/metrics_gcp#gcp-loadbalancing[Stackdriver API]. The field names have been left untouched for people already familiar with them. [float] -=== Fields +=== Metrics +Here is a list of metrics collected by `loadbalancing` metricset: + +[float] +==== https -- `loadbalancing.https.backend_latencies`: A distribution of the latency calculated from when the request was sent by the proxy to the backend until the proxy received from the backend the last byte of response. - `loadbalancing.https.backend_request_bytes_count`: The number of bytes sent as requests from HTTP/S load balancer to backends. - `loadbalancing.https.backend_request_count`: The number of requests served by backends of HTTP/S load balancer. - `loadbalancing.https.backend_response_bytes_count`: The number of bytes sent as responses from backends (or cache) to HTTP/S load balancer. -- `loadbalancing.https.frontend_tcp_rtt`: A distribution of the RTT measured for each connection between client and proxy. - `loadbalancing.https.request_bytes_count`: The number of bytes sent as requests from clients to HTTP/S load balancer. - `loadbalancing.https.request_count`: The number of requests served by HTTP/S load balancer. - `loadbalancing.https.response_bytes_count`: The number of bytes sent as responses from HTTP/S load balancer to clients. -- `loadbalancing.https.total_latencies`: A distribution of the latency calculated from when the request was received by the proxy until the proxy got ACK from client on last response byte. + +[float] +==== l3 + - `loadbalancing.l3.internal.egress_bytes_count`: The number of bytes sent from ILB backend to client (for TCP flows it's counting bytes on application stream only). - `loadbalancing.l3.internal.egress_packets_count`: The number of packets sent from ILB backend to client of the flow. - `loadbalancing.l3.internal.ingress_bytes_count`: The number of bytes sent from client to ILB backend (for TCP flows it's counting bytes on application stream only). - `loadbalancing.l3.internal.ingress_packets_count`: The number of packets sent from client to ILB backend. -- `loadbalancing.l3.internal.rtt_latencies`: A distribution of RTT measured over TCP connections for ILB flows. + +[float] +==== tcp_ssl_proxy + - `loadbalancing.tcp_ssl_proxy.closed_connections`: Number of connections that were terminated over TCP/SSL proxy. - `loadbalancing.tcp_ssl_proxy.egress_bytes_count`: Number of bytes sent from VM to client using proxy. -- `loadbalancing.tcp_ssl_proxy.frontend_tcp_rtt`: A distribution of the smoothed RTT (in ms) measured by the proxy's TCP stack, each minute application layer bytes pass from proxy to client. - `loadbalancing.tcp_ssl_proxy.ingress_bytes_count`: Number of bytes sent from client to VM using proxy. - `loadbalancing.tcp_ssl_proxy.new_connections`: Number of connections that were created over TCP/SSL proxy. - `loadbalancing.tcp_ssl_proxy.open_connections`: Current number of outstanding connections through the TCP/SSL proxy. diff --git a/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/fields.yml b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/fields.yml index 57761c8ea1b..93855f7ee9c 100644 --- a/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/fields.yml +++ b/x-pack/metricbeat/module/googlecloud/loadbalancing/_meta/fields.yml @@ -11,11 +11,11 @@ description: A distribution of the latency calculated from when the request was sent by the proxy to the backend until the proxy received from the backend the last byte of response. type: group fields: - - name: count + - name: count.value type: long - - name: mean + - name: mean.value type: long - - name: bucket_counts + - name: bucket_counts.value type: long - name: bucket_options type: group @@ -26,30 +26,30 @@ - name: ExponentialBuckets type: group fields: - - name: growth_factor + - name: growth_factor.value type: double - - name: scale + - name: scale.value type: long - - name: num_finite_buckets + - name: num_finite_buckets.value type: long - - name: backend_request_bytes_count + - name: backend_request_bytes_count.value type: long description: The number of bytes sent as requests from HTTP/S load balancer to backends. - - name: backend_request_count + - name: backend_request_count.value type: long description: The number of requests served by backends of HTTP/S load balancer. - - name: backend_response_bytes_count + - name: backend_response_bytes_count.value type: long description: The number of bytes sent as responses from backends (or cache) to HTTP/S load balancer. - name: frontend_tcp_rtt description: A distribution of the RTT measured for each connection between client and proxy. type: group fields: - - name: count + - name: count.value type: long - - name: mean + - name: mean.value type: long - - name: bucket_counts + - name: bucket_counts.value type: long - name: bucket_options type: group @@ -60,11 +60,11 @@ - name: ExponentialBuckets type: group fields: - - name: growth_factor + - name: growth_factor.value type: double - - name: scale + - name: scale.value type: long - - name: num_finite_buckets + - name: num_finite_buckets.value type: long - name: internal type: group @@ -73,11 +73,11 @@ description: A distribution of the latency calculated from when the request was sent by the proxy to the backend until the proxy received from the backend the last byte of response. type: group fields: - - name: count + - name: count.value type: long - - name: mean + - name: mean.value type: long - - name: bucket_counts + - name: bucket_counts.value type: long - name: bucket_options type: group @@ -88,30 +88,30 @@ - name: ExponentialBuckets type: group fields: - - name: growth_factor + - name: growth_factor.value type: double - - name: scale + - name: scale.value type: long - - name: num_finite_buckets + - name: num_finite_buckets.value type: long - - name: request_bytes_count + - name: request_bytes_count.value type: long description: The number of bytes sent as requests from clients to HTTP/S load balancer. - - name: request_count + - name: request_count.value type: long description: The number of requests served by HTTP/S load balancer. - - name: response_bytes_count + - name: response_bytes_count.value type: long description: The number of bytes sent as responses from HTTP/S load balancer to clients. - name: total_latencies description: A distribution of the latency calculated from when the request was received by the proxy until the proxy got ACK from client on last response byte. type: group fields: - - name: count + - name: count.value type: long - - name: mean + - name: mean.value type: long - - name: bucket_counts + - name: bucket_counts.value type: long - name: bucket_options type: group @@ -122,30 +122,30 @@ - name: ExponentialBuckets type: group fields: - - name: growth_factor + - name: growth_factor.value type: double - - name: scale + - name: scale.value type: long - - name: num_finite_buckets + - name: num_finite_buckets.value type: long - - name: request_bytes_count + - name: request_bytes_count.value type: long description: The number of bytes sent as requests from clients to HTTP/S load balancer. - - name: request_count + - name: request_count.value type: long description: The number of requests served by HTTP/S load balancer. - - name: response_bytes_count + - name: response_bytes_count.value type: long description: The number of bytes sent as responses from HTTP/S load balancer to clients. - name: total_latencies description: A distribution of the latency calculated from when the request was received by the proxy until the proxy got ACK from client on last response byte. type: group fields: - - name: count + - name: count.value type: long - - name: mean + - name: mean.value type: long - - name: bucket_counts + - name: bucket_counts.value type: long - name: bucket_options type: group @@ -156,37 +156,37 @@ - name: ExponentialBuckets type: group fields: - - name: growth_factor + - name: growth_factor.value type: double - - name: scale + - name: scale.value type: long - - name: num_finite_buckets + - name: num_finite_buckets.value type: long - name: l3.internal type: group description: Google Cloud Load Balancing metrics fields: - - name: egress_bytes_count + - name: egress_bytes_count.value type: long description: The number of bytes sent from ILB backend to client (for TCP flows it's counting bytes on application stream only). - - name: egress_packets_count + - name: egress_packets_count.value type: long description: The number of packets sent from ILB backend to client of the flow. - - name: ingress_bytes_count + - name: ingress_bytes_count.value type: long description: The number of bytes sent from client to ILB backend (for TCP flows it's counting bytes on application stream only). - - name: ingress_packets_count + - name: ingress_packets_count.value type: long description: The number of packets sent from client to ILB backend. - name: rtt_latencies description: A distribution of RTT measured over TCP connections for ILB flows. type: group fields: - - name: count + - name: count.value type: long - - name: mean + - name: mean.value type: long - - name: bucket_counts + - name: bucket_counts.value type: long - name: bucket_options type: group @@ -197,31 +197,31 @@ - name: ExponentialBuckets type: group fields: - - name: growth_factor + - name: growth_factor.value type: double - - name: scale + - name: scale.value type: long - - name: num_finite_buckets + - name: num_finite_buckets.value type: long - name: tcp_ssl_proxy type: group description: Google Cloud Load Balancing metrics fields: - - name: closed_connections + - name: closed_connections.value type: long description: Number of connections that were terminated over TCP/SSL proxy. - - name: egress_bytes_count + - name: egress_bytes_count.value type: long description: Number of bytes sent from VM to client using proxy. - name: frontend_tcp_rtt description: A distribution of the smoothed RTT (in ms) measured by the proxy's TCP stack, each minute application layer bytes pass from proxy to client. type: group fields: - - name: count + - name: count.value type: long - - name: mean + - name: mean.value type: long - - name: bucket_counts + - name: bucket_counts.value type: long - name: bucket_options type: group @@ -232,18 +232,18 @@ - name: ExponentialBuckets type: group fields: - - name: growth_factor + - name: growth_factor.value type: double - - name: scale + - name: scale.value type: long - - name: num_finite_buckets + - name: num_finite_buckets.value type: long - - name: ingress_bytes_count + - name: ingress_bytes_count.value type: long description: Number of bytes sent from client to VM using proxy. - - name: new_connections + - name: new_connections.value type: long description: Number of connections that were created over TCP/SSL proxy. - - name: open_connections + - name: open_connections.value type: long description: Current number of outstanding connections through the TCP/SSL proxy. diff --git a/x-pack/metricbeat/module/googlecloud/loadbalancing/loadbalancing_integration_test.go b/x-pack/metricbeat/module/googlecloud/loadbalancing/loadbalancing_integration_test.go index 80a5e99c57e..c070d96a736 100644 --- a/x-pack/metricbeat/module/googlecloud/loadbalancing/loadbalancing_integration_test.go +++ b/x-pack/metricbeat/module/googlecloud/loadbalancing/loadbalancing_integration_test.go @@ -8,14 +8,38 @@ package loadbalancing import ( + "fmt" "testing" + "github.com/elastic/beats/v7/libbeat/common" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" - "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" + "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud/stackdriver" ) func TestData(t *testing.T) { - config := googlecloud.GetConfigForTest(t, "loadbalancing") - metricSet := mbtest.NewFetcher(t, config) - metricSet.WriteEvents(t, "/") + metricPrefixIs := func(metricPrefix string) func(e common.MapStr) bool { + return func(e common.MapStr) bool { + v, err := e.GetValue(metricPrefix) + return err == nil && v != nil + } + } + + dataFiles := []struct { + metricPrefix string + path string + }{ + {"googlecloud.loadbalancing", "./_meta/data.json"}, + {"googlecloud.loadbalancing.https", "./_meta/data_https.json"}, + {"googlecloud.loadbalancing.l3", "./_meta/data_l3.json"}, + {"googlecloud.loadbalancing.tcp_ssl_proxy", "./_meta/data_tcp_ssl_proxy.json"}, + } + + config := stackdriver.GetConfigForTest(t, "loadbalancing") + + for _, df := range dataFiles { + metricSet := mbtest.NewFetcher(t, config) + t.Run(fmt.Sprintf("metric prefix: %s", df.metricPrefix), func(t *testing.T) { + metricSet.WriteEventsCond(t, df.path, metricPrefixIs(df.metricPrefix)) + }) + } } diff --git a/x-pack/metricbeat/module/googlecloud/loadbalancing/manifest.yml b/x-pack/metricbeat/module/googlecloud/loadbalancing/manifest.yml index 479f92c94d4..5ec1dc8d417 100644 --- a/x-pack/metricbeat/module/googlecloud/loadbalancing/manifest.yml +++ b/x-pack/metricbeat/module/googlecloud/loadbalancing/manifest.yml @@ -6,28 +6,19 @@ input: stackdriver: service: loadbalancing metrics: - - "loadbalancing.googleapis.com/https/backend_latencies" - - "loadbalancing.googleapis.com/https/backend_latencies" - - "loadbalancing.googleapis.com/https/backend_request_bytes_count" - - "loadbalancing.googleapis.com/https/backend_request_count" - - "loadbalancing.googleapis.com/https/backend_response_bytes_count" - - "loadbalancing.googleapis.com/https/frontend_tcp_rtt" - - "loadbalancing.googleapis.com/https/request_bytes_count" - - "loadbalancing.googleapis.com/https/request_bytes_count" - - "loadbalancing.googleapis.com/https/request_count" - - "loadbalancing.googleapis.com/https/request_count" - - "loadbalancing.googleapis.com/https/response_bytes_count" - - "loadbalancing.googleapis.com/https/response_bytes_count" - - "loadbalancing.googleapis.com/https/total_latencies" - - "loadbalancing.googleapis.com/https/total_latencies" - - "loadbalancing.googleapis.com/l3/internal/egress_bytes_count" - - "loadbalancing.googleapis.com/l3/internal/egress_packets_count" - - "loadbalancing.googleapis.com/l3/internal/ingress_bytes_count" - - "loadbalancing.googleapis.com/l3/internal/ingress_packets_count" - - "loadbalancing.googleapis.com/l3/internal/rtt_latencies" - - "loadbalancing.googleapis.com/tcp_ssl_proxy/closed_connections" - - "loadbalancing.googleapis.com/tcp_ssl_proxy/egress_bytes_count" - - "loadbalancing.googleapis.com/tcp_ssl_proxy/frontend_tcp_rtt" - - "loadbalancing.googleapis.com/tcp_ssl_proxy/ingress_bytes_count" - - "loadbalancing.googleapis.com/tcp_ssl_proxy/new_connections" - - "loadbalancing.googleapis.com/tcp_ssl_proxy/open_connections" + - metric_types: + - "loadbalancing.googleapis.com/https/backend_request_bytes_count" + - "loadbalancing.googleapis.com/https/backend_request_count" + - "loadbalancing.googleapis.com/https/backend_response_bytes_count" + - "loadbalancing.googleapis.com/https/request_bytes_count" + - "loadbalancing.googleapis.com/https/request_count" + - "loadbalancing.googleapis.com/https/response_bytes_count" + - "loadbalancing.googleapis.com/l3/internal/egress_bytes_count" + - "loadbalancing.googleapis.com/l3/internal/egress_packets_count" + - "loadbalancing.googleapis.com/l3/internal/ingress_bytes_count" + - "loadbalancing.googleapis.com/l3/internal/ingress_packets_count" + - "loadbalancing.googleapis.com/tcp_ssl_proxy/closed_connections" + - "loadbalancing.googleapis.com/tcp_ssl_proxy/egress_bytes_count" + - "loadbalancing.googleapis.com/tcp_ssl_proxy/ingress_bytes_count" + - "loadbalancing.googleapis.com/tcp_ssl_proxy/new_connections" + - "loadbalancing.googleapis.com/tcp_ssl_proxy/open_connections" diff --git a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data.json b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data.json index 86da8d924e5..c1a0365401d 100644 --- a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data.json +++ b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data.json @@ -14,34 +14,14 @@ "googlecloud": { "labels": { "resource": { - "subscription_id": "test-ks" + "subscription_id": "test-subscription-1" } }, "pubsub": { - "snapshot": { - "backlog_bytes": 19, - "backlog_bytes_by_region": 19, - "num_messages": 4, - "num_messages_by_region": 4, - "oldest_message_age": 69319, - "oldest_message_age_by_region": 69319 - }, "subscription": { - "backlog_bytes": 0, - "num_undelivered_messages": 0, - "oldest_retained_acked_message_age": 0, - "oldest_retained_acked_message_age_by_region": 0, - "oldest_unacked_message_age": 0, - "oldest_unacked_message_age_by_region": 69277, - "retained_acked_bytes": 0, - "retained_acked_bytes_by_region": 0, - "unacked_bytes_by_region": 19 - }, - "topic": { - "oldest_retained_acked_message_age_by_region": 0, - "oldest_unacked_message_age_by_region": 69319, - "retained_acked_bytes_by_region": 0, - "unacked_bytes_by_region": 76 + "backlog_bytes": { + "value": 0 + } } } }, diff --git a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data_subscription.json b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data_subscription.json new file mode 100644 index 00000000000..13c2724143f --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data_subscription.json @@ -0,0 +1,35 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "cloud": { + "account": { + "id": "elastic-observability" + }, + "provider": "googlecloud" + }, + "event": { + "dataset": "googlecloud.pubsub", + "duration": 115000, + "module": "googlecloud" + }, + "googlecloud": { + "labels": { + "resource": { + "subscription_id": "test-ks" + } + }, + "pubsub": { + "subscription": { + "backlog_bytes": { + "value": 0 + } + } + } + }, + "metricset": { + "name": "pubsub", + "period": 10000 + }, + "service": { + "type": "googlecloud" + } +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data_topic.json b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data_topic.json new file mode 100644 index 00000000000..7f296406136 --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/data_topic.json @@ -0,0 +1,43 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "cloud": { + "account": { + "id": "elastic-observability" + }, + "provider": "googlecloud" + }, + "event": { + "dataset": "googlecloud.pubsub", + "duration": 115000, + "module": "googlecloud" + }, + "googlecloud": { + "labels": { + "resource": { + "topic_id": "test-ks" + } + }, + "pubsub": { + "topic": { + "message_sizes": { + "bucket_options": { + "Options": { + "ExponentialBuckets": { + "num_finite_buckets": 16, + "growth_factor": 4, + "scale": 1 + } + } + } + } + } + } + }, + "metricset": { + "name": "pubsub", + "period": 10000 + }, + "service": { + "type": "googlecloud" + } +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/docs.asciidoc b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/docs.asciidoc index cd6e94083ef..f8faf2bada0 100644 --- a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/docs.asciidoc @@ -1,9 +1,13 @@ -PubSub Metricset to fetch metrics from https://cloud.google.com/pubsub/[Pub/Sub] topics and subscriptions in Google Cloud Platform. +PubSub metricsetf to fetch metrics from https://cloud.google.com/pubsub/[Pub/Sub] topics and subscriptions in Google Cloud Platform. -The `pubsub` Metricset contains all GA stage metrics exported from the https://cloud.google.com/monitoring/api/metrics_gcp#gcp-pubsub[Stackdriver API]. The field names have been left untouched for people already familiar with them. +The `pubsub` metricset contains all GA stage metrics exported from the https://cloud.google.com/monitoring/api/metrics_gcp#gcp-pubsub[Stackdriver API]. The field names have been left untouched for people already familiar with them. No special permissions are needed apart from the ones detailed in the module section of the docs. +[float] +=== Metrics +Here is a list of metrics collected by `pubsub` metricset: + [float] ==== Snapshot Metrics - `pubsub.snapshot.backlog_bytes`: Total byte size of the messages retained in a snapshot. @@ -35,7 +39,6 @@ No special permissions are needed apart from the ones detailed in the module sec - `pubsub.subscription.pull_message_operation_count`: Cumulative count of pull message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. - `pubsub.subscription.pull_request_count`: Cumulative count of pull requests, grouped by result. - `pubsub.subscription.push_request_count`: Cumulative count of push attempts, grouped by result. Unlike pulls, the push server implementation does not batch user messages. So each request only contains one user message. The push server retries on errors, so a given user message can appear multiple times. -- `pubsub.subscription.push_request_latencies`: Distribution of push request latencies (in microseconds), grouped by result. - `pubsub.subscription.retained_acked_bytes`: otal byte size of the acknowledged messages retained in a subscription. - `pubsub.subscription.retained_acked_bytes_by_region`: Total byte size of the acknowledged messages retained in a subscription, broken down by Cloud region. - `pubsub.subscription.seek_request_count`: Cumulative count of seek attempts, grouped by result. @@ -52,7 +55,6 @@ No special permissions are needed apart from the ones detailed in the module sec ==== Topic Metrics - `pubsub.topic.byte_cost`: Cost of operations, measured in bytes. This is used to measure utilization for quotas. - `pubsub.topic.config_updates_count`: Cumulative count of configuration changes, grouped by operation type and result. -- `pubsub.topic.message_sizes`: Distribution of publish message sizes (in bytes). - `pubsub.topic.oldest_retained_acked_message_age_by_region`: Age (in seconds) of the oldest acknowledged message retained in a topic, broken down by Cloud region. - `pubsub.topic.oldest_unacked_message_age_by_region`: Age (in seconds) of the oldest unacknowledged message in a topic, broken down by Cloud region. - `pubsub.topic.retained_acked_bytes_by_region`: Total byte size of the acknowledged messages retained in a topic, broken down by Cloud region. diff --git a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/fields.yml b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/fields.yml index 32e4acbb521..ae6443e219f 100644 --- a/x-pack/metricbeat/module/googlecloud/pubsub/_meta/fields.yml +++ b/x-pack/metricbeat/module/googlecloud/pubsub/_meta/fields.yml @@ -7,152 +7,155 @@ type: group description: Suscription related metrics fields: - - name: ack_message_count + - name: ack_message_count.value type: long description: Cumulative count of messages acknowledged by Acknowledge requests, grouped by delivery type. - - name: backlog_bytes + - name: backlog_bytes.value type: long description: Total byte size of the unacknowledged messages (a.k.a. backlog messages) in a subscription. - - name: num_outstanding_messages + - name: num_outstanding_messages.value type: long description: Number of messages delivered to a subscription's push endpoint, but not yet acknowledged. - - name: num_undelivered_messages + - name: num_undelivered_messages.value type: long description: Number of unacknowledged messages (a.k.a. backlog messages) in a subscription. - - name: oldest_unacked_message_age + - name: oldest_unacked_message_age.value type: long description: Age (in seconds) of the oldest unacknowledged message (a.k.a. backlog message) in a subscription. - - name: pull_ack_message_operation_count + - name: pull_ack_message_operation_count.value type: long description: Cumulative count of acknowledge message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. - - name: pull_ack_request_count + - name: pull_ack_request_count.value type: long description: Cumulative count of acknowledge requests, grouped by result. - - name: pull_message_operation_count + - name: pull_message_operation_count.value type: long description: Cumulative count of pull message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. - - name: pull_request_count + - name: pull_request_count.value type: long description: Cumulative count of pull requests, grouped by result. - - name: push_request_count + - name: push_request_count.value type: long description: Cumulative count of push attempts, grouped by result. Unlike pulls, the push server implementation does not batch user messages. So each request only contains one user message. The push server retries on errors, so a given user message can appear multiple times. - - name: push_request_latencies + - name: push_request_latencies.value type: long description: Distribution of push request latencies (in microseconds), grouped by result. - - name: sent_message_count + - name: sent_message_count.value type: long description: Cumulative count of messages sent by Cloud Pub/Sub to subscriber clients, grouped by delivery type. - - name: streaming_pull_ack_message_operation_count + - name: streaming_pull_ack_message_operation_count.value type: long description: Cumulative count of StreamingPull acknowledge message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. - - name: streaming_pull_ack_request_count + - name: streaming_pull_ack_request_count.value type: long description: Cumulative count of streaming pull requests with non-empty acknowledge ids, grouped by result. - - name: streaming_pull_message_operation_count + - name: streaming_pull_message_operation_count.value type: long description: Cumulative count of streaming pull message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count - - name: streaming_pull_response_count + - name: streaming_pull_response_count.value type: long description: Cumulative count of streaming pull responses, grouped by result. - - name: dead_letter_message_count + - name: dead_letter_message_count.value type: long description: Cumulative count of messages published to dead letter topic, grouped by result. - - name: mod_ack_deadline_message_count + - name: mod_ack_deadline_message_count.value type: long description: Cumulative count of messages whose deadline was updated by ModifyAckDeadline requests, grouped by delivery type. - - name: mod_ack_deadline_message_operation_count + - name: mod_ack_deadline_message_operation_count.value type: long description: Cumulative count of ModifyAckDeadline message operations, grouped by result. - - name: mod_ack_deadline_request_count + - name: mod_ack_deadline_request_count.value type: long description: Cumulative count of ModifyAckDeadline requests, grouped by result. - - name: oldest_retained_acked_message_age + - name: oldest_retained_acked_message_age.value type: long description: Age (in seconds) of the oldest acknowledged message retained in a subscription. - - name: oldest_retained_acked_message_age_by_region + - name: oldest_retained_acked_message_age_by_region.value type: long description: Age (in seconds) of the oldest acknowledged message retained in a subscription, broken down by Cloud region. - - name: oldest_unacked_message_age_by_region + - name: oldest_unacked_message_age_by_region.value type: long description: Age (in seconds) of the oldest unacknowledged message in a subscription, broken down by Cloud region. - - name: retained_acked_bytes + - name: retained_acked_bytes.value type: long description: Total byte size of the acknowledged messages retained in a subscription. - - name: retained_acked_bytes_by_region + - name: retained_acked_bytes_by_region.value type: long description: Total byte size of the acknowledged messages retained in a subscription, broken down by Cloud region. - - name: seek_request_count + - name: seek_request_count.value type: long description: Cumulative count of seek attempts, grouped by result. - - name: streaming_pull_mod_ack_deadline_message_operation_count + - name: streaming_pull_mod_ack_deadline_message_operation_count.value type: long description: Cumulative count of StreamingPull ModifyAckDeadline operations, grouped by result. - - name: streaming_pull_mod_ack_deadline_request_count + - name: streaming_pull_mod_ack_deadline_request_count.value type: long description: Cumulative count of streaming pull requests with non-empty ModifyAckDeadline fields, grouped by result. - - name: byte_cost + - name: byte_cost.value type: long description: Cumulative cost of operations, measured in bytes. This is used to measure quota utilization. - - name: config_updates_count + - name: config_updates_count.value type: long description: Cumulative count of configuration changes for each subscription, grouped by operation type and result. - - name: unacked_bytes_by_region + - name: unacked_bytes_by_region.value type: long description: Total byte size of the unacknowledged messages in a subscription, broken down by Cloud region. - name: topic type: group description: Topic related metrics fields: - - name: streaming_pull_response_count + - name: streaming_pull_response_count.value type: long description: Cumulative count of streaming pull responses, grouped by result. - - name: send_message_operation_count + - name: send_message_operation_count.value type: long description: Cumulative count of publish message operations, grouped by result. For a definition of message operations, see Cloud Pub/Sub metric subscription/mod_ack_deadline_message_operation_count. - - name: send_request_count + - name: send_request_count.value type: long description: Cumulative count of publish requests, grouped by result. - - name: oldest_retained_acked_message_age_by_region + - name: oldest_retained_acked_message_age_by_region.value type: long description: Age (in seconds) of the oldest acknowledged message retained in a topic, broken down by Cloud region. - - name: oldest_unacked_message_age_by_region + - name: oldest_unacked_message_age_by_region.value type: long description: Age (in seconds) of the oldest unacknowledged message in a topic, broken down by Cloud region. - - name: retained_acked_bytes_by_region + - name: retained_acked_bytes_by_region.value type: long description: Total byte size of the acknowledged messages retained in a topic, broken down by Cloud region. - - name: byte_cost + - name: byte_cost.value type: long description: Cost of operations, measured in bytes. This is used to measure utilization for quotas. - - name: config_updates_count + - name: config_updates_count.value type: long description: Cumulative count of configuration changes, grouped by operation type and result. - - name: unacked_bytes_by_region + - name: message_sizes.value + type: long + description: Distribution of publish message sizes (in bytes) + - name: unacked_bytes_by_region.value type: long description: Total byte size of the unacknowledged messages in a topic, broken down by Cloud region. - name: snapshot type: group description: Snapshot related metrics fields: - - name: oldest_message_age + - name: oldest_message_age.value type: long description: Age (in seconds) of the oldest message retained in a snapshot. - - name: oldest_message_age_by_region + - name: oldest_message_age_by_region.value type: long description: Age (in seconds) of the oldest message retained in a snapshot, broken down by Cloud region. - - name: backlog_bytes + - name: backlog_bytes.value type: long description: Total byte size of the messages retained in a snapshot. - - name: backlog_bytes_by_region + - name: backlog_bytes_by_region.value type: long description: Total byte size of the messages retained in a snapshot, broken down by Cloud region. - - name: num_messages + - name: num_messages.value type: long description: Number of messages retained in a snapshot. - - name: num_messages_by_region + - name: num_messages_by_region.value type: long description: Number of messages retained in a snapshot, broken down by Cloud region. - - name: config_updates_count + - name: config_updates_count.value type: long description: Cumulative count of configuration changes, grouped by operation type and result. diff --git a/x-pack/metricbeat/module/googlecloud/pubsub/manifest.yml b/x-pack/metricbeat/module/googlecloud/pubsub/manifest.yml index 3d8cdb0949c..a002820ebd6 100644 --- a/x-pack/metricbeat/module/googlecloud/pubsub/manifest.yml +++ b/x-pack/metricbeat/module/googlecloud/pubsub/manifest.yml @@ -6,50 +6,49 @@ input: stackdriver: service: pubsub metrics: - - "pubsub.googleapis.com/snapshot/backlog_bytes" - - "pubsub.googleapis.com/snapshot/backlog_bytes_by_region" - - "pubsub.googleapis.com/snapshot/config_updates_count" - - "pubsub.googleapis.com/snapshot/num_messages" - - "pubsub.googleapis.com/snapshot/num_messages_by_region" - - "pubsub.googleapis.com/snapshot/oldest_message_age" - - "pubsub.googleapis.com/snapshot/oldest_message_age_by_region" - - "pubsub.googleapis.com/subscription/ack_message_count" - - "pubsub.googleapis.com/subscription/backlog_bytes" - - "pubsub.googleapis.com/subscription/byte_cost" - - "pubsub.googleapis.com/subscription/config_updates_count" - - "pubsub.googleapis.com/subscription/dead_letter_message_count" - - "pubsub.googleapis.com/subscription/mod_ack_deadline_message_count" - - "pubsub.googleapis.com/subscription/mod_ack_deadline_message_operation_count" - - "pubsub.googleapis.com/subscription/mod_ack_deadline_request_count" - - "pubsub.googleapis.com/subscription/num_outstanding_messages" - - "pubsub.googleapis.com/subscription/num_undelivered_messages" - - "pubsub.googleapis.com/subscription/oldest_retained_acked_message_age" - - "pubsub.googleapis.com/subscription/oldest_retained_acked_message_age_by_region" - - "pubsub.googleapis.com/subscription/oldest_unacked_message_age" - - "pubsub.googleapis.com/subscription/oldest_unacked_message_age_by_region" - - "pubsub.googleapis.com/subscription/pull_ack_message_operation_count" - - "pubsub.googleapis.com/subscription/pull_ack_request_count" - - "pubsub.googleapis.com/subscription/pull_message_operation_count" - - "pubsub.googleapis.com/subscription/pull_request_count" - - "pubsub.googleapis.com/subscription/push_request_count" - - "pubsub.googleapis.com/subscription/push_request_latencies" - - "pubsub.googleapis.com/subscription/retained_acked_bytes" - - "pubsub.googleapis.com/subscription/retained_acked_bytes_by_region" - - "pubsub.googleapis.com/subscription/seek_request_count" - - "pubsub.googleapis.com/subscription/sent_message_count" - - "pubsub.googleapis.com/subscription/streaming_pull_ack_message_operation_count" - - "pubsub.googleapis.com/subscription/streaming_pull_ack_request_count" - - "pubsub.googleapis.com/subscription/streaming_pull_message_operation_count" - - "pubsub.googleapis.com/subscription/streaming_pull_mod_ack_deadline_message_operation_count" - - "pubsub.googleapis.com/subscription/streaming_pull_mod_ack_deadline_request_count" - - "pubsub.googleapis.com/subscription/streaming_pull_response_count" - - "pubsub.googleapis.com/subscription/unacked_bytes_by_region" - - "pubsub.googleapis.com/topic/byte_cost" - - "pubsub.googleapis.com/topic/config_updates_count" - - "pubsub.googleapis.com/topic/message_sizes" - - "pubsub.googleapis.com/topic/oldest_retained_acked_message_age_by_region" - - "pubsub.googleapis.com/topic/oldest_unacked_message_age_by_region" - - "pubsub.googleapis.com/topic/retained_acked_bytes_by_region" - - "pubsub.googleapis.com/topic/send_message_operation_count" - - "pubsub.googleapis.com/topic/send_request_count" - - "pubsub.googleapis.com/topic/unacked_bytes_by_region" + - metric_types: + - "pubsub.googleapis.com/snapshot/backlog_bytes" + - "pubsub.googleapis.com/snapshot/backlog_bytes_by_region" + - "pubsub.googleapis.com/snapshot/config_updates_count" + - "pubsub.googleapis.com/snapshot/num_messages" + - "pubsub.googleapis.com/snapshot/num_messages_by_region" + - "pubsub.googleapis.com/snapshot/oldest_message_age" + - "pubsub.googleapis.com/snapshot/oldest_message_age_by_region" + - "pubsub.googleapis.com/subscription/ack_message_count" + - "pubsub.googleapis.com/subscription/backlog_bytes" + - "pubsub.googleapis.com/subscription/byte_cost" + - "pubsub.googleapis.com/subscription/config_updates_count" + - "pubsub.googleapis.com/subscription/dead_letter_message_count" + - "pubsub.googleapis.com/subscription/mod_ack_deadline_message_count" + - "pubsub.googleapis.com/subscription/mod_ack_deadline_message_operation_count" + - "pubsub.googleapis.com/subscription/mod_ack_deadline_request_count" + - "pubsub.googleapis.com/subscription/num_outstanding_messages" + - "pubsub.googleapis.com/subscription/num_undelivered_messages" + - "pubsub.googleapis.com/subscription/oldest_retained_acked_message_age" + - "pubsub.googleapis.com/subscription/oldest_retained_acked_message_age_by_region" + - "pubsub.googleapis.com/subscription/oldest_unacked_message_age" + - "pubsub.googleapis.com/subscription/oldest_unacked_message_age_by_region" + - "pubsub.googleapis.com/subscription/pull_ack_message_operation_count" + - "pubsub.googleapis.com/subscription/pull_ack_request_count" + - "pubsub.googleapis.com/subscription/pull_message_operation_count" + - "pubsub.googleapis.com/subscription/pull_request_count" + - "pubsub.googleapis.com/subscription/push_request_count" + - "pubsub.googleapis.com/subscription/retained_acked_bytes" + - "pubsub.googleapis.com/subscription/retained_acked_bytes_by_region" + - "pubsub.googleapis.com/subscription/seek_request_count" + - "pubsub.googleapis.com/subscription/sent_message_count" + - "pubsub.googleapis.com/subscription/streaming_pull_ack_message_operation_count" + - "pubsub.googleapis.com/subscription/streaming_pull_ack_request_count" + - "pubsub.googleapis.com/subscription/streaming_pull_message_operation_count" + - "pubsub.googleapis.com/subscription/streaming_pull_mod_ack_deadline_message_operation_count" + - "pubsub.googleapis.com/subscription/streaming_pull_mod_ack_deadline_request_count" + - "pubsub.googleapis.com/subscription/streaming_pull_response_count" + - "pubsub.googleapis.com/subscription/unacked_bytes_by_region" + - "pubsub.googleapis.com/topic/byte_cost" + - "pubsub.googleapis.com/topic/config_updates_count" + - "pubsub.googleapis.com/topic/oldest_retained_acked_message_age_by_region" + - "pubsub.googleapis.com/topic/oldest_unacked_message_age_by_region" + - "pubsub.googleapis.com/topic/retained_acked_bytes_by_region" + - "pubsub.googleapis.com/topic/send_message_operation_count" + - "pubsub.googleapis.com/topic/send_request_count" + - "pubsub.googleapis.com/topic/unacked_bytes_by_region" diff --git a/x-pack/metricbeat/module/googlecloud/pubsub/pubsub_integration_test.go b/x-pack/metricbeat/module/googlecloud/pubsub/pubsub_integration_test.go index 90032d50310..6d739326dea 100644 --- a/x-pack/metricbeat/module/googlecloud/pubsub/pubsub_integration_test.go +++ b/x-pack/metricbeat/module/googlecloud/pubsub/pubsub_integration_test.go @@ -8,14 +8,38 @@ package pubsub import ( + "fmt" "testing" + "github.com/elastic/beats/v7/libbeat/common" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" - "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" + "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud/stackdriver" ) func TestData(t *testing.T) { - config := googlecloud.GetConfigForTest(t, "pubsub") - metricSet := mbtest.NewFetcher(t, config) - metricSet.WriteEvents(t, "/") + metricPrefixIs := func(metricPrefix string) func(e common.MapStr) bool { + return func(e common.MapStr) bool { + v, err := e.GetValue(metricPrefix) + return err == nil && v != nil + } + } + + dataFiles := []struct { + metricPrefix string + path string + }{ + {"googlecloud.pubsub", "./_meta/data.json"}, + {"googlecloud.pubsub.snapshot", "./_meta/data_snapshot.json"}, + {"googlecloud.pubsub.subscription", "./_meta/data_subscription.json"}, + {"googlecloud.pubsub.topic", "./_meta/data_topic.json"}, + } + + config := stackdriver.GetConfigForTest(t, "pubsub") + + for _, df := range dataFiles { + metricSet := mbtest.NewFetcher(t, config) + t.Run(fmt.Sprintf("metric prefix: %s", df.metricPrefix), func(t *testing.T) { + metricSet.WriteEventsCond(t, df.path, metricPrefixIs(df.metricPrefix)) + }) + } } diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/_meta/docs.asciidoc b/x-pack/metricbeat/module/googlecloud/stackdriver/_meta/docs.asciidoc new file mode 100644 index 00000000000..00cce58cac8 --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/_meta/docs.asciidoc @@ -0,0 +1,82 @@ +Stackdriver provides visibility into the performance, uptime, and overall health +of cloud-powered applications. It collects metrics, events, and metadata from +different services from Google Cloud. This metricset is to collect monitoring +metrics from Google Cloud using `ListTimeSeries` API. + +[float] +== Metricset config and parameters + +* *metric_types*: Required, a list of metric type strings. Each call of the +`ListTimeSeries` API can return any number of time series from a single metric +type. Metric type is to used for identifying a specific time series. + +* *aligner*: A single string with which aggregation operation need to be applied +onto time series data for ListTimeSeries API. If it's not given, default aligner +is set to be `ALIGN_NONE`. Sample period of each metric type is obtained from +making https://cloud.google.com/monitoring/api/ref_v3/rest/v3/projects.metricDescriptors/list [ListMetricDescriptors API] call. + +[float] +=== Example Configuration +* `stackdriver` metricset is enabled to collect metrics from all zones under +`europe-west1-c` region in `elastic-observability` project. Two sets of metrics +are specified: first one is to collect CPU usage time and utilization with +aggregation aligner ALIGN_MEAN; second one is to collect uptime with aggregation +aligner ALIGN_SUM. These metric types all have 240 seconds ingest delay time and +60 seconds sample period. With `period` specified as `300s` in the config below, +Metricbeat will collect compute metrics from googlecloud every 5-minute with +given aggregation aligner applied for each metric type. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - stackdriver + zone: "europe-west1-c" + project_id: elastic-observability + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 300s + stackdriver: + service: compute + metrics: + - aligner: ALIGN_MEAN + metric_types: + - "compute.googleapis.com/instance/cpu/usage_time" + - "compute.googleapis.com/instance/cpu/utilization" + - aligner: ALIGN_SUM + metric_types: + - "compute.googleapis.com/instance/uptime" + +---- + +* `stackdriver` metricset is enabled to collect metrics from all zones under +`europe-west1-c` region in `elastic-observability` project. Two sets of metrics +are specified: first one is to collect CPU usage time and utilization with +aggregation aligner ALIGN_MEAN; second one is to collect uptime with aggregation +aligner ALIGN_SUM. These metric types all have 240 seconds ingest delay time and +60 seconds sample period. With `period` specified as `60s` in the config below, +Metricbeat will collect compute metrics from googlecloud every minute with no +aggregation. This case, the aligners specified in the configuration will be +ignored. ++ +[source,yaml] +---- +- module: googlecloud + metricsets: + - stackdriver + zone: "europe-west1-c" + project_id: elastic-observability + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s + stackdriver: + service: compute + metrics: + - aligner: ALIGN_MEAN + metric_types: + - "compute.googleapis.com/instance/cpu/usage_time" + - "compute.googleapis.com/instance/cpu/utilization" + - aligner: ALIGN_SUM + metric_types: + - "compute.googleapis.com/instance/uptime" +---- diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/compute/identity.go b/x-pack/metricbeat/module/googlecloud/stackdriver/compute/identity.go index 9367862e6c7..19e434e8df7 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/compute/identity.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/compute/identity.go @@ -23,7 +23,7 @@ func (s *metadataCollector) ID(ctx context.Context, in *googlecloud.MetadataColl if in.Timestamp != nil { metadata.ECS.Put("timestamp", in.Timestamp) } else if in.Point != nil { - metadata.ECS.Put("timestamp", in.Point.Interval.StartTime) + metadata.ECS.Put("timestamp", in.Point.Interval.EndTime) } else { return "", errors.New("no timestamp information found") } diff --git a/x-pack/metricbeat/module/googlecloud/integration.go b/x-pack/metricbeat/module/googlecloud/stackdriver/integration.go similarity index 86% rename from x-pack/metricbeat/module/googlecloud/integration.go rename to x-pack/metricbeat/module/googlecloud/stackdriver/integration.go index 0e7ac055bc7..68e3750d5a5 100644 --- a/x-pack/metricbeat/module/googlecloud/integration.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/integration.go @@ -2,7 +2,7 @@ // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. -package googlecloud +package stackdriver import ( "os" @@ -36,7 +36,11 @@ func GetConfigForTest(t *testing.T, metricSetName string) map[string]interface{} if metricSetName == "stackdriver" { config["stackdriver.service"] = "compute" - config["stackdriver.metrics"] = []string{"compute.googleapis.com/instance/uptime"} + stackDriverConfig := stackDriverConfig{ + Aligner: "ALIGN_NONE", + MetricTypes: []string{"compute.googleapis.com/instance/uptime"}, + } + config["stackdriver.metrics"] = stackDriverConfig } } return config diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester.go b/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester.go index 8d2147d285c..11b04e5a1b2 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester.go @@ -8,12 +8,14 @@ import ( "context" "fmt" "regexp" + "strings" "sync" "time" + "github.com/golang/protobuf/ptypes/duration" + monitoring "cloud.google.com/go/monitoring/apiv3" "github.com/golang/protobuf/ptypes/timestamp" - "github.com/pkg/errors" "google.golang.org/api/iterator" monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" @@ -21,42 +23,31 @@ import ( "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" ) -func newStackdriverMetricsRequester(ctx context.Context, c config, window time.Duration, logger *logp.Logger) (*stackdriverMetricsRequester, error) { - interval, err := getTimeInterval(window) - if err != nil { - return nil, errors.Wrap(err, "error trying to get time window") - } - - client, err := monitoring.NewMetricClient(ctx, c.opt...) - if err != nil { - return nil, errors.Wrap(err, "error creating Stackdriver client") - } - - return &stackdriverMetricsRequester{ - config: c, - client: client, - logger: logger, - interval: interval, - }, nil -} - type stackdriverMetricsRequester struct { config config - client *monitoring.MetricClient - interval *monitoringpb.TimeInterval + client *monitoring.MetricClient logger *logp.Logger } -func (r *stackdriverMetricsRequester) Metric(ctx context.Context, m string) (out []*monitoringpb.TimeSeries) { - out = make([]*monitoringpb.TimeSeries, 0) +type timeSeriesWithAligner struct { + timeSeries []*monitoringpb.TimeSeries + aligner string +} + +func (r *stackdriverMetricsRequester) Metric(ctx context.Context, metricType string, timeInterval *monitoringpb.TimeInterval, aligner string) (out timeSeriesWithAligner) { + timeSeries := make([]*monitoringpb.TimeSeries, 0) req := &monitoringpb.ListTimeSeriesRequest{ Name: "projects/" + r.config.ProjectID, - Interval: r.interval, + Interval: timeInterval, View: monitoringpb.ListTimeSeriesRequest_FULL, - Filter: r.getFilterForMetric(m), + Filter: r.getFilterForMetric(metricType), + Aggregation: &monitoringpb.Aggregation{ + PerSeriesAligner: googlecloud.AlignersMapToGCP[aligner], + AlignmentPeriod: &r.config.period, + }, } it := r.client.ListTimeSeries(ctx, req) @@ -67,45 +58,41 @@ func (r *stackdriverMetricsRequester) Metric(ctx context.Context, m string) (out } if err != nil { - r.logger.Errorf("Could not read time series value: %s: %v", m, err) + r.logger.Errorf("Could not read time series value: %s: %v", metricType, err) break } - out = append(out, resp) + timeSeries = append(timeSeries, resp) } + out.aligner = aligner + out.timeSeries = timeSeries return } -func constructFilter(m string, region string, zone string) string { - filter := fmt.Sprintf(`metric.type="%s" AND resource.labels.zone = `, m) - // If region is specified, use region as filter resource label. - // If region is empty but zone is given, use zone instead. - if region != "" { - filter += fmt.Sprintf(`starts_with("%s")`, region) - } else if zone != "" { - filter += fmt.Sprintf(`"%s"`, zone) - } - return filter -} - -func (r *stackdriverMetricsRequester) Metrics(ctx context.Context, ms []string) ([]*monitoringpb.TimeSeries, error) { +func (r *stackdriverMetricsRequester) Metrics(ctx context.Context, stackDriverConfigs []stackDriverConfig, metricsMeta map[string]metricMeta) ([]timeSeriesWithAligner, error) { var lock sync.Mutex var wg sync.WaitGroup - results := make([]*monitoringpb.TimeSeries, 0) + results := make([]timeSeriesWithAligner, 0) - for _, metric := range ms { - wg.Add(1) + for _, sdc := range stackDriverConfigs { + aligner := sdc.Aligner + for _, mt := range sdc.MetricTypes { + metricType := mt + wg.Add(1) - go func(m string) { - defer wg.Done() + go func(metricType string) { + defer wg.Done() - ts := r.Metric(ctx, m) + metricMeta := metricsMeta[metricType] + interval, aligner := getTimeIntervalAligner(metricMeta.ingestDelay, metricMeta.samplePeriod, r.config.period, aligner) + ts := r.Metric(ctx, metricType, interval, aligner) - lock.Lock() - defer lock.Unlock() - results = append(results, ts...) - }(metric) + lock.Lock() + defer lock.Unlock() + results = append(results, ts) + }(metricType) + } } wg.Wait() @@ -136,29 +123,44 @@ func (r *stackdriverMetricsRequester) getFilterForMetric(m string) (f string) { "both are provided, only use region", r.config.Region, r.config.Zone) } if r.config.Region != "" { - f = fmt.Sprintf(`%s AND resource.labels.zone = starts_with("%s")`, f, r.config.Region) + region := r.config.Region + if strings.HasSuffix(r.config.Region, "*") { + region = strings.TrimSuffix(r.config.Region, "*") + } + f = fmt.Sprintf(`%s AND resource.labels.zone = starts_with("%s")`, f, region) } else if r.config.Zone != "" { - f = fmt.Sprintf(`%s AND resource.labels.zone = "%s"`, f, r.config.Zone) + zone := r.config.Zone + if strings.HasSuffix(r.config.Zone, "*") { + zone = strings.TrimSuffix(r.config.Zone, "*") + } + f = fmt.Sprintf(`%s AND resource.labels.zone = starts_with("%s")`, f, zone) } } return } -// Returns a GCP TimeInterval based on the provided config -func getTimeInterval(windowTime time.Duration) (*monitoringpb.TimeInterval, error) { - var startTime, endTime time.Time - - if windowTime > 0 { - endTime = time.Now().UTC() - startTime = time.Now().UTC().Add(-windowTime) +// Returns a GCP TimeInterval based on the ingestDelay and samplePeriod from ListMetricDescriptor +func getTimeIntervalAligner(ingestDelay time.Duration, samplePeriod time.Duration, collectionPeriod duration.Duration, inputAligner string) (*monitoringpb.TimeInterval, string) { + var startTime, endTime, currentTime time.Time + var needsAggregation bool + currentTime = time.Now().UTC() + + // When samplePeriod < collectionPeriod, aggregation will be done in ListTimeSeriesRequest. + // For example, samplePeriod = 60s, collectionPeriod = 300s, if perSeriesAligner is not given, + // ALIGN_MEAN will be used by default. + if int64(samplePeriod.Seconds()) < collectionPeriod.Seconds { + endTime = currentTime.Add(-ingestDelay) + startTime = endTime.Add(-time.Duration(collectionPeriod.Seconds) * time.Second) + needsAggregation = true } - if windowTime.Minutes() < googlecloud.MinTimeIntervalDataWindowMinutes { - return nil, errors.Errorf("the provided window time is too small. No less than %d minutes can be fetched", googlecloud.MinTimeIntervalDataWindowMinutes) - } - - if windowTime.Minutes() >= googlecloud.MaxTimeIntervalDataWindowMinutes { - return nil, errors.Errorf("the provided window time is too big. No more than %d minutes can be fetched", googlecloud.MaxTimeIntervalDataWindowMinutes) + // When samplePeriod == collectionPeriod, aggregation is not needed + // When samplePeriod > collectionPeriod, aggregation is not needed, use sample period + // to determine startTime and endTime to make sure there will be data point in this time range. + if int64(samplePeriod.Seconds()) >= collectionPeriod.Seconds { + endTime = time.Now().UTC().Add(-ingestDelay) + startTime = endTime.Add(-samplePeriod) + needsAggregation = false } interval := &monitoringpb.TimeInterval{ @@ -170,5 +172,11 @@ func getTimeInterval(windowTime time.Duration) (*monitoringpb.TimeInterval, erro }, } - return interval, nil + // Default aligner for aggregation is ALIGN_NONE if it's not given + updatedAligner := googlecloud.DefaultAligner + if needsAggregation && inputAligner != "" { + updatedAligner = inputAligner + } + + return interval, updatedAligner } diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester_test.go b/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester_test.go index be7b824d224..f7aff666c0f 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester_test.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/metrics_requester_test.go @@ -6,44 +6,14 @@ package stackdriver import ( "testing" + "time" + "github.com/golang/protobuf/ptypes/duration" "github.com/stretchr/testify/assert" "github.com/elastic/beats/v7/libbeat/logp" ) -func TestStringInSlice(t *testing.T) { - cases := []struct { - title string - m string - region string - zone string - expectedFilter string - }{ - { - "construct filter with zone", - "compute.googleapis.com/instance/cpu/utilization", - "", - "us-east1-b", - "metric.type=\"compute.googleapis.com/instance/cpu/utilization\" AND resource.labels.zone = \"us-east1-b\"", - }, - { - "construct filter with region", - "compute.googleapis.com/instance/cpu/utilization", - "us-east1", - "", - "metric.type=\"compute.googleapis.com/instance/cpu/utilization\" AND resource.labels.zone = starts_with(\"us-east1\")", - }, - } - - for _, c := range cases { - t.Run(c.title, func(t *testing.T) { - filter := constructFilter(c.m, c.region, c.zone) - assert.Equal(t, c.expectedFilter, filter) - }) - } -} - func TestGetFilterForMetric(t *testing.T) { var logger = logp.NewLogger("test") cases := []struct { @@ -56,7 +26,7 @@ func TestGetFilterForMetric(t *testing.T) { "compute service with zone in config", "compute.googleapis.com/firewall/dropped_bytes_count", stackdriverMetricsRequester{config: config{Zone: "us-central1-a"}}, - "metric.type=\"compute.googleapis.com/firewall/dropped_bytes_count\" AND resource.labels.zone = \"us-central1-a\"", + "metric.type=\"compute.googleapis.com/firewall/dropped_bytes_count\" AND resource.labels.zone = starts_with(\"us-central1-a\")", }, { "pubsub service with zone in config", @@ -94,6 +64,30 @@ func TestGetFilterForMetric(t *testing.T) { stackdriverMetricsRequester{config: config{Region: "us-central1", Zone: "us-central1-a"}, logger: logger}, "metric.type=\"compute.googleapis.com/firewall/dropped_bytes_count\" AND resource.labels.zone = starts_with(\"us-central1\")", }, + { + "compute uptime with partial region", + "compute.googleapis.com/instance/uptime", + stackdriverMetricsRequester{config: config{Region: "us-west"}, logger: logger}, + "metric.type=\"compute.googleapis.com/instance/uptime\" AND resource.labels.zone = starts_with(\"us-west\")", + }, + { + "compute uptime with partial zone", + "compute.googleapis.com/instance/uptime", + stackdriverMetricsRequester{config: config{Zone: "us-west1-"}, logger: logger}, + "metric.type=\"compute.googleapis.com/instance/uptime\" AND resource.labels.zone = starts_with(\"us-west1-\")", + }, + { + "compute uptime with wildcard in region", + "compute.googleapis.com/instance/uptime", + stackdriverMetricsRequester{config: config{Region: "us-*"}, logger: logger}, + "metric.type=\"compute.googleapis.com/instance/uptime\" AND resource.labels.zone = starts_with(\"us-\")", + }, + { + "compute uptime with wildcard in zone", + "compute.googleapis.com/instance/uptime", + stackdriverMetricsRequester{config: config{Zone: "us-west1-*"}, logger: logger}, + "metric.type=\"compute.googleapis.com/instance/uptime\" AND resource.labels.zone = starts_with(\"us-west1-\")", + }, } for _, c := range cases { @@ -103,3 +97,62 @@ func TestGetFilterForMetric(t *testing.T) { }) } } + +func TestGetTimeIntervalAligner(t *testing.T) { + cases := []struct { + title string + ingestDelay time.Duration + samplePeriod time.Duration + collectionPeriod duration.Duration + inputAligner string + expectedAligner string + }{ + { + "test collectionPeriod equals to samplePeriod", + time.Duration(240) * time.Second, + time.Duration(60) * time.Second, + duration.Duration{ + Seconds: int64(60), + }, + "", + "ALIGN_NONE", + }, + { + "test collectionPeriod larger than samplePeriod", + time.Duration(240) * time.Second, + time.Duration(60) * time.Second, + duration.Duration{ + Seconds: int64(300), + }, + "ALIGN_MEAN", + "ALIGN_MEAN", + }, + { + "test collectionPeriod smaller than samplePeriod", + time.Duration(240) * time.Second, + time.Duration(60) * time.Second, + duration.Duration{ + Seconds: int64(30), + }, + "ALIGN_MAX", + "ALIGN_NONE", + }, + { + "test collectionPeriod equals to samplePeriod with given aligner", + time.Duration(240) * time.Second, + time.Duration(60) * time.Second, + duration.Duration{ + Seconds: int64(60), + }, + "ALIGN_MEAN", + "ALIGN_NONE", + }, + } + + for _, c := range cases { + t.Run(c.title, func(t *testing.T) { + _, aligner := getTimeIntervalAligner(c.ingestDelay, c.samplePeriod, c.collectionPeriod, c.inputAligner) + assert.Equal(t, c.expectedAligner, aligner) + }) + } +} diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/metricset.go b/x-pack/metricbeat/module/googlecloud/stackdriver/metricset.go index 69ac38ca101..81fa98751aa 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/metricset.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/metricset.go @@ -6,8 +6,13 @@ package stackdriver import ( "context" + "fmt" "time" + "github.com/golang/protobuf/ptypes/duration" + + monitoring "cloud.google.com/go/monitoring/apiv3" + "github.com/pkg/errors" "google.golang.org/api/option" @@ -38,19 +43,33 @@ func init() { // interface methods except for Fetch. type MetricSet struct { mb.BaseMetricSet - config config + config config + metricsMeta map[string]metricMeta + requester *stackdriverMetricsRequester + stackDriverConfig []stackDriverConfig `config:"metrics" validate:"nonzero,required"` +} + +//stackDriverConfig holds a configuration specific for stackdriver metricset. +type stackDriverConfig struct { + MetricTypes []string `config:"metric_types" validate:"required"` + Aligner string `config:"aligner"` +} + +type metricMeta struct { + samplePeriod time.Duration + ingestDelay time.Duration } type config struct { - Metrics []string `config:"stackdriver.metrics" validate:"required"` - Zone string `config:"zone"` - Region string `config:"region"` - ProjectID string `config:"project_id" validate:"required"` - ExcludeLabels bool `config:"exclude_labels"` - ServiceName string `config:"stackdriver.service" validate:"required"` - CredentialsFilePath string `config:"credentials_file_path"` - - opt []option.ClientOption + Zone string `config:"zone"` + Region string `config:"region"` + ProjectID string `config:"project_id" validate:"required"` + ExcludeLabels bool `config:"exclude_labels"` + ServiceName string `config:"stackdriver.service" validate:"required"` + CredentialsFilePath string `config:"credentials_file_path"` + + opt []option.ClientOption + period duration.Duration } // New creates a new instance of the MetricSet. New is responsible for unpacking @@ -64,12 +83,39 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return nil, err } + stackDriverConfigs := struct { + StackDriverMetrics []stackDriverConfig `config:"stackdriver.metrics" validate:"nonzero,required"` + }{} + + if err := base.Module().UnpackConfig(&stackDriverConfigs); err != nil { + return nil, err + } + + m.stackDriverConfig = stackDriverConfigs.StackDriverMetrics m.config.opt = []option.ClientOption{option.WithCredentialsFile(m.config.CredentialsFilePath)} + m.config.period.Seconds = int64(m.Module().Config().Period.Seconds()) if err := validatePeriodForGCP(m.Module().Config().Period); err != nil { return nil, err } + // Get ingest delay and sample period for each metric type + ctx := context.Background() + client, err := monitoring.NewMetricClient(ctx, m.config.opt...) + if err != nil { + return nil, errors.Wrap(err, "error creating Stackdriver client") + } + + m.metricsMeta, err = metricDescriptor(ctx, client, m.config.ProjectID, m.stackDriverConfig) + if err != nil { + return nil, errors.Wrap(err, "error calling metricDescriptor function") + } + + m.requester = &stackdriverMetricsRequester{ + config: m.config, + client: client, + logger: m.Logger(), + } return m, nil } @@ -77,12 +123,7 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // format. It publishes the event which is then forwarded to the output. In case // of an error set the Error field of mb.Event or simply call report.Error(). func (m *MetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) (err error) { - reqs, err := newStackdriverMetricsRequester(ctx, m.config, m.Module().Config().Period, m.Logger()) - if err != nil { - return errors.Wrapf(err, "error trying to do create a request client to GCP project '%s' in zone '%s' or region '%s'", m.config.ProjectID, m.config.Zone, m.config.Region) - } - - responses, err := reqs.Metrics(ctx, m.config.Metrics) + responses, err := m.requester.Metrics(ctx, m.stackDriverConfig, m.metricsMeta) if err != nil { return errors.Wrapf(err, "error trying to get metrics for project '%s' and zone '%s' or region '%s'", m.config.ProjectID, m.config.Zone, m.config.Region) } @@ -99,7 +140,7 @@ func (m *MetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) (err erro return nil } -func (m *MetricSet) eventMapping(ctx context.Context, tss []*monitoringpb.TimeSeries) ([]mb.Event, error) { +func (m *MetricSet) eventMapping(ctx context.Context, tss []timeSeriesWithAligner) ([]mb.Event, error) { e := newIncomingFieldExtractor(m.Logger()) var gcpService = googlecloud.NewStackdriverMetadataServiceForTimeSeries(nil) @@ -140,13 +181,14 @@ func (m *MetricSet) eventMapping(ctx context.Context, tss []*monitoringpb.TimeSe // validatePeriodForGCP returns nil if the Period in the module config is in the accepted threshold func validatePeriodForGCP(d time.Duration) (err error) { - if d.Seconds() < 300 { - return errors.New("period in Google Cloud config file cannot be set to less than 300 seconds") + if d.Seconds() < googlecloud.MonitoringMetricsSamplingRate { + return errors.Errorf("period in Google Cloud config file cannot be set to less than %d seconds", googlecloud.MonitoringMetricsSamplingRate) } return nil } +// Validate googlecloud module config func (c *config) Validate() error { // storage metricset does not require region or zone config parameter. if c.ServiceName == "storage" { @@ -158,3 +200,46 @@ func (c *config) Validate() error { } return nil } + +// Validate stackdriver related config +func (mc *stackDriverConfig) Validate() error { + gcpAlignerNames := make([]string, 0) + for k := range googlecloud.AlignersMapToGCP { + gcpAlignerNames = append(gcpAlignerNames, k) + } + + if mc.Aligner != "" { + if _, ok := googlecloud.AlignersMapToGCP[mc.Aligner]; !ok { + return errors.Errorf("the given aligner is not supported, please specify one of %s as aligner", gcpAlignerNames) + } + } + return nil +} + +// metricDescriptor calls ListMetricDescriptorsRequest API to get metric metadata +// (sample period and ingest delay) of each given metric type +func metricDescriptor(ctx context.Context, client *monitoring.MetricClient, projectID string, stackDriverConfigs []stackDriverConfig) (map[string]metricMeta, error) { + metricsWithMeta := make(map[string]metricMeta, 0) + + for _, sdc := range stackDriverConfigs { + for _, mt := range sdc.MetricTypes { + req := &monitoringpb.ListMetricDescriptorsRequest{ + Name: "projects/" + projectID, + Filter: fmt.Sprintf(`metric.type = "%s"`, mt), + } + + it := client.ListMetricDescriptors(ctx, req) + out, err := it.Next() + if err != nil { + return metricsWithMeta, errors.Errorf("Could not make ListMetricDescriptors request: %s: %v", mt, err) + } + + metricsWithMeta[mt] = metricMeta{ + samplePeriod: time.Duration(out.Metadata.SamplePeriod.Seconds) * time.Second, + ingestDelay: time.Duration(out.Metadata.IngestDelay.Seconds) * time.Second, + } + } + } + + return metricsWithMeta, nil +} diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser.go b/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser.go index 474f04a244b..b6e38f4d333 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser.go @@ -9,9 +9,10 @@ import ( "strings" "time" - "github.com/pkg/errors" + "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" "github.com/golang/protobuf/ptypes" + "github.com/pkg/errors" "google.golang.org/genproto/googleapis/monitoring/v3" "github.com/elastic/beats/v7/libbeat/common" @@ -36,7 +37,7 @@ type KeyValuePoint struct { } // extractTimeSeriesMetricValues valuable to send to Elasticsearch. This includes, for example, metric values, labels and timestamps -func (e *incomingFieldExtractor) extractTimeSeriesMetricValues(resp *monitoring.TimeSeries) (points []KeyValuePoint, err error) { +func (e *incomingFieldExtractor) extractTimeSeriesMetricValues(resp *monitoring.TimeSeries, aligner string) (points []KeyValuePoint, err error) { points = make([]KeyValuePoint, 0) for _, point := range resp.Points { @@ -48,7 +49,7 @@ func (e *incomingFieldExtractor) extractTimeSeriesMetricValues(resp *monitoring. } p := KeyValuePoint{ - Key: cleanMetricNameString(resp.Metric.Type), + Key: cleanMetricNameString(resp.Metric.Type, aligner), Value: getValueFromPoint(point), Timestamp: ts, } @@ -62,8 +63,8 @@ func (e *incomingFieldExtractor) extractTimeSeriesMetricValues(resp *monitoring. func (e *incomingFieldExtractor) getTimestamp(p *monitoring.Point) (ts time.Time, err error) { // Don't add point intervals that can't be "stated" at some timestamp. if p.Interval != nil { - if ts, err = ptypes.Timestamp(p.Interval.StartTime); err != nil { - return time.Time{}, errors.Errorf("error trying to parse timestamp '%#v' from metric\n", p.Interval.StartTime) + if ts, err = ptypes.Timestamp(p.Interval.EndTime); err != nil { + return time.Time{}, errors.Errorf("error trying to parse timestamp '%#v' from metric\n", p.Interval.EndTime) } return ts, nil } @@ -73,7 +74,7 @@ func (e *incomingFieldExtractor) getTimestamp(p *monitoring.Point) (ts time.Time var rx = regexp.MustCompile(`^[a-z_-]+\.googleapis.com\/`) -func cleanMetricNameString(s string) string { +func cleanMetricNameString(s string, aligner string) string { if s == "" { return "unknown" } @@ -83,7 +84,8 @@ func cleanMetricNameString(s string) string { removedPrefix := strings.TrimPrefix(s, prefix) replacedChars := strings.Replace(removedPrefix, "/", ".", -1) - return replacedChars + metricName := replacedChars + googlecloud.AlignersMapToSuffix[aligner] + return metricName } func getValueFromPoint(p *monitoring.Point) (out interface{}) { diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser_test.go b/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser_test.go index 4f055ffc0cc..84f689d042b 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser_test.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/response_parser_test.go @@ -5,7 +5,10 @@ package stackdriver import ( + "testing" + "github.com/golang/protobuf/ptypes/timestamp" + "github.com/stretchr/testify/assert" "google.golang.org/genproto/googleapis/api/metric" "google.golang.org/genproto/googleapis/api/monitoredres" "google.golang.org/genproto/googleapis/monitoring/v3" @@ -65,3 +68,32 @@ var metrics = []string{ "compute.googleapis.com/instance/disk/read_bytes_count", "compute.googleapis.com/http/server/response_latencies", } + +func TestCleanMetricNameString(t *testing.T) { + cases := []struct { + title string + metricType string + aligner string + expectedMetricName string + }{ + { + "test construct metric name with ALIGN_MEAN aligner", + "compute.googleapis.com/instance/cpu/usage_time", + "ALIGN_MEAN", + "instance.cpu.usage_time.avg", + }, + { + "test construct metric name with ALIGN_NONE aligner", + "compute.googleapis.com/instance/cpu/utilization", + "ALIGN_NONE", + "instance.cpu.utilization.value", + }, + } + + for _, c := range cases { + t.Run(c.title, func(t *testing.T) { + metricName := cleanMetricNameString(c.metricType, c.aligner) + assert.Equal(t, c.expectedMetricName, metricName) + }) + } +} diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/stackdriver_integration_test.go b/x-pack/metricbeat/module/googlecloud/stackdriver/stackdriver_integration_test.go index 8fe568ebe95..fd11a50c6e9 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/stackdriver_integration_test.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/stackdriver_integration_test.go @@ -11,11 +11,10 @@ import ( "testing" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" - "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" ) func TestData(t *testing.T) { - config := googlecloud.GetConfigForTest(t, "stackdriver") + config := GetConfigForTest(t, "stackdriver") metricSet := mbtest.NewFetcher(t, config) metricSet.WriteEvents(t, "/") } diff --git a/x-pack/metricbeat/module/googlecloud/stackdriver/timeseries.go b/x-pack/metricbeat/module/googlecloud/stackdriver/timeseries.go index fcf3184717c..c0b456f9954 100644 --- a/x-pack/metricbeat/module/googlecloud/stackdriver/timeseries.go +++ b/x-pack/metricbeat/module/googlecloud/stackdriver/timeseries.go @@ -7,53 +7,54 @@ package stackdriver import ( "context" - monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" - "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" ) //timeSeriesGrouped groups TimeSeries responses into common Elasticsearch friendly events. This is to avoid sending // events with a single metric that shares info (like timestamp) with another event with a single metric too -func (m *MetricSet) timeSeriesGrouped(ctx context.Context, gcpService googlecloud.MetadataService, tss []*monitoringpb.TimeSeries, e *incomingFieldExtractor) (map[string][]KeyValuePoint, error) { +func (m *MetricSet) timeSeriesGrouped(ctx context.Context, gcpService googlecloud.MetadataService, tsas []timeSeriesWithAligner, e *incomingFieldExtractor) (map[string][]KeyValuePoint, error) { eventGroups := make(map[string][]KeyValuePoint) metadataService := gcpService - for _, ts := range tss { - keyValues, err := e.extractTimeSeriesMetricValues(ts) - if err != nil { - return nil, err - } - - sdCollectorInputData := googlecloud.NewStackdriverCollectorInputData(ts, m.config.ProjectID, m.config.Zone, m.config.Region) - if gcpService == nil { - metadataService = googlecloud.NewStackdriverMetadataServiceForTimeSeries(ts) - } - - for i := range keyValues { - sdCollectorInputData.Timestamp = &keyValues[i].Timestamp - - id, err := metadataService.ID(ctx, sdCollectorInputData) + for _, tsa := range tsas { + aligner := tsa.aligner + for _, ts := range tsa.timeSeries { + keyValues, err := e.extractTimeSeriesMetricValues(ts, aligner) if err != nil { - m.Logger().Errorf("error trying to retrieve ID from metric event '%v'", err) - continue + return nil, err } - metadataCollectorData, err := metadataService.Metadata(ctx, sdCollectorInputData.TimeSeries) - if err != nil { - m.Logger().Error("error trying to retrieve labels from metric event") - continue + sdCollectorInputData := googlecloud.NewStackdriverCollectorInputData(ts, m.config.ProjectID, m.config.Zone, m.config.Region) + if gcpService == nil { + metadataService = googlecloud.NewStackdriverMetadataServiceForTimeSeries(ts) } - if _, ok := eventGroups[id]; !ok { - eventGroups[id] = make([]KeyValuePoint, 0) - } + for i := range keyValues { + sdCollectorInputData.Timestamp = &keyValues[i].Timestamp + + id, err := metadataService.ID(ctx, sdCollectorInputData) + if err != nil { + m.Logger().Errorf("error trying to retrieve ID from metric event '%v'", err) + continue + } - keyValues[i].ECS = metadataCollectorData.ECS - keyValues[i].Labels = metadataCollectorData.Labels + metadataCollectorData, err := metadataService.Metadata(ctx, sdCollectorInputData.TimeSeries) + if err != nil { + m.Logger().Error("error trying to retrieve labels from metric event") + continue + } - // Group the data into common events - eventGroups[id] = append(eventGroups[id], keyValues[i]) + if _, ok := eventGroups[id]; !ok { + eventGroups[id] = make([]KeyValuePoint, 0) + } + + keyValues[i].ECS = metadataCollectorData.ECS + keyValues[i].Labels = metadataCollectorData.Labels + + // Group the data into common events + eventGroups[id] = append(eventGroups[id], keyValues[i]) + } } } diff --git a/x-pack/metricbeat/module/googlecloud/storage/_meta/data.json b/x-pack/metricbeat/module/googlecloud/storage/_meta/data.json index f94a1375997..679102209b2 100644 --- a/x-pack/metricbeat/module/googlecloud/storage/_meta/data.json +++ b/x-pack/metricbeat/module/googlecloud/storage/_meta/data.json @@ -2,7 +2,7 @@ "@timestamp": "2017-10-12T08:05:34.853Z", "cloud": { "account": { - "id": "elastic-observability" + "id": "elastic-apm" }, "provider": "googlecloud" }, @@ -14,18 +14,18 @@ "googlecloud": { "labels": { "metrics": { - "storage_class": "REGIONAL" + "storage_class": "MULTI_REGIONAL" }, "resource": { - "bucket_name": "elastic-vsphere-images", - "location": "us-east1" + "bucket_name": "artifacts.elastic-apm.appspot.com", + "location": "us" } }, "storage": { "storage": { - "object_count": 3, - "total_byte_seconds": 58816542441472, - "total_bytes": 680747019 + "object_count": { + "value": 15 + } } } }, diff --git a/x-pack/metricbeat/module/googlecloud/storage/_meta/data_network.json b/x-pack/metricbeat/module/googlecloud/storage/_meta/data_network.json new file mode 100644 index 00000000000..7bb1c6a4c86 --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/storage/_meta/data_network.json @@ -0,0 +1,38 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "cloud": { + "account": { + "id": "elastic-observability" + }, + "provider": "googlecloud" + }, + "event": { + "dataset": "googlecloud.storage", + "duration": 115000, + "module": "googlecloud" + }, + "googlecloud": { + "labels": { + "metrics": { + "method": "GetBucketMetadata", + "response_code": "OK" + }, + "resource": { + "bucket_name": "ocp-be-c5kjr-image-registry-us-central1-dsoafnbgctvfimpavswkgn", + "location": "us-central1" + } + }, + "storage": { + "network": { + "received_bytes_count": 0 + } + } + }, + "metricset": { + "name": "storage", + "period": 10000 + }, + "service": { + "type": "googlecloud" + } +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/storage/_meta/data_storage.json b/x-pack/metricbeat/module/googlecloud/storage/_meta/data_storage.json new file mode 100644 index 00000000000..f98a6a6f744 --- /dev/null +++ b/x-pack/metricbeat/module/googlecloud/storage/_meta/data_storage.json @@ -0,0 +1,39 @@ +{ + "@timestamp": "2017-10-12T08:05:34.853Z", + "cloud": { + "account": { + "id": "elastic-observability" + }, + "provider": "googlecloud" + }, + "event": { + "dataset": "googlecloud.storage", + "duration": 115000, + "module": "googlecloud" + }, + "googlecloud": { + "labels": { + "metrics": { + "storage_class": "MULTI_REGIONAL" + }, + "resource": { + "bucket_name": "fstuermer-log-data-categorization-7-6-0", + "location": "us" + } + }, + "storage": { + "storage": { + "total_bytes": { + "value": 4472520191 + } + } + } + }, + "metricset": { + "name": "storage", + "period": 10000 + }, + "service": { + "type": "googlecloud" + } +} \ No newline at end of file diff --git a/x-pack/metricbeat/module/googlecloud/storage/_meta/docs.asciidoc b/x-pack/metricbeat/module/googlecloud/storage/_meta/docs.asciidoc index 4c9ff62e4ae..d58ceda230f 100644 --- a/x-pack/metricbeat/module/googlecloud/storage/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/googlecloud/storage/_meta/docs.asciidoc @@ -1,11 +1,12 @@ -Storage Metricset to fetch metrics from https://cloud.google.com/storage/[Storage] in Google Cloud Platform. +Storage metricset to fetch metrics from https://cloud.google.com/storage/[Storage] in Google Cloud Platform. -The `storage` Metricset contains all metrics exported from the https://cloud.google.com/monitoring/api/metrics_gcp#gcp-storage[Stackdriver API]. The field names have been left untouched for people already familiar with them. +The `storage` metricset contains all metrics exported from the https://cloud.google.com/monitoring/api/metrics_gcp#gcp-storage[Stackdriver API]. The field names have been left untouched for people already familiar with them. You can specify a single region to fetch metrics like `us-central1`. Be aware that GCP Storage does not use zones so `us-central1-a` will return nothing. If no region is specified, it will return metrics from all buckets. [float] -=== Fields +=== Metrics +Here is a list of metrics collected by `storage` metricset: - `storage.api.request_count`: Delta count of API calls, grouped by the API method name and response code. - `storage.authz.acl_based_object_access_count`: Delta count of requests that result in an object being granted access solely due to object ACLs. diff --git a/x-pack/metricbeat/module/googlecloud/storage/_meta/fields.yml b/x-pack/metricbeat/module/googlecloud/storage/_meta/fields.yml index 71fcb2bdbeb..cdf2fde2fff 100644 --- a/x-pack/metricbeat/module/googlecloud/storage/_meta/fields.yml +++ b/x-pack/metricbeat/module/googlecloud/storage/_meta/fields.yml @@ -6,39 +6,39 @@ - name: api type: group fields: - - name: request_count + - name: request_count.value type: long description: Delta count of API calls, grouped by the API method name and response code. - name: authz type: group fields: - - name: acl_based_object_access_count + - name: acl_based_object_access_count.value type: long description: Delta count of requests that result in an object being granted access solely due to object ACLs. - - name: acl_operations_count + - name: acl_operations_count.value type: long description: Usage of ACL operations broken down by type. - - name: object_specific_acl_mutation_count + - name: object_specific_acl_mutation_count.value type: long description: Delta count of changes made to object specific ACLs. - name: network type: group fields: - - name: received_bytes_count + - name: received_bytes_count.value type: long description: Delta count of bytes received over the network, grouped by the API method name and response code. - - name: sent_bytes_count + - name: sent_bytes_count.value type: long description: Delta count of bytes sent over the network, grouped by the API method name and response code. - name: storage type: group fields: - - name: object_count + - name: object_count.value type: long description: Total number of objects per bucket, grouped by storage class. This value is measured once per day, and the value is repeated at each sampling interval throughout the day. - - name: total_byte_seconds + - name: total_byte_seconds.value type: long description: Delta count of bytes received over the network, grouped by the API method name and response code. - - name: total_bytes + - name: total_bytes.value type: long description: Total size of all objects in the bucket, grouped by storage class. This value is measured once per day, and the value is repeated at each sampling interval throughout the day. diff --git a/x-pack/metricbeat/module/googlecloud/storage/manifest.yml b/x-pack/metricbeat/module/googlecloud/storage/manifest.yml index f462867dcb5..2a9363cf78d 100644 --- a/x-pack/metricbeat/module/googlecloud/storage/manifest.yml +++ b/x-pack/metricbeat/module/googlecloud/storage/manifest.yml @@ -6,12 +6,13 @@ input: stackdriver: service: storage metrics: - - "storage.googleapis.com/api/request_count" - - "storage.googleapis.com/authz/acl_based_object_access_count" - - "storage.googleapis.com/authz/acl_operations_count" - - "storage.googleapis.com/authz/object_specific_acl_mutation_count" - - "storage.googleapis.com/network/received_bytes_count" - - "storage.googleapis.com/network/sent_bytes_count" - - "storage.googleapis.com/storage/object_count" - - "storage.googleapis.com/storage/total_byte_seconds" - - "storage.googleapis.com/storage/total_bytes" + - metric_types: + - "storage.googleapis.com/api/request_count" + - "storage.googleapis.com/authz/acl_based_object_access_count" + - "storage.googleapis.com/authz/acl_operations_count" + - "storage.googleapis.com/authz/object_specific_acl_mutation_count" + - "storage.googleapis.com/network/received_bytes_count" + - "storage.googleapis.com/network/sent_bytes_count" + - "storage.googleapis.com/storage/object_count" + - "storage.googleapis.com/storage/total_byte_seconds" + - "storage.googleapis.com/storage/total_bytes" diff --git a/x-pack/metricbeat/module/googlecloud/storage/storage_integration_test.go b/x-pack/metricbeat/module/googlecloud/storage/storage_integration_test.go index f669a7c46e9..7d40e7b2bf9 100644 --- a/x-pack/metricbeat/module/googlecloud/storage/storage_integration_test.go +++ b/x-pack/metricbeat/module/googlecloud/storage/storage_integration_test.go @@ -8,14 +8,38 @@ package storage import ( + "fmt" "testing" + "github.com/elastic/beats/v7/libbeat/common" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" - "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud" + "github.com/elastic/beats/v7/x-pack/metricbeat/module/googlecloud/stackdriver" ) func TestData(t *testing.T) { - config := googlecloud.GetConfigForTest(t, "storage") - metricSet := mbtest.NewFetcher(t, config) - metricSet.WriteEvents(t, "/") + metricPrefixIs := func(metricPrefix string) func(e common.MapStr) bool { + return func(e common.MapStr) bool { + v, err := e.GetValue(metricPrefix) + return err == nil && v != nil + } + } + + dataFiles := []struct { + metricPrefix string + path string + }{ + {"googlecloud.storage", "./_meta/data.json"}, + {"googlecloud.storage.authz", "./_meta/data_authz.json"}, + {"googlecloud.storage.network", "./_meta/data_network.json"}, + {"googlecloud.storage.storage", "./_meta/data_storage.json"}, + } + + config := stackdriver.GetConfigForTest(t, "storage") + + for _, df := range dataFiles { + metricSet := mbtest.NewFetcher(t, config) + t.Run(fmt.Sprintf("metric prefix: %s", df.metricPrefix), func(t *testing.T) { + metricSet.WriteEventsCond(t, df.path, metricPrefixIs(df.metricPrefix)) + }) + } } diff --git a/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-application-pool-overview.json b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-application-pool-overview.json new file mode 100644 index 00000000000..866b4593489 --- /dev/null +++ b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-application-pool-overview.json @@ -0,0 +1,1026 @@ +{ + "objects": [ + { + "attributes": { + "description": "This dashboard shows application pools metrics for the IIS server.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 5, + "i": "43790631-73f5-4f5e-824e-aa9d3f4f5664", + "w": 9, + "x": 0, + "y": 0 + }, + "panelIndex": "43790631-73f5-4f5e-824e-aa9d3f4f5664", + "panelRefName": "panel_0", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Thread Count" + }, + "gridData": { + "h": 11, + "i": "b94f6dac-0f16-4781-ac08-d632154458a6", + "w": 20, + "x": 9, + "y": 0 + }, + "panelIndex": "b94f6dac-0f16-4781-ac08-d632154458a6", + "panelRefName": "panel_1", + "title": "Thread Count", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Handle Count" + }, + "gridData": { + "h": 11, + "i": "a3c19f11-99d4-4794-8ebb-aa2b3583577b", + "w": 19, + "x": 29, + "y": 0 + }, + "panelIndex": "a3c19f11-99d4-4794-8ebb-aa2b3583577b", + "panelRefName": "panel_2", + "title": "Handle Count", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Filters" + }, + "gridData": { + "h": 6, + "i": "10fe7306-655b-4bc6-b078-892f91d0d9ea", + "w": 9, + "x": 0, + "y": 5 + }, + "panelIndex": "10fe7306-655b-4bc6-b078-892f91d0d9ea", + "panelRefName": "panel_3", + "title": "Filters", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "IO Write Operations" + }, + "gridData": { + "h": 15, + "i": "4dfc6a98-4749-47a8-a92e-ee955cf20c00", + "w": 24, + "x": 0, + "y": 11 + }, + "panelIndex": "4dfc6a98-4749-47a8-a92e-ee955cf20c00", + "panelRefName": "panel_4", + "title": "IO Write Operations", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "IO Read Operations" + }, + "gridData": { + "h": 15, + "i": "bae63734-a752-46c3-8083-8c12eabca5aa", + "w": 24, + "x": 24, + "y": 11 + }, + "panelIndex": "bae63734-a752-46c3-8083-8c12eabca5aa", + "panelRefName": "panel_5", + "title": "IO Read Operations", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Working Set" + }, + "gridData": { + "h": 15, + "i": "ce663f89-7cf9-4659-af06-2128f7d2f74b", + "w": 24, + "x": 0, + "y": 26 + }, + "panelIndex": "ce663f89-7cf9-4659-af06-2128f7d2f74b", + "panelRefName": "panel_6", + "title": "Working Set", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Virtual Bytes" + }, + "gridData": { + "h": 15, + "i": "99da2951-fb14-4a18-8a0c-4647daa9b626", + "w": 24, + "x": 24, + "y": 41 + }, + "panelIndex": "99da2951-fb14-4a18-8a0c-4647daa9b626", + "panelRefName": "panel_7", + "title": "Virtual Bytes", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Private Bytes" + }, + "gridData": { + "h": 15, + "i": "71985962-b5c9-412e-a99f-b55ed988f4ea", + "w": 24, + "x": 0, + "y": 41 + }, + "panelIndex": "71985962-b5c9-412e-a99f-b55ed988f4ea", + "panelRefName": "panel_8", + "title": "Private Bytes", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "CPU Usage" + }, + "gridData": { + "h": 15, + "i": "347b6893-0ab0-47fc-9e80-efc9bf47f13e", + "w": 24, + "x": 24, + "y": 26 + }, + "panelIndex": "347b6893-0ab0-47fc-9e80-efc9bf47f13e", + "panelRefName": "panel_9", + "title": "CPU Usage", + "version": "7.6.0" + } + ], + "timeRestore": false, + "title": "[Metricbeat IIS] Application Pool Overview", + "version": 1 + }, + "id": "b4108810-861c-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "dc97bec0-861c-11ea-91bc-ab084c7ec0e7", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "41324ad0-861d-11ea-91bc-ab084c7ec0e7", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "98b90fa0-861d-11ea-91bc-ab084c7ec0e7", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "dd419de0-861d-11ea-91bc-ab084c7ec0e7", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "442a86c0-861e-11ea-91bc-ab084c7ec0e7", + "name": "panel_4", + "type": "visualization" + }, + { + "id": "29a23aa0-861e-11ea-91bc-ab084c7ec0e7", + "name": "panel_5", + "type": "visualization" + }, + { + "id": "34bfec50-8620-11ea-91bc-ab084c7ec0e7", + "name": "panel_6", + "type": "visualization" + }, + { + "id": "14300bf0-8620-11ea-91bc-ab084c7ec0e7", + "name": "panel_7", + "type": "visualization" + }, + { + "id": "f7194cc0-861f-11ea-91bc-ab084c7ec0e7", + "name": "panel_8", + "type": "visualization" + }, + { + "id": "90fe3b30-861f-11ea-91bc-ab084c7ec0e7", + "name": "panel_9", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-04-24T12:19:57.800Z", + "version": "Wzk3MzMsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Navigation Application Pool Overview [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "fontSize": 10, + "markdown": "### IIS\n\n[Webserver](#/dashboard/ebc23240-8572-11ea-91bc-ab084c7ec0e7)| [Webserver processes](#/dashboard/2c171500-858b-11ea-91bc-ab084c7ec0e7) | [Websites](#/dashboard/4b975820-85a1-11ea-91bc-ab084c7ec0e7) | [**Application Pools**](#/dashboard/b4108810-861c-11ea-91bc-ab084c7ec0e7) ", + "openLinksInNewTab": false + }, + "title": "Navigation Application Pool Overview [Metricbeat IIS]", + "type": "markdown" + } + }, + "id": "dc97bec0-861c-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:54:38.997Z", + "version": "WzkzMTksMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool Thread Count [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(22,165,165,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Thread Count", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.application_pool.process.thread_count", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "terms_order_by": "_count", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "top_n" + }, + "title": "Application Pool Thread Count [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "41324ad0-861d-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:17:59.421Z", + "version": "WzkwMDMsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool Handle Count [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(101,50,148,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Handle Count", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.application_pool.process.handle_count", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "top_n" + }, + "title": "Application Pool Handle Count [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "98b90fa0-861d-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:20:26.266Z", + "version": "WzkwMTgsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool Filters [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "controls": [ + { + "fieldName": "iis.application_pool.name", + "id": "1549397251041", + "indexPatternRefName": "control_0_index_pattern", + "label": "Application Pools", + "options": { + "dynamicOptions": true, + "multiselect": true, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "", + "type": "list" + } + ], + "pinFilters": false, + "updateFiltersOnChange": true, + "useTimeFilter": false + }, + "title": "Application Pool Filters [Metricbeat IIS]", + "type": "input_control_vis" + } + }, + "id": "dd419de0-861d-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "control_0_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-04-24T11:22:21.246Z", + "version": "WzkwMjksMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool IO Write Operations [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(104,188,0,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "IO Write Operations/s", + "line_width": "2", + "metrics": [ + { + "field": "iis.application_pool.process.io_write_operations_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Application Pool IO Write Operations [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "442a86c0-861e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:25:13.899Z", + "version": "WzkwNDIsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool IO Read Operations [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(252,196,0,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "IO Read Operations/s", + "line_width": "2", + "metrics": [ + { + "field": "iis.application_pool.process.io_read_operations_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Application Pool IO Read Operations [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "29a23aa0-861e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:26:30.995Z", + "version": "WzkwNjcsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool Working Set [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(115,216,255,1)", + "fill": "0", + "formatter": "bytes", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Working Set", + "line_width": "2", + "metrics": [ + { + "field": "iis.application_pool.process.working_set", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Application Pool Working Set [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "34bfec50-8620-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:39:22.702Z", + "version": "WzkxMTEsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool Virtual Bytes [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(0,98,177,1)", + "fill": "0", + "formatter": "bytes", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Virtual Bytes", + "line_width": "2", + "metrics": [ + { + "field": "iis.application_pool.process.virtual_bytes", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Application Pool Virtual Bytes [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "14300bf0-8620-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:38:12.399Z", + "version": "WzkxMDEsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool Private Bytes [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#3185FC", + "fill": "0", + "formatter": "bytes", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Private Bytes", + "line_width": "2", + "metrics": [ + { + "field": "iis.application_pool.process.private_bytes", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Application Pool Private Bytes [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "f7194cc0-861f-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T12:19:46.947Z", + "version": "Wzk3MjQsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Application Pool CPU Usage [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#3185FC", + "fill": "0", + "formatter": "percent", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "CPU Usage", + "line_width": "2", + "metrics": [ + { + "field": "iis.application_pool.process.cpu_usage_perc", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.application_pool.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Application Pool CPU Usage [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "90fe3b30-861f-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:34:32.291Z", + "version": "WzkwODEsMTNd" + } + ], + "version": "7.6.0" +} diff --git a/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-webserver-overview.json b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-webserver-overview.json new file mode 100644 index 00000000000..57fe28b8ea6 --- /dev/null +++ b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-webserver-overview.json @@ -0,0 +1,1602 @@ +{ + "objects": [ + { + "attributes": { + "description": "This dashboard shows metrics for the IIS server.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 5, + "i": "55b62196-f0fa-4473-a578-c5197ae7581c", + "w": 10, + "x": 0, + "y": 0 + }, + "panelIndex": "55b62196-f0fa-4473-a578-c5197ae7581c", + "panelRefName": "panel_0", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "9f1c724a-e57a-4667-8930-b60997efeee5", + "w": 9, + "x": 10, + "y": 0 + }, + "panelIndex": "9f1c724a-e57a-4667-8930-b60997efeee5", + "panelRefName": "panel_1", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "5aeeab99-314c-4732-b731-ebd390fa9210", + "w": 10, + "x": 19, + "y": 0 + }, + "panelIndex": "5aeeab99-314c-4732-b731-ebd390fa9210", + "panelRefName": "panel_2", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "88113bca-98b0-4877-b768-46f9b1c29751", + "w": 10, + "x": 29, + "y": 0 + }, + "panelIndex": "88113bca-98b0-4877-b768-46f9b1c29751", + "panelRefName": "panel_3", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "0add6210-d7d5-4b66-afaa-bcec0bc445d9", + "w": 9, + "x": 39, + "y": 0 + }, + "panelIndex": "0add6210-d7d5-4b66-afaa-bcec0bc445d9", + "panelRefName": "panel_4", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 8, + "i": "e809e015-29b2-496b-aaf1-f461d59b1568", + "w": 5, + "x": 0, + "y": 5 + }, + "panelIndex": "e809e015-29b2-496b-aaf1-f461d59b1568", + "panelRefName": "panel_5", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 8, + "i": "6ebec04f-3df0-4995-ae9c-836774f05ea7", + "w": 5, + "x": 5, + "y": 5 + }, + "panelIndex": "6ebec04f-3df0-4995-ae9c-836774f05ea7", + "panelRefName": "panel_6", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "Total Requests" + }, + "gridData": { + "h": 12, + "i": "41de633b-40aa-4456-967a-5737d139a374", + "w": 38, + "x": 10, + "y": 9 + }, + "panelIndex": "41de633b-40aa-4456-967a-5737d139a374", + "panelRefName": "panel_7", + "title": "Total Requests", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 8, + "i": "3e1276ed-2f8f-4a07-948c-cf5ae0de9fc3", + "w": 5, + "x": 0, + "y": 13 + }, + "panelIndex": "3e1276ed-2f8f-4a07-948c-cf5ae0de9fc3", + "panelRefName": "panel_8", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 8, + "i": "983e0c36-04f7-4ec0-b6a6-8d2599a63065", + "w": 5, + "x": 5, + "y": 13 + }, + "panelIndex": "983e0c36-04f7-4ec0-b6a6-8d2599a63065", + "panelRefName": "panel_9", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "Total Bytes Transferred" + }, + "gridData": { + "h": 14, + "i": "aa567463-86ec-4bbe-bf0f-b0d4d1d57bb0", + "w": 24, + "x": 24, + "y": 21 + }, + "panelIndex": "aa567463-86ec-4bbe-bf0f-b0d4d1d57bb0", + "panelRefName": "panel_10", + "title": "Total Bytes Transferred", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "Bytes Transferred/sec" + }, + "gridData": { + "h": 14, + "i": "623596fb-3f1f-4f2e-b15b-800d197ce1aa", + "w": 24, + "x": 0, + "y": 21 + }, + "panelIndex": "623596fb-3f1f-4f2e-b15b-800d197ce1aa", + "panelRefName": "panel_11", + "title": "Bytes Transferred/sec", + "version": "7.6.2" + } + ], + "timeRestore": false, + "title": "[Metricbeat IIS] Webserver Overview", + "version": 1 + }, + "id": "ebc23240-8572-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "40614070-8573-11ea-91bc-ab084c7ec0e7", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "82f1d7d0-858a-11ea-91bc-ab084c7ec0e7", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "c92e0b80-8574-11ea-91bc-ab084c7ec0e7", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "348c4fe0-8575-11ea-91bc-ab084c7ec0e7", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "461a8640-8576-11ea-91bc-ab084c7ec0e7", + "name": "panel_4", + "type": "visualization" + }, + { + "id": "c8e467d0-8d55-11ea-817c-a9b6d42fd8a0", + "name": "panel_5", + "type": "visualization" + }, + { + "id": "df9d0e50-8d55-11ea-817c-a9b6d42fd8a0", + "name": "panel_6", + "type": "visualization" + }, + { + "id": "75812480-857f-11ea-91bc-ab084c7ec0e7", + "name": "panel_7", + "type": "visualization" + }, + { + "id": "14e77b40-8d56-11ea-817c-a9b6d42fd8a0", + "name": "panel_8", + "type": "visualization" + }, + { + "id": "2d802c60-8d56-11ea-817c-a9b6d42fd8a0", + "name": "panel_9", + "type": "visualization" + }, + { + "id": "68a9df20-8581-11ea-91bc-ab084c7ec0e7", + "name": "panel_10", + "type": "visualization" + }, + { + "id": "92acc3e0-8582-11ea-91bc-ab084c7ec0e7", + "name": "panel_11", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-05-03T16:20:16.383Z", + "version": "WzEyNDUsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Navigation Webserver Overview [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "fontSize": 10, + "markdown": "### IIS\n\n[**Webserver**](#/dashboard/ebc23240-8572-11ea-91bc-ab084c7ec0e7)| [Webserver processes](#/dashboard/2c171500-858b-11ea-91bc-ab084c7ec0e7) | [Websites](#/dashboard/4b975820-85a1-11ea-91bc-ab084c7ec0e7) | [Application Pools](#/dashboard/b4108810-861c-11ea-91bc-ab084c7ec0e7) \n\n\n\n", + "openLinksInNewTab": false + }, + "title": "Navigation Webserver Overview [Metricbeat IIS]", + "type": "markdown" + } + }, + "id": "40614070-8573-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUwOSwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Service Uptime [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,49,21,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "s,h,", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Service Uptime", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.service_uptime", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} h" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Service Uptime [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "82f1d7d0-858a-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUxMCwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Current Connections [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(104,188,0,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Current Connections", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.current_connections", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Current Connections [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "c92e0b80-8574-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUxMSwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Maximum Connections [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(104,204,202,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Maximum Connections", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.maximum_connections", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Maximum Connections [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "348c4fe0-8575-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUxMiwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Total Connection Attempts [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(251,158,0,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Total Connection Attempts", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.total_connection_attempts", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Total Connection Attempts [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "461a8640-8576-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUxMywxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Overview Current Anonymous Users [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(49,211,21,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Current Anonymous Users", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.current_anonymous_users", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Overview Current Anonymous Users [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "c8e467d0-8d55-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:50:17.034Z", + "version": "WzEwNjcsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Overview Total Anonymous Users [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(21,68,211,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Total Anonymous Users", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.total_anonymous_users", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Overview Total Anonymous Users [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "df9d0e50-8d55-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:50:55.156Z", + "version": "WzEwNzMsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Total Requests [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#3185FC", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Total Get Requests", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.network.total_get_requests", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "everything", + "stacked": "none", + "terms_field": null, + "type": "timeseries", + "value_template": "{{value}}" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": "0", + "formatter": "number", + "id": "55ef6fb0-857e-11ea-87b6-db4d36ae5839", + "label": "Total Post Requests", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.network.total_post_requests", + "id": "55ef6fb1-857e-11ea-87b6-db4d36ae5839", + "type": "avg" + } + ], + "point_size": "0", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(149,0,188,1)", + "fill": "0", + "formatter": "number", + "id": "7501b0c0-857e-11ea-87b6-db4d36ae5839", + "label": "Total Delete Requests", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.network.total_delete_requests", + "id": "7501b0c1-857e-11ea-87b6-db4d36ae5839", + "type": "avg" + } + ], + "point_size": "", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Webserver Total Requests [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "75812480-857f-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUxNSwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Overview Current Non Anonymous Users [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(171,21,211,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Current Non Anonymous Users", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.current_non_anonymous_users", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Overview Current Non Anonymous Users [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "14e77b40-8d56-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:52:24.564Z", + "version": "WzEwOTcsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Overview Total Non Anonymous Users [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,21,105,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Total Non Anonymous Users", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.total_non_anonymous_users", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Overview Total Non Anonymous Users [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "2d802c60-8d56-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:53:05.830Z", + "version": "WzExMDQsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Total Bytes Transfered [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(251,158,0,1)", + "fill": "0", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "bytes", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Total Bytes Sent ", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.total_bytes_sent", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}}" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": "0", + "formatter": "bytes", + "id": "cb6910f0-8580-11ea-8d9f-cf59f8572d31", + "label": "Total Bytes Received", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.network.total_bytes_received", + "id": "cb693800-8580-11ea-8d9f-cf59f8572d31", + "type": "avg" + } + ], + "point_size": "", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "timeseries" + }, + "title": "Webserver Total Bytes Transfered [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "68a9df20-8581-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUxNywxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Bytes Transfered Per Sec [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(22,165,165,1)", + "fill": "0", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Bytes Sent/sec", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.network.bytes_sent_per_sec", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}}/s" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(250,40,255,1)", + "fill": "0", + "formatter": "number", + "id": "cb6910f0-8580-11ea-8d9f-cf59f8572d31", + "label": "Bytes Received/sec", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.network.bytes_received_per_sec", + "id": "cb693800-8580-11ea-8d9f-cf59f8572d31", + "type": "avg" + } + ], + "point_size": "", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "timeseries" + }, + "title": "Webserver Bytes Transfered Per Sec [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "92acc3e0-8582-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:24.547Z", + "version": "WzUxNiwxXQ==" + } + ], + "version": "7.6.2" +} diff --git a/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-webserver-process-overview.json b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-webserver-process-overview.json new file mode 100644 index 00000000000..6c294be381f --- /dev/null +++ b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-webserver-process-overview.json @@ -0,0 +1,1510 @@ +{ + "objects": [ + { + "attributes": { + "description": "This dashboard shows process and cache metrics for the IIS server.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 5, + "i": "8c503a52-2cfb-4922-97db-b91bf343285d", + "w": 10, + "x": 0, + "y": 0 + }, + "panelIndex": "8c503a52-2cfb-4922-97db-b91bf343285d", + "panelRefName": "panel_0", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 11, + "i": "1bfd458d-84ff-4b0c-b091-9bfd7ac15de1", + "w": 8, + "x": 10, + "y": 0 + }, + "panelIndex": "1bfd458d-84ff-4b0c-b091-9bfd7ac15de1", + "panelRefName": "panel_1", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 11, + "i": "c0111c35-69be-41b9-a6be-b83f83721aeb", + "w": 10, + "x": 18, + "y": 0 + }, + "panelIndex": "c0111c35-69be-41b9-a6be-b83f83721aeb", + "panelRefName": "panel_2", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 11, + "i": "b857a52c-865a-4f99-9acb-cc6b64b4508b", + "w": 10, + "x": 28, + "y": 0 + }, + "panelIndex": "b857a52c-865a-4f99-9acb-cc6b64b4508b", + "panelRefName": "panel_3", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 11, + "i": "dfbef78c-4636-4c5f-ad52-45f931b6a101", + "w": 10, + "x": 38, + "y": 0 + }, + "panelIndex": "dfbef78c-4636-4c5f-ad52-45f931b6a101", + "panelRefName": "panel_4", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "552a8182-6705-4faa-95e5-2cde2a3e77c0", + "w": 5, + "x": 0, + "y": 5 + }, + "panelIndex": "552a8182-6705-4faa-95e5-2cde2a3e77c0", + "panelRefName": "panel_5", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "1f8299eb-beef-4851-ac58-4416891629c2", + "w": 5, + "x": 5, + "y": 5 + }, + "panelIndex": "1f8299eb-beef-4851-ac58-4416891629c2", + "panelRefName": "panel_6", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "IO Operations" + }, + "gridData": { + "h": 12, + "i": "5b29dd93-bdc2-4539-a08f-023638a8ccdb", + "w": 38, + "x": 10, + "y": 11 + }, + "panelIndex": "5b29dd93-bdc2-4539-a08f-023638a8ccdb", + "panelRefName": "panel_7", + "title": "IO Operations", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "c0d58d03-1af7-46d0-a01d-6d9c2d05d703", + "w": 5, + "x": 0, + "y": 14 + }, + "panelIndex": "c0d58d03-1af7-46d0-a01d-6d9c2d05d703", + "panelRefName": "panel_8", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 9, + "i": "ddc9edcc-d8ee-4953-9f8e-0c24328a90e3", + "w": 5, + "x": 5, + "y": 14 + }, + "panelIndex": "ddc9edcc-d8ee-4953-9f8e-0c24328a90e3", + "panelRefName": "panel_9", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "Memory Usage" + }, + "gridData": { + "h": 15, + "i": "12a8098a-9dfa-464b-b4b7-c08a02829aa1", + "w": 24, + "x": 24, + "y": 23 + }, + "panelIndex": "12a8098a-9dfa-464b-b4b7-c08a02829aa1", + "panelRefName": "panel_10", + "title": "Memory Usage", + "version": "7.6.2" + }, + { + "embeddableConfig": { + "title": "CPU Usage" + }, + "gridData": { + "h": 15, + "i": "4b54bddb-c2e7-47a1-ab7c-71c51e91a931", + "w": 24, + "x": 0, + "y": 23 + }, + "panelIndex": "4b54bddb-c2e7-47a1-ab7c-71c51e91a931", + "panelRefName": "panel_11", + "title": "CPU Usage", + "version": "7.6.2" + } + ], + "timeRestore": false, + "title": "[Metricbeat IIS] Webserver Process Overview", + "version": 1 + }, + "id": "2c171500-858b-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "e6fab5c0-858b-11ea-91bc-ab084c7ec0e7", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "1084a0e0-8d57-11ea-817c-a9b6d42fd8a0", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "55755550-858c-11ea-91bc-ab084c7ec0e7", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "6c1272a0-858e-11ea-91bc-ab084c7ec0e7", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "92dcde20-858e-11ea-91bc-ab084c7ec0e7", + "name": "panel_4", + "type": "visualization" + }, + { + "id": "d9dc78b0-8d56-11ea-817c-a9b6d42fd8a0", + "name": "panel_5", + "type": "visualization" + }, + { + "id": "b5c6f400-8d56-11ea-817c-a9b6d42fd8a0", + "name": "panel_6", + "type": "visualization" + }, + { + "id": "e26479e0-858d-11ea-91bc-ab084c7ec0e7", + "name": "panel_7", + "type": "visualization" + }, + { + "id": "7d9e1f40-8d56-11ea-817c-a9b6d42fd8a0", + "name": "panel_8", + "type": "visualization" + }, + { + "id": "945f7850-8d56-11ea-817c-a9b6d42fd8a0", + "name": "panel_9", + "type": "visualization" + }, + { + "id": "e4d91170-858f-11ea-91bc-ab084c7ec0e7", + "name": "panel_10", + "type": "visualization" + }, + { + "id": "2dd099f0-858d-11ea-91bc-ab084c7ec0e7", + "name": "panel_11", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-05-03T16:16:09.608Z", + "version": "WzEyMTcsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Navigation Webserver Process Overview [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "fontSize": 10, + "markdown": "### IIS\n\n[Webserver](#/dashboard/ebc23240-8572-11ea-91bc-ab084c7ec0e7)| [**Webserver processes**](#/dashboard/2c171500-858b-11ea-91bc-ab084c7ec0e7) | [Websites](#/dashboard/4b975820-85a1-11ea-91bc-ab084c7ec0e7) | [Application Pools](#/dashboard/b4108810-861c-11ea-91bc-ab084c7ec0e7) ", + "openLinksInNewTab": false + }, + "title": "Navigation Webserver Process Overview [Metricbeat IIS]", + "type": "markdown" + } + }, + "id": "e6fab5c0-858b-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:25.601Z", + "version": "WzUxOSwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process Output Cache Current Memory Usage [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,181,21,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "bytes", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Output Cache Current Memory Usage", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.cache.output_cache_current_memory_usage", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Process Output Cache Current Memory Usage [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "1084a0e0-8d57-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T16:10:45.498Z", + "version": "WzExOTEsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process Worker Process Count [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,49,21,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Worker Processes", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.process.worker_process_count", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Process Worker Process Count [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "55755550-858c-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:25.601Z", + "version": "WzUyMCwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process Thread Count [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(22,165,165,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Thread Count", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.process.thread_count", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Process Thread Count [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "6c1272a0-858e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:25.601Z", + "version": "WzUyMSwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process Handle Count [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(101,50,148,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Handle Count", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.process.handle_count", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Process Handle Count [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "92dcde20-858e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:25.601Z", + "version": "WzUyMiwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process Current Files Cached [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(21,211,162,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Current Files Cached", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.cache.current_files_cached", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Process Current Files Cached [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "d9dc78b0-8d56-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:57:55.003Z", + "version": "WzExMzMsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process Total Files Cached [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(21,162,211,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Total Files Cached", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.cache.total_files_cached", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Process Total Files Cached [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "b5c6f400-8d56-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:57:12.722Z", + "version": "WzExMjgsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process IO Operations [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(252,196,0,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "IO Read Operations/s", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.process.io_read_operations_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "everything", + "stacked": "none", + "terms_field": null, + "type": "timeseries", + "value_template": "{{value}}/s" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": "0", + "formatter": "number", + "id": "55ef6fb0-857e-11ea-87b6-db4d36ae5839", + "label": "IO Write Operations/s", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.process.io_write_operations_per_sec", + "id": "55ef6fb1-857e-11ea-87b6-db4d36ae5839", + "type": "avg" + } + ], + "point_size": "0", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Webserver Process IO Operations [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "e26479e0-858d-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:25.601Z", + "version": "WzUyNCwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Current Uris Cached [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,21,105,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Current Uris Cached", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.cache.current_uris_cached", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Current Uris Cached [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "7d9e1f40-8d56-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:55:20.244Z", + "version": "WzExMTQsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Total Uris Cached [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(92,21,211,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Total Uris Cached", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.webserver.cache.total_uris_cached", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "everything", + "stacked": "none", + "terms_field": "cloud.instance.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}} " + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "gauge" + }, + "title": "Webserver Total Uris Cached [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "945f7850-8d56-11ea-817c-a9b6d42fd8a0", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-05-03T15:55:58.421Z", + "version": "WzExMjAsMV0=" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process Memory Usage [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#3185FC", + "fill": "0", + "formatter": "bytes", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Private Bytes", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.process.private_bytes", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "everything", + "stacked": "none", + "terms_field": null, + "type": "timeseries", + "value_template": "{{value}}" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "#68BC00", + "fill": "0", + "formatter": "bytes", + "id": "55ef6fb0-857e-11ea-87b6-db4d36ae5839", + "label": "Virtual Bytes", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.process.virtual_bytes", + "id": "55ef6fb1-857e-11ea-87b6-db4d36ae5839", + "type": "avg" + } + ], + "point_size": "0", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries" + }, + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(149,0,188,1)", + "fill": "0", + "formatter": "bytes", + "id": "7501b0c0-857e-11ea-87b6-db4d36ae5839", + "label": "Working Set", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.process.working_set", + "id": "7501b0c1-857e-11ea-87b6-db4d36ae5839", + "type": "avg" + } + ], + "point_size": "", + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Webserver Process Memory Usage [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "e4d91170-858f-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:25.601Z", + "version": "WzUyNiwxXQ==" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Webserver Process CPU Usage [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#3185FC", + "fill": "0", + "formatter": "percent", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "CPU Usage", + "line_width": "2", + "metrics": [ + { + "field": "iis.webserver.process.cpu_usage_perc", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "everything", + "stacked": "none", + "terms_field": null, + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Webserver Process CPU Usage [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "2dd099f0-858d-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T13:07:25.601Z", + "version": "WzUyNSwxXQ==" + } + ], + "version": "7.6.2" +} diff --git a/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-website-overview.json b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-website-overview.json new file mode 100644 index 00000000000..8f1491f0f1c --- /dev/null +++ b/x-pack/metricbeat/module/iis/_meta/kibana/7/dashboard/Metricbeat-iis-website-overview.json @@ -0,0 +1,1685 @@ +{ + "objects": [ + { + "attributes": { + "description": "This dashboard shows metrics for the websites running on IIS.", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "" + }, + "gridData": { + "h": 6, + "i": "e8bd7244-57bf-4e16-b096-4fa7cb8cbba8", + "w": 9, + "x": 0, + "y": 0 + }, + "panelIndex": "e8bd7244-57bf-4e16-b096-4fa7cb8cbba8", + "panelRefName": "panel_0", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Current Connections" + }, + "gridData": { + "h": 12, + "i": "193cca9a-a5c3-40c4-a9af-1020b279b845", + "w": 13, + "x": 9, + "y": 0 + }, + "panelIndex": "193cca9a-a5c3-40c4-a9af-1020b279b845", + "panelRefName": "panel_1", + "title": "Current Connections", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Maximum Connections" + }, + "gridData": { + "h": 12, + "i": "7b5d1298-0480-443a-bf0e-5b594903c680", + "w": 13, + "x": 22, + "y": 0 + }, + "panelIndex": "7b5d1298-0480-443a-bf0e-5b594903c680", + "panelRefName": "panel_2", + "title": "Maximum Connections", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Total Connection Attempts" + }, + "gridData": { + "h": 12, + "i": "fd0c5fd7-4df8-44d9-905f-5634f49ae875", + "w": 13, + "x": 35, + "y": 0 + }, + "panelIndex": "fd0c5fd7-4df8-44d9-905f-5634f49ae875", + "panelRefName": "panel_3", + "title": "Total Connection Attempts", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Filters" + }, + "gridData": { + "h": 6, + "i": "69a127c6-8c4f-403a-aded-7a49dc3d3cd9", + "w": 9, + "x": 0, + "y": 6 + }, + "panelIndex": "69a127c6-8c4f-403a-aded-7a49dc3d3cd9", + "panelRefName": "panel_4", + "title": "Filters", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Service Uptime" + }, + "gridData": { + "h": 12, + "i": "545b1abe-fd22-455b-8bc6-68bd48c2d384", + "w": 9, + "x": 0, + "y": 12 + }, + "panelIndex": "545b1abe-fd22-455b-8bc6-68bd48c2d384", + "panelRefName": "panel_5", + "title": "Service Uptime", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Bytes Sent/sec" + }, + "gridData": { + "h": 12, + "i": "c026e461-8b64-4952-be07-744773cb18b9", + "w": 20, + "x": 9, + "y": 12 + }, + "panelIndex": "c026e461-8b64-4952-be07-744773cb18b9", + "panelRefName": "panel_6", + "title": "Bytes Sent/sec", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Bytes Received/sec" + }, + "gridData": { + "h": 12, + "i": "98b6e727-3343-43df-84e1-12933856f432", + "w": 19, + "x": 29, + "y": 12 + }, + "panelIndex": "98b6e727-3343-43df-84e1-12933856f432", + "panelRefName": "panel_7", + "title": "Bytes Received/sec", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "GET Requests/sec" + }, + "gridData": { + "h": 12, + "i": "d4b32bd4-dee2-44e2-930d-91e4607c0668", + "w": 12, + "x": 0, + "y": 24 + }, + "panelIndex": "d4b32bd4-dee2-44e2-930d-91e4607c0668", + "panelRefName": "panel_8", + "title": "GET Requests/sec", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "POST Requests/sec" + }, + "gridData": { + "h": 12, + "i": "d24b5bb0-6dc3-40ba-91cb-5ca5e48a3afa", + "w": 12, + "x": 12, + "y": 24 + }, + "panelIndex": "d24b5bb0-6dc3-40ba-91cb-5ca5e48a3afa", + "panelRefName": "panel_9", + "title": "POST Requests/sec", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "PUT Requests/sec" + }, + "gridData": { + "h": 12, + "i": "c8cfac9e-ad2a-4ddf-94c9-0454e987b2b8", + "w": 12, + "x": 24, + "y": 24 + }, + "panelIndex": "c8cfac9e-ad2a-4ddf-94c9-0454e987b2b8", + "panelRefName": "panel_10", + "title": "PUT Requests/sec", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "DELETE Requests/sec" + }, + "gridData": { + "h": 12, + "i": "41b4d631-4878-49af-b34b-f04f473599c8", + "w": 12, + "x": 36, + "y": 24 + }, + "panelIndex": "41b4d631-4878-49af-b34b-f04f473599c8", + "panelRefName": "panel_11", + "title": "DELETE Requests/sec", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Total DELETE Requests" + }, + "gridData": { + "h": 12, + "i": "90d9d209-6ef9-4ab7-b9a2-48a1f420e555", + "w": 12, + "x": 36, + "y": 36 + }, + "panelIndex": "90d9d209-6ef9-4ab7-b9a2-48a1f420e555", + "panelRefName": "panel_12", + "title": "Total DELETE Requests", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Total GET Requests" + }, + "gridData": { + "h": 12, + "i": "c7379d87-3da3-4be4-a8bd-8db04da66ba4", + "w": 12, + "x": 0, + "y": 36 + }, + "panelIndex": "c7379d87-3da3-4be4-a8bd-8db04da66ba4", + "panelRefName": "panel_13", + "title": "Total GET Requests", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Total POST Requests" + }, + "gridData": { + "h": 12, + "i": "9a22a36c-5fe8-4cc2-be23-4d9db2d7beda", + "w": 12, + "x": 12, + "y": 36 + }, + "panelIndex": "9a22a36c-5fe8-4cc2-be23-4d9db2d7beda", + "panelRefName": "panel_14", + "title": "Total POST Requests", + "version": "7.6.0" + }, + { + "embeddableConfig": { + "title": "Total PUT Requests " + }, + "gridData": { + "h": 12, + "i": "6cf940a0-eb04-447b-8f7e-d4561f8838ac", + "w": 12, + "x": 24, + "y": 36 + }, + "panelIndex": "6cf940a0-eb04-447b-8f7e-d4561f8838ac", + "panelRefName": "panel_15", + "title": "Total PUT Requests ", + "version": "7.6.0" + } + ], + "timeRestore": false, + "title": "[Metricbeat IIS] Website Overview", + "version": 1 + }, + "id": "4b975820-85a1-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "f9723710-8602-11ea-91bc-ab084c7ec0e7", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "57d914d0-860e-11ea-91bc-ab084c7ec0e7", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "6db58c20-860e-11ea-91bc-ab084c7ec0e7", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "e3ee4990-860e-11ea-91bc-ab084c7ec0e7", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "b7230190-8603-11ea-91bc-ab084c7ec0e7", + "name": "panel_4", + "type": "visualization" + }, + { + "id": "4557d670-860e-11ea-91bc-ab084c7ec0e7", + "name": "panel_5", + "type": "visualization" + }, + { + "id": "c784f9b0-8614-11ea-91bc-ab084c7ec0e7", + "name": "panel_6", + "type": "visualization" + }, + { + "id": "96fe7d70-8614-11ea-91bc-ab084c7ec0e7", + "name": "panel_7", + "type": "visualization" + }, + { + "id": "4921d5c0-8619-11ea-91bc-ab084c7ec0e7", + "name": "panel_8", + "type": "visualization" + }, + { + "id": "7dabd8e0-8619-11ea-91bc-ab084c7ec0e7", + "name": "panel_9", + "type": "visualization" + }, + { + "id": "a9427270-8619-11ea-91bc-ab084c7ec0e7", + "name": "panel_10", + "type": "visualization" + }, + { + "id": "7453b910-8624-11ea-91bc-ab084c7ec0e7", + "name": "panel_11", + "type": "visualization" + }, + { + "id": "8ee988d0-861b-11ea-91bc-ab084c7ec0e7", + "name": "panel_12", + "type": "visualization" + }, + { + "id": "1b4f8790-861a-11ea-91bc-ab084c7ec0e7", + "name": "panel_13", + "type": "visualization" + }, + { + "id": "31ed84b0-861b-11ea-91bc-ab084c7ec0e7", + "name": "panel_14", + "type": "visualization" + }, + { + "id": "54038fe0-861b-11ea-91bc-ab084c7ec0e7", + "name": "panel_15", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-04-24T12:15:15.363Z", + "version": "Wzk1OTcsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Navigation Website Overview [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "fontSize": 10, + "markdown": "### IIS\n\n[Webserver](#/dashboard/ebc23240-8572-11ea-91bc-ab084c7ec0e7)| [Webserver processes](#/dashboard/2c171500-858b-11ea-91bc-ab084c7ec0e7) | [**Websites**](#/dashboard/4b975820-85a1-11ea-91bc-ab084c7ec0e7) | [Application Pools](#/dashboard/b4108810-861c-11ea-91bc-ab084c7ec0e7) \n\n\n\n", + "openLinksInNewTab": false + }, + "title": "Navigation Website Overview [Metricbeat IIS]", + "type": "markdown" + } + }, + "id": "f9723710-8602-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T12:15:06.609Z", + "version": "Wzk1ODIsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Current Connections [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(104,188,0,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Current Connections", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.website.network.current_connections", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "terms_order_by": "_count", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "top_n" + }, + "title": "Website Current Connections [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "57d914d0-860e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:09:05.853Z", + "version": "Wzg3NTksMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Maximum Connections [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(104,204,202,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Maximum Connections", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.website.network.maximum_connections", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "terms_order_by": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "top_n" + }, + "title": "Website Maximum Connections [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "6db58c20-860e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:07:57.207Z", + "version": "Wzg3NDMsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Total Connection Attempts [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(251,158,0,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Total Connection Attempts", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.website.network.total_connection_attempts", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "terms_order_by": "_count", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "top_n" + }, + "title": "Website Total Connection Attempts [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "e3ee4990-860e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:08:45.818Z", + "version": "Wzg3NTMsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Filters [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "controls": [ + { + "fieldName": "iis.website.name", + "id": "1549397251041", + "indexPatternRefName": "control_0_index_pattern", + "label": "Website", + "options": { + "dynamicOptions": true, + "multiselect": true, + "order": "desc", + "size": 5, + "type": "terms" + }, + "parent": "", + "type": "list" + } + ], + "pinFilters": false, + "updateFiltersOnChange": true, + "useTimeFilter": false + }, + "title": "Website Filters [Metricbeat IIS]", + "type": "input_control_vis" + } + }, + "id": "b7230190-8603-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "control_0_index_pattern", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-04-24T09:47:17.191Z", + "version": "Wzg2NDUsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Service Uptime [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "background_color": null, + "background_color_rules": [ + { + "id": "71978870-32e4-11ea-af9e-d70582a45bda" + } + ], + "bar_color_rules": [ + { + "id": "f11cfd90-32e5-11ea-af9e-d70582a45bda" + } + ], + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "drilldown_url": "", + "filter": { + "language": "kuery", + "query": "" + }, + "gauge_color_rules": [ + { + "id": "9c09ed50-32e4-11ea-af9e-d70582a45bda" + } + ], + "gauge_inner_color": null, + "gauge_inner_width": "6", + "gauge_style": "circle", + "gauge_width": "10", + "id": "61fb4190-32e4-11ea-b9f8-4d0b340ad993", + "index_pattern": "metricbeat-*", + "interval": "60m", + "isModelInvalid": false, + "pivot_id": "cloud.instance.name", + "pivot_label": "Resource Name", + "pivot_rows": "30", + "pivot_type": "string", + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,49,21,1)", + "fill": "1.2", + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "s,d,", + "id": "61fb4191-32e4-11ea-b9f8-4d0b340ad993", + "label": "Service Uptime", + "line_width": 2, + "metrics": [ + { + "agg_with": "avg", + "field": "iis.website.network.service_uptime", + "id": "61fb4192-32e4-11ea-b9f8-4d0b340ad993", + "order": "desc", + "order_by": "@timestamp", + "size": 1, + "type": "top_hit" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "gradient", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "terms_order_by": "_count", + "terms_size": "10", + "type": "timeseries", + "value_template": "{{value}} d" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "time_range_mode": "entire_time_range", + "type": "top_n" + }, + "title": "Website Service Uptime [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "4557d670-860e-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:06:35.877Z", + "version": "Wzg3MjksMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Bytes Sent/s [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(252,196,0,1)", + "fill": "0", + "formatter": "bytes", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Bytes Sent", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.bytes_sent_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website Bytes Sent/s [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "c784f9b0-8614-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:17:19.307Z", + "version": "Wzg3ODgsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Bytes Received/s [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(252,196,0,1)", + "fill": "0", + "formatter": "bytes", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Bytes Received", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.bytes_received_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website Bytes Received/s [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "96fe7d70-8614-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:18:51.070Z", + "version": "Wzg3OTksMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website GET Requests/s [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(252,196,0,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "GET Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.get_requests_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website GET Requests/s [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "4921d5c0-8619-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:49:34.748Z", + "version": "Wzg4MjQsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website POST Requests/s [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(252,196,0,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "POST Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.post_requests_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website POST Requests/s [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "7dabd8e0-8619-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:51:02.893Z", + "version": "Wzg4MjksMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website PUT Requests/s [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(211,49,21,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "PUT Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.put_requests_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website PUT Requests/s [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "a9427270-8619-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T10:52:16.023Z", + "version": "Wzg4MzUsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website DELETE Requests/s [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(25,77,51,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "DELETE Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.delete_requests_per_sec", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}/s" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website DELETE Requests/s [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "7453b910-8624-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T12:09:31.681Z", + "version": "Wzk1MjEsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Total DELETE Requests [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(244,78,59,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Total DELETE Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.total_delete_requests", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website Total DELETE Requests [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "8ee988d0-861b-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:05:50.813Z", + "version": "Wzg4ODUsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Total GET Requests [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(104,188,0,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Total GET Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.total_get_requests", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website Total GET Requests [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "1b4f8790-861a-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:02:23.731Z", + "version": "Wzg4NjQsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Total POST Requests [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(104,188,0,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Total POST Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.total_post_requests", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website Total POST Requests [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "31ed84b0-861b-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:03:14.811Z", + "version": "Wzg4NjgsMTNd" + }, + { + "attributes": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Website Total PUT Requests [Metricbeat IIS]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "c9fd65d0-32e8-11ea-84f4-e9593f8ba8f6", + "index_pattern": "metricbeat-*", + "interval": "", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "rgba(22,165,165,1)", + "fill": "0", + "formatter": "number", + "id": "c9fd8ce0-32e8-11ea-84f4-e9593f8ba8f6", + "label": "Total PUT Requests ", + "line_width": "2", + "metrics": [ + { + "field": "iis.website.network.total_put_requests", + "id": "c9fd8ce1-32e8-11ea-84f4-e9593f8ba8f6", + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_color_mode": "rainbow", + "split_mode": "terms", + "stacked": "none", + "terms_field": "iis.website.name", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Website Total PUT Requests [Metricbeat IIS]", + "type": "metrics" + } + }, + "id": "54038fe0-861b-11ea-91bc-ab084c7ec0e7", + "migrationVersion": { + "visualization": "7.4.2" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-04-24T11:04:11.998Z", + "version": "Wzg4NzQsMTNd" + } + ], + "version": "7.6.0" +} diff --git a/x-pack/metricbeat/module/iis/application_pool/_meta/docs.asciidoc b/x-pack/metricbeat/module/iis/application_pool/_meta/docs.asciidoc index e85da0ee7e8..bb4c752b844 100644 --- a/x-pack/metricbeat/module/iis/application_pool/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/iis/application_pool/_meta/docs.asciidoc @@ -9,6 +9,12 @@ The `process` object contains System/Process counters like the the overall serve The `net_clr` object which returns ASP.NET error rate counter values. Users can specify the application pools they would like to monitor using the configuration option `application_pool.name`, else, all application pools are considered. +[float] +==== Dashboard + +image::./images/metricbeat-iis-application-pool-overview.png[] + + diff --git a/x-pack/metricbeat/module/iis/application_pool/reader.go b/x-pack/metricbeat/module/iis/application_pool/reader.go index 518d7231dca..2c137c42d65 100644 --- a/x-pack/metricbeat/module/iis/application_pool/reader.go +++ b/x-pack/metricbeat/module/iis/application_pool/reader.go @@ -49,7 +49,7 @@ var appPoolCounters = map[string]string{ "process.handle_count": "\\Process(w3wp*)\\Handle Count", "process.thread_count": "\\Process(w3wp*)\\Thread Count", "process.working_set": "\\Process(w3wp*)\\Working Set", - "process.private_byte": "\\Process(w3wp*)\\Private Bytes", + "process.private_bytes": "\\Process(w3wp*)\\Private Bytes", "process.virtual_bytes": "\\Process(w3wp*)\\Virtual Bytes", "process.page_faults_per_sec": "\\Process(w3wp*)\\Page Faults/sec", "process.io_read_operations_per_sec": "\\Process(w3wp*)\\IO Read Operations/sec", diff --git a/x-pack/metricbeat/module/iis/webserver/_meta/docs.asciidoc b/x-pack/metricbeat/module/iis/webserver/_meta/docs.asciidoc index 9b15b923985..9c77eb7de43 100644 --- a/x-pack/metricbeat/module/iis/webserver/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/iis/webserver/_meta/docs.asciidoc @@ -15,7 +15,12 @@ The `cache` object contains metrics from the user mode cache and output cache. The `asp_net` and `asp_net_application` contain asp.net related performance counter values. +[float] +==== Dashboard +image::./images/metricbeat-iis-webserver-overview.png[] + +image::./images/metricbeat-iis-webserver-process.png[] diff --git a/x-pack/metricbeat/module/iis/webserver/manifest.yml b/x-pack/metricbeat/module/iis/webserver/manifest.yml index 32da4b384b6..39648f054d8 100644 --- a/x-pack/metricbeat/module/iis/webserver/manifest.yml +++ b/x-pack/metricbeat/module/iis/webserver/manifest.yml @@ -33,8 +33,7 @@ input: - name: "Anonymous Users/sec" - name: "Total NonAnonymous Users" #asp.net - - object: "ASP.NET Applications" - instance: "__Total__" + - object: "ASP.NET" namespace: "asp_net" counters: - name: "Application Restarts" @@ -76,6 +75,7 @@ input: instance: "w3wp*" counters: - name: "% Processor Time" + field: "cpu_usage_perc" - name: "Handle Count" - name: "Thread Count" - name: "Working Set" diff --git a/x-pack/metricbeat/module/iis/website/_meta/docs.asciidoc b/x-pack/metricbeat/module/iis/website/_meta/docs.asciidoc index e4e6a5e9dc9..808afe381cf 100644 --- a/x-pack/metricbeat/module/iis/website/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/iis/website/_meta/docs.asciidoc @@ -7,6 +7,9 @@ The metrics contain the IIS Performance counter values like: Web Service: Current Connections (through experience with their apps app, users can identify what is a normal value for this) and others. +[float] +==== Dashboard +image::./images/metricbeat-iis-website-overview.png[] diff --git a/x-pack/metricbeat/module/iis/website/manifest.yml b/x-pack/metricbeat/module/iis/website/manifest.yml index 62ebfd27ed8..8492b584948 100644 --- a/x-pack/metricbeat/module/iis/website/manifest.yml +++ b/x-pack/metricbeat/module/iis/website/manifest.yml @@ -6,18 +6,18 @@ input: perfmon.group_measurements_by_instance: true perfmon.ignore_non_existent_counters: true perfmon.queries: - - object: 'Web Service' + - object: "Web Service" instance: "*" namespace : "network" counters: - - name: 'Total Bytes Received' - - name: 'Total Bytes Sent' + - name: "Total Bytes Received" + - name: "Total Bytes Sent" - name: "Bytes Sent/sec" - name: "Bytes Received/sec" - name: "Current Connections" - name: "Maximum Connections" - name: "Total Connection Attempts (all instances)" - field: total_connection_attempts + field: "total_connection_attempts" - name: "Total Get Requests" - name: "Get Requests/sec" - name: "Total Post Requests" @@ -30,7 +30,7 @@ input: processors: - drop_event.when.equals: - iis.website.name: '_Total' + iis.website.instance: '_Total' - drop_fields: fields: "iis.website.object" - rename: diff --git a/x-pack/metricbeat/module/mssql/_meta/docs.asciidoc b/x-pack/metricbeat/module/mssql/_meta/docs.asciidoc index 155e643b4a2..ad32544a465 100644 --- a/x-pack/metricbeat/module/mssql/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/mssql/_meta/docs.asciidoc @@ -1,4 +1,4 @@ -This is the https://www.microsoft.com/en-us/sql-server/sql-server-2017[Microsoft SQL 2017] Metricbeat module. It is still in beta and under active development to add new Metricsets and introduce enhancements. +This is the https://www.microsoft.com/en-us/sql-server/sql-server-2017[Microsoft SQL 2017] Metricbeat module. It is still under active development to add new Metricsets and introduce enhancements. [float] === Compatibility @@ -23,7 +23,7 @@ The following Metricsets are already included: [float] === Module-specific configuration notes -When configuring the `hosts` option, you can specify native user credentials +When configuring the `hosts` option, you can specify native user credentials as part of the host string with the following format: ---- @@ -45,4 +45,4 @@ metricbeat.modules: period: 10 ---- -Store sensitive values like passwords in the <>. \ No newline at end of file +Store sensitive values like passwords in the <>. diff --git a/x-pack/metricbeat/module/mssql/fields.go b/x-pack/metricbeat/module/mssql/fields.go index f3d9bcc13e2..55c8f0c787a 100644 --- a/x-pack/metricbeat/module/mssql/fields.go +++ b/x-pack/metricbeat/module/mssql/fields.go @@ -19,5 +19,5 @@ func init() { // AssetMssql returns asset data. // This is the base64 encoded gzipped contents of module/mssql. func AssetMssql() string { - return "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" + return "eJzMWU9v3LgPvedTED21QDK959bfb4HdAml3u0nPhkambSGy5Ij0TGc//YKSZ8bjsed/i+2hQWyLfHykHinlAV5x9Qg10Zu9A2DDFh/h3Zfn529P7+4AciQdTMPGu0f48gzP356g9nlr8Q4goEVF+AhzZHUHUBi0OT3eAQA8gFM1bi3LP141+Ahl8G3TPdkx/1IhBO852QHtHSvjjCtBWQsRUueiW9331/eZK1ZzRbh5MeZ61P16JXClGLhCqJGD0QSGYI6CJWCBIWAO7HuWhlD6cEy+83gNxnpXDl7s4PnuzFuL8Pk38EWEsgFnHJkcEyOjHuX/UZ+vuFr6MMSz4/arqnHocY/hBkPhQ62cPpPk3sI1tchQIOsKCYyLL+VTUHPfphT81Vvzf986xkA9+5sqLNWJCWlUiRk11jBlDYaMUF+Uoa9tPccgZIlFSBYlRCDU3uWpirzWbQBFMZaA1FqWJX6BobB+KTVlXI4/ohGabRyOQrdev2ZLdTvkYhACvrVII9jlhYnFXiFoZS0GYA8C4AjQljBk2juHWlzSRTBfPCsLbgNWjMKY0VEIHJQjNer+oJcD60Zgj7qW1QvMGOsmYzW3eBDANhuywAcVVpBWfYw/YKGCib+DcULC7EJYPeqygIR8VRENebO+NI6AWAXGHIrg61Q3G6/QeG+PVrhYmQB2jv9tLc/gpTIEuUcC5xmM07bNMaLBvM/KpcRaX/p2ck8eQy1C5xsMUfhGA7gQVkDt68bYZPhGikGsGGt0vDGPtEN21Ogkd71NZWqk8bUqIHAwZYkB8xn8jg6DsnZ1DyvfwlI57qRzs4I9zBGsXx6ppROinwhSZo3+6p0AP7vcaMU4HmMq+YgUtJcyU1zJ/IDSuTCuj/VHsAlIq5Ywh/lqy9CDxQXaQQpl/wu0ZwwLDDP4VDCG3pOkkFF9DK/EKUUFuQeWHbBQthUiVey2CohR5Z3PS2tsrlhX2bqDnMfyS6e0Dx3dtXJ5MogkkaNZjG1kwWuIjZYAVVGg5kSezInaO+KgjJTge2p1Ja3388c/7wd9hO5BCw9A5h+8j6mw+ENY88WmId6DICIP3n2YwR+mrBK8bcesUTkovZcWGXxbVk3LF1PZFgWG0cXDoQomRpyd6pfwssrw3heHrO6la7faGwwaHcu80809BIVvXS6lKR+kIDpul4YrUbdKLWTOYS/F17WG3NDrkKlDYfVDa/RYUHvQ44lCdo4UioDjgfgmlJWMbblZmDyV0fSX1vvXtqE4vMXPrCIZX5cgcZIUSxwFldZIhLTeoSpmX8gzPl9LxX2StYiv9gskWGBYgTXMFmfwP4yqEBkT8rb9NEIxBLVUt0UiwB8NOjKLeHBxu0uE5x0pNdQ5TTJambKaIDNVSe7bucXx0oXhWG1NgZnA0aycXt2y9LbJSGogChbZXhprRRRWgxqU/r4pwXhuQ6dxr8mfWnT7svYLQcN749YmPoxFACcITT8aXaF+bbxxnMVdPKHcJ4fXSYFtqYqH41h3/QOF6HPPK/gAnitZvB59ds4cUcxzE3jVmU7V2jkYY+Cc6Ncn2xT7dTFP5G97XtfeMbq9tgAH6+6caFiFEvnEWHLcEbapGK5GO3IMy6wvz7svGCzu3RnE/RMvDiauDbZlJbLcMyQjt0QuY9A2SVErVXfNZBzx4HLj/GsGapTGrCVVjl/GjEngTvDPYgCigZ0YC5+azyCmM8cDMk5jJg0smyv9elyNpZuqWob7KGcdOMyTpW07FHonLR5W/1NkeL7i0Sq/AV7ZBofMTx6VetUuc8N1Xe+lG0rXt3D7qf0pVA1c3oALIft6KrzWbWNi2n45J1O+ryAHzh9jPw2mbj6ETMUJI32/fp6GWXkrXx/EXFiveFzOWPH4fdhRIfsbuQ2OgNq6VjLixqOtYg5m3kpbVfH8sFU4mQUGgl3Eg7IvNopNM/geb+oN7alj7Z1hH+LfDlwOuVGl83JipMEFXzRdobJcDRveMf3s7vgmGB2580kL9ryLgelx5tco5TF8NxCDpLCZHHwmg80VH+XySSR7iLGTbzG+T2X/NkxOWFMpO0OXnta8pFYiENbWE2f41srYvp6z3j89fx2d2m+axqdhtq6GB6fltjdKbAf82zIsWe8dHv6DJF+D8ESeo47foHzTfj9ViH6C1BzUmBMI+jcAAP//oRLGKQ==" } diff --git a/x-pack/metricbeat/module/mssql/performance/_meta/data.json b/x-pack/metricbeat/module/mssql/performance/_meta/data.json index 7f409619ea2..656c0b525fd 100644 --- a/x-pack/metricbeat/module/mssql/performance/_meta/data.json +++ b/x-pack/metricbeat/module/mssql/performance/_meta/data.json @@ -1,45 +1,42 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "agent": { - "hostname": "host.example.com", - "name": "host.example.com" - }, "event": { "dataset": "mssql.performance", "duration": 115000, "module": "mssql" }, "metricset": { - "name": "performance" + "name": "performance", + "period": 10000 }, "mssql": { "performance": { "active_temp_tables": 0, - "batch_requests_per_sec": 24249, + "batch_requests_per_sec": 3355, "buffer": { "cache_hit": { - "pct": 0.14 + "pct": 0.22 }, - "checkpoint_pages_per_sec": 182, - "database_pages": 1892, + "checkpoint_pages_per_sec": 295, + "database_pages": 2152, "page_life_expectancy": { - "sec": 8201 + "sec": 1400 }, - "target_pages": 3194880 + "target_pages": 3178496 }, - "compilations_per_sec": 7379, - "connections_reset_per_sec": 2179, - "lock_waits_per_sec": 3, - "logins_per_sec": 7346, - "logouts_per_sec": 7339, - "page_splits_per_sec": 45, - "recompilations_per_sec": 0, + "compilations_per_sec": 1151, + "connections_reset_per_sec": 88, + "lock_waits_per_sec": 6, + "logins_per_sec": 1105, + "logouts_per_sec": 1103, + "page_splits_per_sec": 14, + "recompilations_per_sec": 2, "transactions": 0, - "user_connections": 7 + "user_connections": 2 } }, "service": { - "address": "172.26.0.2", + "address": "172.23.0.2:1433", "type": "mssql" } } \ No newline at end of file diff --git a/x-pack/metricbeat/module/mssql/performance/_meta/fields.yml b/x-pack/metricbeat/module/mssql/performance/_meta/fields.yml index 199be4830fd..4352c237015 100644 --- a/x-pack/metricbeat/module/mssql/performance/_meta/fields.yml +++ b/x-pack/metricbeat/module/mssql/performance/_meta/fields.yml @@ -1,7 +1,7 @@ - name: performance type: group description: performance metricset fetches information about the Performance Counters - release: beta + release: ga fields: - name: page_splits_per_sec type: long diff --git a/x-pack/metricbeat/module/mssql/performance/data_integration_test.go b/x-pack/metricbeat/module/mssql/performance/data_integration_test.go index f7753b98364..f0520a843bd 100644 --- a/x-pack/metricbeat/module/mssql/performance/data_integration_test.go +++ b/x-pack/metricbeat/module/mssql/performance/data_integration_test.go @@ -8,6 +8,9 @@ import ( "net/url" "testing" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/tests/compose" + _ "github.com/denisenkom/go-mssqldb" "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -17,23 +20,13 @@ import ( ) func TestData(t *testing.T) { - t.Skip("Skipping `data.json` generation test") + logp.TestingSetup() + service := compose.EnsureUp(t, "mssql") - _, config, err := getHostURI() - if err != nil { - t.Fatal("error getting config information", err.Error()) - } + f := mbtest.NewReportingMetricSetV2(t, mtest.GetConfig(service.Host(), "performance")) - f := mbtest.NewReportingMetricSetV2(t, config) - events, errs := mbtest.ReportingFetchV2(f) - if len(errs) > 0 { - t.Fatalf("Expected 0 error, had %d. %v\n", len(errs), errs) - } - assert.NotEmpty(t, events) - - if err = mbtest.WriteEventsReporterV2(f, t, ""); err != nil { - t.Fatal("write", err) - } + err := mbtest.WriteEventsReporterV2(f, t, "") + assert.NoError(t, err) } func getHostURI() (string, map[string]interface{}, error) { diff --git a/x-pack/metricbeat/module/mssql/performance/performance.go b/x-pack/metricbeat/module/mssql/performance/performance.go index faea7f09c2b..fb9c4b4c888 100644 --- a/x-pack/metricbeat/module/mssql/performance/performance.go +++ b/x-pack/metricbeat/module/mssql/performance/performance.go @@ -13,7 +13,6 @@ import ( "github.com/pkg/errors" - "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/x-pack/metricbeat/module/mssql" @@ -49,8 +48,6 @@ type MetricSet struct { // New creates a new instance of the MetricSet. New is responsible for unpacking // any MetricSet specific configuration options if there are any. func New(base mb.BaseMetricSet) (mb.MetricSet, error) { - cfgwarn.Beta("The mssql performance metricset is beta.") - logger := logp.NewLogger("mssql.performance").With("host", base.HostData().SanitizedURI) db, err := mssql.NewConnection(base.HostData().URI) @@ -71,40 +68,40 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { func (m *MetricSet) Fetch(reporter mb.ReporterV2) { var err error var rows *sql.Rows - rows, err = m.db.Query(`SELECT object_name, - counter_name, - instance_name, - cntr_value -FROM sys.dm_os_performance_counters -WHERE counter_name = 'SQL Compilations/sec' - OR counter_name = 'SQL Re-Compilations/sec' - OR counter_name = 'User Connections' - OR counter_name = 'Page splits/sec' - OR ( counter_name = 'Lock Waits/sec' - AND instance_name = '_Total' ) - OR counter_name = 'Page splits/sec' - OR ( object_name = 'SQLServer:Buffer Manager' - AND counter_name = 'Page life expectancy' ) - OR counter_name = 'Batch Requests/sec' - OR ( counter_name = 'Buffer cache hit ratio' - AND object_name = 'SQLServer:Buffer Manager' ) - OR ( counter_name = 'Target pages' - AND object_name = 'SQLServer:Buffer Manager' ) - OR ( counter_name = 'Database pages' - AND object_name = 'SQLServer:Buffer Manager' ) - OR ( counter_name = 'Checkpoint pages/sec' - AND object_name = 'SQLServer:Buffer Manager' ) - OR ( counter_name = 'Lock Waits/sec' - AND instance_name = '_Total' ) - OR ( counter_name = 'Transactions' - AND object_name = 'SQLServer:General Statistics' ) - OR ( counter_name = 'Logins/sec' - AND object_name = 'SQLServer:General Statistics' ) - OR ( counter_name = 'Logouts/sec' - AND object_name = 'SQLServer:General Statistics' ) - OR ( counter_name = 'Connection Reset/sec' - AND object_name = 'SQLServer:General Statistics' ) - OR ( counter_name = 'Active Temp Tables' + rows, err = m.db.Query(`SELECT object_name, + counter_name, + instance_name, + cntr_value +FROM sys.dm_os_performance_counters +WHERE counter_name = 'SQL Compilations/sec' + OR counter_name = 'SQL Re-Compilations/sec' + OR counter_name = 'User Connections' + OR counter_name = 'Page splits/sec' + OR ( counter_name = 'Lock Waits/sec' + AND instance_name = '_Total' ) + OR counter_name = 'Page splits/sec' + OR ( object_name = 'SQLServer:Buffer Manager' + AND counter_name = 'Page life expectancy' ) + OR counter_name = 'Batch Requests/sec' + OR ( counter_name = 'Buffer cache hit ratio' + AND object_name = 'SQLServer:Buffer Manager' ) + OR ( counter_name = 'Target pages' + AND object_name = 'SQLServer:Buffer Manager' ) + OR ( counter_name = 'Database pages' + AND object_name = 'SQLServer:Buffer Manager' ) + OR ( counter_name = 'Checkpoint pages/sec' + AND object_name = 'SQLServer:Buffer Manager' ) + OR ( counter_name = 'Lock Waits/sec' + AND instance_name = '_Total' ) + OR ( counter_name = 'Transactions' + AND object_name = 'SQLServer:General Statistics' ) + OR ( counter_name = 'Logins/sec' + AND object_name = 'SQLServer:General Statistics' ) + OR ( counter_name = 'Logouts/sec' + AND object_name = 'SQLServer:General Statistics' ) + OR ( counter_name = 'Connection Reset/sec' + AND object_name = 'SQLServer:General Statistics' ) + OR ( counter_name = 'Active Temp Tables' AND object_name = 'SQLServer:General Statistics' )`) if err != nil { reporter.Error(errors.Wrapf(err, "error closing rows")) diff --git a/x-pack/metricbeat/module/mssql/performance/performance_integration_test.go b/x-pack/metricbeat/module/mssql/performance/performance_integration_test.go index 884bfe72763..5e039265d2b 100644 --- a/x-pack/metricbeat/module/mssql/performance/performance_integration_test.go +++ b/x-pack/metricbeat/module/mssql/performance/performance_integration_test.go @@ -81,7 +81,7 @@ func TestFetch(t *testing.T) { {key: "connections_reset_per_sec", assertion: int64Assertion(int64HigherThanZero)}, {key: "logouts_per_sec", assertion: int64Assertion(int64HigherThanZero)}, {key: "logins_per_sec", assertion: int64Assertion(int64HigherThanZero)}, - {key: "recompilations_per_sec", assertion: int64Assertion(int64EqualZero)}, + {key: "recompilations_per_sec", assertion: int64Assertion(int64EqualOrHigherThanZero)}, {key: "compilations_per_sec", assertion: int64Assertion(int64HigherThanZero)}, {key: "batch_requests_per_sec", assertion: int64Assertion(int64HigherThanZero)}, {key: "buffer.cache_hit.pct", assertion: float64Assertion(float64HigherThanZero)}, diff --git a/x-pack/metricbeat/module/mssql/transaction_log/_meta/data.json b/x-pack/metricbeat/module/mssql/transaction_log/_meta/data.json index ce46579d561..186c154ee98 100644 --- a/x-pack/metricbeat/module/mssql/transaction_log/_meta/data.json +++ b/x-pack/metricbeat/module/mssql/transaction_log/_meta/data.json @@ -1,16 +1,13 @@ { "@timestamp": "2017-10-12T08:05:34.853Z", - "agent": { - "hostname": "host.example.com", - "name": "host.example.com" - }, "event": { "dataset": "mssql.transaction_log", "duration": 115000, "module": "mssql" }, "metricset": { - "name": "transaction_log" + "name": "transaction_log", + "period": 10000 }, "mssql": { "database": { @@ -20,26 +17,26 @@ "transaction_log": { "space_usage": { "since_last_backup": { - "bytes": 135168 + "bytes": 937984 }, "total": { "bytes": 2088960 }, "used": { - "bytes": 622592, - "pct": 29.80392074584961 + "bytes": 1318912, + "pct": 63.13725662231445 } }, "stats": { "active_size": { - "bytes": 135167.737856 + "bytes": 937983.737856 }, "backup_time": "1900-01-01T00:00:00Z", "recovery_size": { - "bytes": 0.128906 + "bytes": 0.894531 }, "since_last_checkpoint": { - "bytes": 135167.737856 + "bytes": 937983.737856 }, "total_size": { "bytes": 2088959.475712 @@ -48,7 +45,7 @@ } }, "service": { - "address": "172.26.0.2", + "address": "172.23.0.2:1433", "type": "mssql" } } \ No newline at end of file diff --git a/x-pack/metricbeat/module/mssql/transaction_log/_meta/fields.yml b/x-pack/metricbeat/module/mssql/transaction_log/_meta/fields.yml index 5ba421ba079..c03df4084d0 100644 --- a/x-pack/metricbeat/module/mssql/transaction_log/_meta/fields.yml +++ b/x-pack/metricbeat/module/mssql/transaction_log/_meta/fields.yml @@ -1,7 +1,7 @@ - name: transaction_log type: group description: transaction_log metricset will fetch information about the operation and transaction log of each database from a MSSQL instance - release: beta + release: ga fields: - name: space_usage type: group diff --git a/x-pack/metricbeat/module/mssql/transaction_log/data_integration_test.go b/x-pack/metricbeat/module/mssql/transaction_log/data_integration_test.go index fcdad6ccb44..16f5c16eac6 100644 --- a/x-pack/metricbeat/module/mssql/transaction_log/data_integration_test.go +++ b/x-pack/metricbeat/module/mssql/transaction_log/data_integration_test.go @@ -7,14 +7,18 @@ package transaction_log import ( "testing" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/tests/compose" + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" mtest "github.com/elastic/beats/v7/x-pack/metricbeat/module/mssql/testing" ) func TestData(t *testing.T) { - t.Skip("Skipping `data.json` generation test") + logp.TestingSetup() + service := compose.EnsureUp(t, "mssql") - f := mbtest.NewReportingMetricSetV2(t, mtest.GetConfig("transaction_log")) + f := mbtest.NewReportingMetricSetV2(t, mtest.GetConfig(service.Host(), "transaction_log")) err := mbtest.WriteEventsReporterV2(f, t, "") if err != nil { diff --git a/x-pack/metricbeat/module/mssql/transaction_log/transaction_log.go b/x-pack/metricbeat/module/mssql/transaction_log/transaction_log.go index 6047d0161b4..a974ffdab9b 100644 --- a/x-pack/metricbeat/module/mssql/transaction_log/transaction_log.go +++ b/x-pack/metricbeat/module/mssql/transaction_log/transaction_log.go @@ -11,7 +11,6 @@ import ( "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/x-pack/metricbeat/module/mssql" @@ -55,8 +54,6 @@ type MetricSet struct { // New create a new instance of the MetricSet func New(base mb.BaseMetricSet) (mb.MetricSet, error) { - cfgwarn.Beta("The mssql transaction_log metricset is beta.") - logger := logp.NewLogger("mssql.transaction_log").With("host", base.HostData().SanitizedURI) db, err := mssql.NewConnection(base.HostData().URI) @@ -127,7 +124,7 @@ func (m *MetricSet) Close() error { func (m *MetricSet) getLogSpaceUsageForDb(dbName string) (common.MapStr, error) { // According to MS docs a single result is always returned for this query - row := m.db.QueryRow(fmt.Sprintf(`USE %s; SELECT * FROM sys.dm_db_log_space_usage;`, dbName)) + row := m.db.QueryRow(fmt.Sprintf(`USE [%s]; SELECT * FROM sys.dm_db_log_space_usage;`, dbName)) var res logSpace if err := row.Scan(&res.id, &res.totalLogSizeInBytes, &res.usedLogSpaceInBytes, &res.usedLogSpaceInPercent, @@ -153,7 +150,7 @@ func (m *MetricSet) getLogSpaceUsageForDb(dbName string) (common.MapStr, error) } func (m *MetricSet) getLogStats(db dbInfo) (common.MapStr, error) { // According to MS docs a single result is always returned for this query - row := m.db.QueryRow(fmt.Sprintf(`USE %s; SELECT database_id,total_log_size_mb,active_log_size_mb,log_backup_time,log_since_last_log_backup_mb,log_since_last_checkpoint_mb,log_recovery_size_mb FROM sys.dm_db_log_stats(%d);`, db.name, db.id)) + row := m.db.QueryRow(fmt.Sprintf(`USE [%s]; SELECT database_id,total_log_size_mb,active_log_size_mb,log_backup_time,log_since_last_log_backup_mb,log_since_last_checkpoint_mb,log_recovery_size_mb FROM sys.dm_db_log_stats(%d);`, db.name, db.id)) var res logStats if err := row.Scan(&res.databaseID, &res.sizeMB, &res.activeSizeMB, &res.backupTime, &res.sinceLastBackupMB, &res.sinceLastCheckpointMB, &res.recoverySizeMB); err != nil { diff --git a/x-pack/metricbeat/module/oracle/_meta/config.yml b/x-pack/metricbeat/module/oracle/_meta/config.yml index b36667b5733..8208685e033 100644 --- a/x-pack/metricbeat/module/oracle/_meta/config.yml +++ b/x-pack/metricbeat/module/oracle/_meta/config.yml @@ -2,7 +2,7 @@ metricsets: ["tablespace", "performance"] enabled: true period: 10s - hosts: ["oracle://user:pass@localhost:1521/ORCLPDB1.localdomain?sysdba=1"] + hosts: ["user:pass@0.0.0.0:1521/ORCLPDB1.localdomain"] # username: "" # password: "" diff --git a/x-pack/metricbeat/module/oracle/connection.go b/x-pack/metricbeat/module/oracle/connection.go index f2fc870fb8d..8beb7c936ad 100644 --- a/x-pack/metricbeat/module/oracle/connection.go +++ b/x-pack/metricbeat/module/oracle/connection.go @@ -50,10 +50,6 @@ func NewConnection(c *ConnectionDetails) (*sql.DB, error) { params.Password = c.Password } - if params.IsSysDBA == false { - return nil, errors.New("a user with DBA permissions are required, check your connection details on field `hosts`") - } - db, err := sql.Open("godror", params.StringWithPassword()) if err != nil { return nil, errors.Wrap(err, "could not open database") diff --git a/x-pack/metricbeat/module/oracle/testing.go b/x-pack/metricbeat/module/oracle/testing.go index 5ffe9cd83f4..5a177f9d823 100644 --- a/x-pack/metricbeat/module/oracle/testing.go +++ b/x-pack/metricbeat/module/oracle/testing.go @@ -28,7 +28,7 @@ func GetOracleEnvServiceName() string { serviceName := os.Getenv("ORACLE_SERVICE_NAME") if len(serviceName) == 0 { - serviceName = "ORCLPDB1.localdomain" + serviceName = "ORCLCDB.localdomain" } return serviceName } diff --git a/x-pack/metricbeat/modules.d/googlecloud.yml.disabled b/x-pack/metricbeat/modules.d/googlecloud.yml.disabled index fc7d792dadf..cd49fdc146f 100644 --- a/x-pack/metricbeat/modules.d/googlecloud.yml.disabled +++ b/x-pack/metricbeat/modules.d/googlecloud.yml.disabled @@ -4,13 +4,21 @@ - module: googlecloud metricsets: - compute + region: "us-central1" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 300s + +- module: googlecloud + metricsets: - pubsub - loadbalancing zone: "us-central1-a" project_id: "your project id" credentials_file_path: "your JSON credentials file path" exclude_labels: false - period: 300s + period: 60s - module: googlecloud metricsets: @@ -20,3 +28,12 @@ credentials_file_path: "your JSON credentials file path" exclude_labels: false period: 300s + +- module: googlecloud + metricsets: + - compute + region: "us-" + project_id: "your project id" + credentials_file_path: "your JSON credentials file path" + exclude_labels: false + period: 60s diff --git a/x-pack/metricbeat/modules.d/oracle.yml.disabled b/x-pack/metricbeat/modules.d/oracle.yml.disabled index 219a27f0fe7..04beb96f9b7 100644 --- a/x-pack/metricbeat/modules.d/oracle.yml.disabled +++ b/x-pack/metricbeat/modules.d/oracle.yml.disabled @@ -5,7 +5,7 @@ metricsets: ["tablespace", "performance"] enabled: true period: 10s - hosts: ["oracle://user:pass@localhost:1521/ORCLPDB1.localdomain?sysdba=1"] + hosts: ["user:pass@0.0.0.0:1521/ORCLPDB1.localdomain"] # username: "" # password: "" diff --git a/x-pack/metricbeat/tests/system/test_xpack_base.py b/x-pack/metricbeat/tests/system/test_xpack_base.py index 69d07b61354..225ad779f0d 100644 --- a/x-pack/metricbeat/tests/system/test_xpack_base.py +++ b/x-pack/metricbeat/tests/system/test_xpack_base.py @@ -5,5 +5,4 @@ class Test(xpack_metricbeat.XPackTest, test_base.Test): - def kibana_dir(self): - return os.path.join(self.beat_path, 'build', 'kibana') + pass diff --git a/x-pack/winlogbeat/Makefile b/x-pack/winlogbeat/Makefile index 5da45563104..022bb515c23 100644 --- a/x-pack/winlogbeat/Makefile +++ b/x-pack/winlogbeat/Makefile @@ -7,4 +7,4 @@ GOX_OS := windows # # Includes # -include ../../dev-tools/make/xpack.mk +include ../../dev-tools/make/mage.mk diff --git a/x-pack/winlogbeat/_meta/beat.yml.tmpl b/x-pack/winlogbeat/_meta/config/winlogbeat.event_logs.yml.tmpl similarity index 50% rename from x-pack/winlogbeat/_meta/beat.yml.tmpl rename to x-pack/winlogbeat/_meta/config/winlogbeat.event_logs.yml.tmpl index f2660df68bd..6c29d94f6db 100644 --- a/x-pack/winlogbeat/_meta/beat.yml.tmpl +++ b/x-pack/winlogbeat/_meta/config/winlogbeat.event_logs.yml.tmpl @@ -1,4 +1,3 @@ -{{ template "header" . }} winlogbeat.event_logs: - name: Application ignore_older: 72h @@ -19,4 +18,16 @@ winlogbeat.event_logs: id: sysmon file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js -{{if not .Reference}}{{ template "elasticsearch_settings" . }}{{end}} + - name: ForwardedEvents + tags: [forwarded] + processors: + - script: + when.equals.winlog.channel: Security + lang: javascript + id: security + file: ${path.home}/module/security/config/winlogbeat-security.js + - script: + when.equals.winlog.channel: Microsoft-Windows-Sysmon/Operational + lang: javascript + id: sysmon + file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js diff --git a/x-pack/winlogbeat/module/security/_meta/docs.asciidoc b/x-pack/winlogbeat/module/security/_meta/docs.asciidoc index c7e7415244e..30d2d04fe3a 100644 --- a/x-pack/winlogbeat/module/security/_meta/docs.asciidoc +++ b/x-pack/winlogbeat/module/security/_meta/docs.asciidoc @@ -19,8 +19,16 @@ The module has transformations for the following event IDs: * 4647 - User initiated logoff (interactive logon types). * 4648 - A logon was attempted using explicit credentials. * 4672 - Special privileges assigned to new logon. +* 4673 - A privileged service was called. +* 4674 - An operation was attempted on a privileged object. * 4688 - A new process has been created. * 4689 - A process has exited. +* 4697 - A service was installed in the system. +* 4698 - A scheduled task was created. +* 4699 - A scheduled task was deleted. +* 4700 - A scheduled task was enabled. +* 4701 - A scheduled task was disabled. +* 4702 - A scheduled task was updated. * 4719 - System audit policy was changed. * 4720 - A user account was created. * 4722 - A user account was enabled. @@ -32,7 +40,7 @@ The module has transformations for the following event IDs: * 4728 - A member was added to a security-enabled global group. * 4729 - A member was removed from a security-enabled global group. * 4730 - A security-enabled global group was deleted. -* 4731 - A security-enabled local group was created +* 4731 - A security-enabled local group was created. * 4732 - A member was added to a security-enabled local group. * 4733 - A member was removed from a security-enabled local group. * 4734 - A security-enabled local group was deleted. @@ -65,9 +73,41 @@ The module has transformations for the following event IDs: * 4763 - A security-disabled global group was deleted. * 4764 - A group's type was changed. * 4767 - An account was unlocked. +* 4741 - A computer account was created. +* 4742 - A computer account was changed. +* 4743 - A computer account was deleted. +* 4744 - A security-disabled local group was created. +* 4745 - A security-disabled local group was changed. +* 4746 - A member was added to a security-disabled local group. +* 4747 - A member was removed from a security-disabled local group. +* 4748 - A security-disabled local group was deleted. +* 4749 - A security-disabled global group was created. +* 4750 - A security-disabled global group was changed. +* 4751 - A member was added to a security-disabled global group. +* 4752 - A member was removed from a security-disabled global group. +* 4753 - A security-disabled global group was deleted. +* 4754 - A security-enabled universal group was created. +* 4755 - A security-enabled universal group was changed. +* 4756 - A member was added to a security-enabled universal group. +* 4757 - A member was removed from a security-enabled universal group. +* 4758 - A security-enabled universal group was deleted. +* 4759 - A security-disabled universal group was created. +* 4760 - A security-disabled universal group was changed. +* 4761 - A member was added to a security-disabled universal group. +* 4762 - A member was removed from a security-disabled universal group. +* 4763 - A security-disabled global group was deleted. +* 4764 - A group's type was changed. +* 4768 - A Kerberos authentication ticket TGT was requested. +* 4769 - A Kerberos service ticket was requested. +* 4770 - A Kerberos service ticket was renewed. +* 4771 - Kerberos pre-authentication failed. +* 4776 - The computer attempted to validate the credentials for an account. +* 4778 - A session was reconnected to a Window Station. +* 4779 - A session was disconnected from a Window Station. * 4781 - The name of an account was changed. * 4798 - A user's local group membership was enumerated. * 4799 - A security-enabled local group membership was enumerated. +* 4964 - Special groups have been assigned to a new logon. More event IDs will be added. diff --git a/x-pack/winlogbeat/module/security/config/winlogbeat-security.js b/x-pack/winlogbeat/module/security/config/winlogbeat-security.js index b6cac040b74..f223b8f0b8d 100644 --- a/x-pack/winlogbeat/module/security/config/winlogbeat-security.js +++ b/x-pack/winlogbeat/module/security/config/winlogbeat-security.js @@ -7,6 +7,8 @@ var security = (function () { var processor = require("processor"); var winlogbeat = require("winlogbeat"); + // Logon Types + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/basic-audit-logon-events var logonTypes = { "2": "Interactive", "3": "Network", @@ -19,96 +21,237 @@ var security = (function () { "11": "CachedInteractive", }; + // ECS Allowed Event Outcome + // https://www.elastic.co/guide/en/ecs/current/ecs-allowed-values-event-outcome.html + var eventOutcomes = { + "Audit Success": "success", + "Audit Failure": "failure", + }; + // User Account Control Attributes Table // https://support.microsoft.com/es-us/help/305144/how-to-use-useraccountcontrol-to-manipulate-user-account-properties var uacFlags = [ - [0x0001, "SCRIPT"], - [0x0002, "ACCOUNTDISABLE"], - [0x0008, "HOMEDIR_REQUIRED"], - [0x0010, "LOCKOUT"], - [0x0020, "PASSWD_NOTREQD"], - [0x0040, "PASSWD_CANT_CHANGE"], - [0x0080, "ENCRYPTED_TEXT_PWD_ALLOWED"], - [0x0100, "TEMP_DUPLICATE_ACCOUNT"], - [0x0200, "NORMAL_ACCOUNT"], - [0x0800, "INTERDOMAIN_TRUST_ACCOUNT"], - [0x1000, "WORKSTATION_TRUST_ACCOUNT"], - [0x2000, "SERVER_TRUST_ACCOUNT"], - [0x10000, "DONT_EXPIRE_PASSWORD"], - [0x20000, "MNS_LOGON_ACCOUNT"], - [0x40000, "SMARTCARD_REQUIRED"], - [0x80000, "TRUSTED_FOR_DELEGATION"], - [0x100000, "NOT_DELEGATED"], - [0x200000, "USE_DES_KEY_ONLY"], - [0x400000, "DONT_REQ_PREAUTH"], - [0x800000, "PASSWORD_EXPIRED"], - [0x1000000, "TRUSTED_TO_AUTH_FOR_DELEGATION"], - [0x4000000, "PARTIAL_SECRETS_ACCOUNT"], + [0x0001, 'SCRIPT'], + [0x0002, 'ACCOUNTDISABLE'], + [0x0008, 'HOMEDIR_REQUIRED'], + [0x0010, 'LOCKOUT'], + [0x0020, 'PASSWD_NOTREQD'], + [0x0040, 'PASSWD_CANT_CHANGE'], + [0x0080, 'ENCRYPTED_TEXT_PWD_ALLOWED'], + [0x0100, 'TEMP_DUPLICATE_ACCOUNT'], + [0x0200, 'NORMAL_ACCOUNT'], + [0x0800, 'INTERDOMAIN_TRUST_ACCOUNT'], + [0x1000, 'WORKSTATION_TRUST_ACCOUNT'], + [0x2000, 'SERVER_TRUST_ACCOUNT'], + [0x10000, 'DONT_EXPIRE_PASSWORD'], + [0x20000, 'MNS_LOGON_ACCOUNT'], + [0x40000, 'SMARTCARD_REQUIRED'], + [0x80000, 'TRUSTED_FOR_DELEGATION'], + [0x100000, 'NOT_DELEGATED'], + [0x200000, 'USE_DES_KEY_ONLY'], + [0x400000, 'DONT_REQ_PREAUTH'], + [0x800000, 'PASSWORD_EXPIRED'], + [0x1000000, 'TRUSTED_TO_AUTH_FOR_DELEGATION'], + [0x04000000, 'PARTIAL_SECRETS_ACCOUNT'], ]; - // event.action Description Table - // event.action Description Table + // Kerberos TGT and TGS Ticket Options + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4768 + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4769 + var ticketOptions = [ + "Reserved", + "Forwardable", + "Forwarded", + "Proxiable", + "Proxy", + "Allow-postdate", + "Postdated", + "Invalid", + "Renewable", + "Initial", + "Pre-authent", + "Opt-hardware-auth", + "Transited-policy-checked", + "Ok-as-delegate", + "Request-anonymous", + "Name-canonicalize", + "Unused", + "Unused", + "Unused", + "Unused", + "Unused", + "Unused", + "Unused", + "Unused", + "Unused", + "Unused", + "Disable-transited-check", + "Renewable-ok", + "Enc-tkt-in-skey", + "Unused", + "Renew", + "Validate"]; + + // Kerberos Encryption Types + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4768 + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4768 + var ticketEncryptionTypes = { + "0x1": "DES-CBC-CRC", + "0x3": "DES-CBC-MD5", + "0x11": "AES128-CTS-HMAC-SHA1-96", + "0x12": "AES256-CTS-HMAC-SHA1-96", + "0x17": "RC4-HMAC", + "0x18": "RC4-HMAC-EXP", + "0xffffffff": "FAIL", + }; + + // Kerberos Result Status Codes + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4768 + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4768 + var kerberosTktStatusCodes = { + "0x0": "KDC_ERR_NONE", + "0x1": "KDC_ERR_NAME_EXP", + "0x2": "KDC_ERR_SERVICE_EXP", + "0x3": "KDC_ERR_BAD_PVNO", + "0x4": "KDC_ERR_C_OLD_MAST_KVNO", + "0x5": "KDC_ERR_S_OLD_MAST_KVNO", + "0x6": "KDC_ERR_C_PRINCIPAL_UNKNOWN", + "0x7": "KDC_ERR_S_PRINCIPAL_UNKNOWN", + "0x8": "KDC_ERR_PRINCIPAL_NOT_UNIQUE", + "0x9": "KDC_ERR_NULL_KEY", + "0xA": "KDC_ERR_CANNOT_POSTDATE", + "0xB": "KDC_ERR_NEVER_VALID", + "0xC": "KDC_ERR_POLICY", + "0xD": "KDC_ERR_BADOPTION", + "0xE": "KDC_ERR_ETYPE_NOTSUPP", + "0xF": "KDC_ERR_SUMTYPE_NOSUPP", + "0x10": "KDC_ERR_PADATA_TYPE_NOSUPP", + "0x11": "KDC_ERR_TRTYPE_NO_SUPP", + "0x12": "KDC_ERR_CLIENT_REVOKED", + "0x13": "KDC_ERR_SERVICE_REVOKED", + "0x14": "KDC_ERR_TGT_REVOKED", + "0x15": "KDC_ERR_CLIENT_NOTYET", + "0x16": "KDC_ERR_SERVICE_NOTYET", + "0x17": "KDC_ERR_KEY_EXPIRED", + "0x18": "KDC_ERR_PREAUTH_FAILED", + "0x19": "KDC_ERR_PREAUTH_REQUIRED", + "0x1A": "KDC_ERR_SERVER_NOMATCH", + "0x1B": "KDC_ERR_MUST_USE_USER2USER", + "0x1F": "KRB_AP_ERR_BAD_INTEGRITY", + "0x20": "KRB_AP_ERR_TKT_EXPIRED", + "0x21": "KRB_AP_ERR_TKT_NYV", + "0x22": "KRB_AP_ERR_REPEAT", + "0x23": "KRB_AP_ERR_NOT_US", + "0x24": "KRB_AP_ERR_BADMATCH", + "0x25": "KRB_AP_ERR_SKEW", + "0x26": "KRB_AP_ERR_BADADDR", + "0x27": "KRB_AP_ERR_BADVERSION", + "0x28": "KRB_AP_ERR_MSG_TYPE", + "0x29": "KRB_AP_ERR_MODIFIED", + "0x2A": "KRB_AP_ERR_BADORDER", + "0x2C": "KRB_AP_ERR_BADKEYVER", + "0x2D": "KRB_AP_ERR_NOKEY", + "0x2E": "KRB_AP_ERR_MUT_FAIL", + "0x2F": "KRB_AP_ERR_BADDIRECTION", + "0x30": "KRB_AP_ERR_METHOD", + "0x31": "KRB_AP_ERR_BADSEQ", + "0x32": "KRB_AP_ERR_INAPP_CKSUM", + "0x33": "KRB_AP_PATH_NOT_ACCEPTED", + "0x34": "KRB_ERR_RESPONSE_TOO_BIG", + "0x3C": "KRB_ERR_GENERIC", + "0x3D": "KRB_ERR_FIELD_TOOLONG", + "0x3E": "KDC_ERR_CLIENT_NOT_TRUSTED", + "0x3F": "KDC_ERR_KDC_NOT_TRUSTED", + "0x40": "KDC_ERR_INVALID_SIG", + "0x41": "KDC_ERR_KEY_TOO_WEAK", + "0x42": "KRB_AP_ERR_USER_TO_USER_REQUIRED", + "0x43": "KRB_AP_ERR_NO_TGT", + "0x44": "KDC_ERR_WRONG_REALM", + }; + + // event.category, event.type, event.action var eventActionTypes = { - "1100": "logging-service-shutdown", - "1102": "changed-audit-config", - "1104": "logging-full", - "1105": "auditlog-archieved", - "1108": "logging-processing-error", - "4624": "logged-in", - "4625": "logon-failed", - "4634": "logged-out", - "4672": "logged-in-special", - "4688": "created-process", - "4689": "exited-process", - "4719": "changed-audit-config", - "4720": "added-user-account", - "4722": "enabled-user-account", - "4723": "changed-password", - "4724": "reset-password", - "4725": "disabled-user-account", - "4726": "deleted-user-account", - "4727": "added-group-account", - "4728": "added-member-to-group", - "4729": "removed-member-from-group", - "4730": "deleted-group-account", - "4731": "added-member-to-group", - "4732": "added-member-to-group", - "4733": "removed-member-from-group", - "4734": "deleted-group-account", - "4735": "modified-group-account", - "4737": "modified-group-account", - "4738": "modified-user-account", - "4740": "locked-out-user-account", - "4741": "added-computer-account", - "4742": "changed-computer-account", - "4743": "deleted-computer-account", - "4744": "added-distribution-group-account", - "4745": "changed-distribution-group-account", - "4746": "added-member-to-distribution-group", - "4747": "removed-member-from-distribution-group", - "4748": "deleted-distribution-group-account", - "4749": "added-distribution-group-account", - "4750": "changed-distribution-group-account", - "4751": "added-member-to-distribution-group", - "4752": "removed-member-from-distribution-group", - "4753": "deleted-distribution-group-account", - "4754": "added-group-account", - "4755": "modified-group-account", - "4756": "added-member-to-group", - "4757": "removed-member-from-group", - "4758": "deleted-group-account", - "4759": "added-distribution-group-account", - "4760": "changed-distribution-group-account", - "4761": "added-member-to-distribution-group", - "4762": "removed-member-from-distribution-group", - "4763": "deleted-distribution-group-account", - "4764": "type-changed-group-account", - "4767": "unlocked-user-account", - "4781": "renamed-user-account", - "4798": "group-membership-enumerated", - "4799": "user-member-enumerated", + "1100": ["process","end","logging-service-shutdown"], + "1102": ["iam", "admin", "audit-log-cleared"], + "1104": ["iam","admin","logging-full"], + "1105": ["iam","admin","auditlog-archieved"], + "1108": ["iam","admin","logging-processing-error"], + "4624": ["authentication","start","logged-in"], + "4625": ["authentication","start","logon-failed"], + "4634": ["authentication","end","logged-out"], + "4647": ["authentication","end","logged-out"], + "4648": ["authentication","start","logged-in-explicit"], + "4672": ["iam","admin","logged-in-special"], + "4673": ["iam","admin","privileged-service-called"], + "4674": ["iam","admin","privileged-operation"], + "4688": ["process","start","created-process"], + "4689": ["process", "end", "exited-process"], + "4697": ["iam","admin","service-installed"], + "4698": ["iam","creation","scheduled-task-created"], + "4699": ["iam","deletion","scheduled-task-deleted"], + "4700": ["iam","change","scheduled-task-enabled"], + "4701": ["iam","change","scheduled-task-disabled"], + "4702": ["iam","change","scheduled-task-updated"], + "4719": ["iam","admin","changed-audit-config"], + "4720": ["iam","creation","added-user-account"], + "4722": ["iam","creation","enabled-user-account"], + "4723": ["iam","change","changed-password"], + "4724": ["iam","change","reset-password"], + "4725": ["iam","deletion","disabled-user-account"], + "4726": ["iam","deletion","deleted-user-account"], + "4727": ["iam","creation","added-group-account"], + "4728": ["iam","change","added-member-to-group"], + "4729": ["iam","change","removed-member-from-group"], + "4730": ["iam","deletion","deleted-group-account"], + "4731": ["iam","creation","added-group-account"], + "4732": ["iam","change","added-member-to-group"], + "4733": ["iam","change","removed-member-from-group"], + "4734": ["iam","deletion","deleted-group-account"], + "4735": ["iam","change","modified-group-account"], + "4737": ["iam","change","modified-group-account"], + "4738": ["iam","change","modified-user-account"], + "4740": ["iam","change","locked-out-user-account"], + "4741": ["iam","creation","added-computer-account"], + "4742": ["iam","change","changed-computer-account"], + "4743": ["iam","deletion","deleted-computer-account"], + "4744": ["iam","creation","added-distribution-group-account"], + "4745": ["iam","change","changed-distribution-group-account"], + "4746": ["iam","change","added-member-to-distribution-group"], + "4747": ["iam","change","removed-member-from-distribution-group"], + "4748": ["iam","deletion","deleted-distribution-group-account"], + "4749": ["iam","creation","added-distribution-group-account"], + "4750": ["iam","change","changed-distribution-group-account"], + "4751": ["iam","change","added-member-to-distribution-group"], + "4752": ["iam","change","removed-member-from-distribution-group"], + "4753": ["iam","deletion","deleted-distribution-group-account"], + "4754": ["iam","creation","added-group-account"], + "4755": ["iam","change","modified-group-account"], + "4756": ["iam","change","added-member-to-group"], + "4757": ["iam","change","removed-member-from-group"], + "4758": ["iam","deletion","deleted-group-account"], + "4759": ["iam","creation","added-distribution-group-account"], + "4760": ["iam","change","changed-distribution-group-account"], + "4761": ["iam","change","added-member-to-distribution-group"], + "4762": ["iam","change","removed-member-from-distribution-group"], + "4763": ["iam","deletion","deleted-distribution-group-account"], + "4764": ["iam","change","type-changed-group-account"], + "4767": ["iam","change","unlocked-user-account"], + "4768": ["authentication","start","kerberos-authentication-ticket-requested"], + "4769": ["authentication","start","kerberos-service-ticket-requested"], + "4770": ["authentication","start","kerberos-service-ticket-renewed"], + "4771": ["authentication","start","kerberos-preauth-failed"], + "4776": ["authentication","start","credential-validated"], + "4778": ["authentication","start","session-reconnected"], + "4779": ["authentication","end","session-disconnected"], + "4781": ["iam","change","renamed-user-account","dummy"], + "4798": ["iam","info","group-membership-enumerated"], + "4799": ["iam","info","user-member-enumerated","dummy"], + "4964": ["iam","admin","logged-in-special"], }; + + // Audit Policy Changes Table + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4719 var auditActions = { "8448": "Success Removed", "8450": "Failure Removed", @@ -116,68 +259,85 @@ var security = (function () { "8451": "Failure Added", }; + // Services Types + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4697 + var serviceTypes = { + "0x1": "Kernel Driver", + "0x2": "File System Driver", + "0x8": "Recognizer Driver", + "0x10": "Win32 Own Process", + "0x20": "Win32 Share Process", + "0x110": "Interactive Own Process", + "0x120": "Interactive Share Process", + }; + + + // Audit Categories Description + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpac/77878370-0712-47cd-997d-b07053429f6d var auditDescription = { - "0CCE9210-69AE-11D9-BED3-505054503030": ["Security State Change", "System"], - "0CCE9211-69AE-11D9-BED3-505054503030": ["Security System Extension", "System"], - "0CCE9212-69AE-11D9-BED3-505054503030": ["System Integrity", "System"], - "0CCE9213-69AE-11D9-BED3-505054503030": ["IPsec Driver", "System"], - "0CCE9214-69AE-11D9-BED3-505054503030": ["Other System Events", "System"], - "0CCE9215-69AE-11D9-BED3-505054503030": ["Logon", "Logon/Logoff"], - "0CCE9216-69AE-11D9-BED3-505054503030": ["Logoff", "Logon/Logoff"], - "0CCE9217-69AE-11D9-BED3-505054503030": ["Account Lockout", "Logon/Logoff"], - "0CCE9218-69AE-11D9-BED3-505054503030": ["IPsec Main Mode", "Logon/Logoff"], - "0CCE9219-69AE-11D9-BED3-505054503030": ["IPsec Quick Mode", "Logon/Logoff"], - "0CCE921A-69AE-11D9-BED3-505054503030": ["IPsec Extended Mode", "Logon/Logoff"], - "0CCE921B-69AE-11D9-BED3-505054503030": ["Special Logon", "Logon/Logoff"], - "0CCE921C-69AE-11D9-BED3-505054503030": ["Other Logon/Logoff Events", "Logon/Logoff"], - "0CCE9243-69AE-11D9-BED3-505054503030": ["Network Policy Server", "Logon/Logoff"], - "0CCE9247-69AE-11D9-BED3-505054503030": ["User / Device Claims", "Logon/Logoff"], - "0CCE921D-69AE-11D9-BED3-505054503030": ["File System", "Object Access"], - "0CCE921E-69AE-11D9-BED3-505054503030": ["Registry", "Object Access"], - "0CCE921F-69AE-11D9-BED3-505054503030": ["Kernel Object", "Object Access"], - "0CCE9220-69AE-11D9-BED3-505054503030": ["SAM", "Object Access"], - "0CCE9221-69AE-11D9-BED3-505054503030": ["Certification Services", "Object Access"], - "0CCE9222-69AE-11D9-BED3-505054503030": ["Application Generated", "Object Access"], - "0CCE9223-69AE-11D9-BED3-505054503030": ["Handle Manipulation", "Object Access"], - "0CCE9224-69AE-11D9-BED3-505054503030": ["File Share", "Object Access"], - "0CCE9225-69AE-11D9-BED3-505054503030": ["Filtering Platform Packet Drop", "Object Access"], - "0CCE9226-69AE-11D9-BED3-505054503030": ["Filtering Platform Connection ", "Object Access"], - "0CCE9227-69AE-11D9-BED3-505054503030": ["Other Object Access Events", "Object Access"], - "0CCE9244-69AE-11D9-BED3-505054503030": ["Detailed File Share", "Object Access"], - "0CCE9245-69AE-11D9-BED3-505054503030": ["Removable Storage", "Object Access"], - "0CCE9246-69AE-11D9-BED3-505054503030": ["Central Policy Staging", "Object Access"], - "0CCE9228-69AE-11D9-BED3-505054503030": ["Sensitive Privilege Use", "Privilege Use"], - "0CCE9229-69AE-11D9-BED3-505054503030": ["Non Sensitive Privilege Use", "Privilege Use"], - "0CCE922A-69AE-11D9-BED3-505054503030": ["Other Privilege Use Events", "Privilege Use"], - "0CCE922B-69AE-11D9-BED3-505054503030": ["Process Creation", "Detailed Tracking"], - "0CCE922C-69AE-11D9-BED3-505054503030": ["Process Termination", "Detailed Tracking"], - "0CCE922D-69AE-11D9-BED3-505054503030": ["DPAPI Activity", "Detailed Tracking"], - "0CCE922E-69AE-11D9-BED3-505054503030": ["RPC Events", "Detailed Tracking"], - "0CCE9248-69AE-11D9-BED3-505054503030": ["Plug and Play Events", "Detailed Tracking"], - "0CCE922F-69AE-11D9-BED3-505054503030": ["Audit Policy Change", "Policy Change"], - "0CCE9230-69AE-11D9-BED3-505054503030": ["Authentication Policy Change", "Policy Change"], - "0CCE9231-69AE-11D9-BED3-505054503030": ["Authorization Policy Change", "Policy Change"], - "0CCE9232-69AE-11D9-BED3-505054503030": ["MPSSVC Rule-Level Policy Change", "Policy Change"], - "0CCE9233-69AE-11D9-BED3-505054503030": ["Filtering Platform Policy Change", "Policy Change"], - "0CCE9234-69AE-11D9-BED3-505054503030": ["Other Policy Change Events", "Policy Change"], - "0CCE9235-69AE-11D9-BED3-505054503030": ["User Account Management", "Account Management"], - "0CCE9236-69AE-11D9-BED3-505054503030": ["Computer Account Management", "Account Management"], - "0CCE9237-69AE-11D9-BED3-505054503030": ["Security Group Management", "Account Management"], - "0CCE9238-69AE-11D9-BED3-505054503030": ["Distribution Group Management", "Account Management"], - "0CCE9239-69AE-11D9-BED3-505054503030": ["Application Group Management", "Account Management"], - "0CCE923A-69AE-11D9-BED3-505054503030": ["Other Account Management Events", "Account Management"], - "0CCE923B-69AE-11D9-BED3-505054503030": ["Directory Service Access", "Account Management"], - "0CCE923C-69AE-11D9-BED3-505054503030": ["Directory Service Changes", "Account Management"], - "0CCE923D-69AE-11D9-BED3-505054503030": ["Directory Service Replication", "Account Management"], - "0CCE923E-69AE-11D9-BED3-505054503030": ["Detailed Directory Service Replication", "Account Management"], - "0CCE923F-69AE-11D9-BED3-505054503030": ["Credential Validation", "Account Logon"], - "0CCE9240-69AE-11D9-BED3-505054503030": ["Kerberos Service Ticket Operations", "Account Logon"], - "0CCE9241-69AE-11D9-BED3-505054503030": ["Other Account Logon Events", "Account Logon"], - "0CCE9242-69AE-11D9-BED3-505054503030": ["Kerberos Authentication Service", "Account Logon"], + "0CCE9210-69AE-11D9-BED3-505054503030":["Security State Change", "System"], + "0CCE9211-69AE-11D9-BED3-505054503030":["Security System Extension", "System"], + "0CCE9212-69AE-11D9-BED3-505054503030":["System Integrity", "System"], + "0CCE9213-69AE-11D9-BED3-505054503030":["IPsec Driver", "System"], + "0CCE9214-69AE-11D9-BED3-505054503030":["Other System Events", "System"], + "0CCE9215-69AE-11D9-BED3-505054503030":["Logon", "Logon/Logoff"], + "0CCE9216-69AE-11D9-BED3-505054503030":["Logoff","Logon/Logoff"], + "0CCE9217-69AE-11D9-BED3-505054503030":["Account Lockout","Logon/Logoff"], + "0CCE9218-69AE-11D9-BED3-505054503030":["IPsec Main Mode","Logon/Logoff"], + "0CCE9219-69AE-11D9-BED3-505054503030":["IPsec Quick Mode","Logon/Logoff"], + "0CCE921A-69AE-11D9-BED3-505054503030":["IPsec Extended Mode","Logon/Logoff"], + "0CCE921B-69AE-11D9-BED3-505054503030":["Special Logon","Logon/Logoff"], + "0CCE921C-69AE-11D9-BED3-505054503030":["Other Logon/Logoff Events","Logon/Logoff"], + "0CCE9243-69AE-11D9-BED3-505054503030":["Network Policy Server","Logon/Logoff"], + "0CCE9247-69AE-11D9-BED3-505054503030":["User / Device Claims","Logon/Logoff"], + "0CCE921D-69AE-11D9-BED3-505054503030":["File System","Object Access"], + "0CCE921E-69AE-11D9-BED3-505054503030":["Registry","Object Access"], + "0CCE921F-69AE-11D9-BED3-505054503030":["Kernel Object","Object Access"], + "0CCE9220-69AE-11D9-BED3-505054503030":["SAM","Object Access"], + "0CCE9221-69AE-11D9-BED3-505054503030":["Certification Services","Object Access"], + "0CCE9222-69AE-11D9-BED3-505054503030":["Application Generated","Object Access"], + "0CCE9223-69AE-11D9-BED3-505054503030":["Handle Manipulation","Object Access"], + "0CCE9224-69AE-11D9-BED3-505054503030":["File Share","Object Access"], + "0CCE9225-69AE-11D9-BED3-505054503030":["Filtering Platform Packet Drop","Object Access"], + "0CCE9226-69AE-11D9-BED3-505054503030":["Filtering Platform Connection ","Object Access"], + "0CCE9227-69AE-11D9-BED3-505054503030":["Other Object Access Events","Object Access"], + "0CCE9244-69AE-11D9-BED3-505054503030":["Detailed File Share","Object Access"], + "0CCE9245-69AE-11D9-BED3-505054503030":["Removable Storage","Object Access"], + "0CCE9246-69AE-11D9-BED3-505054503030":["Central Policy Staging","Object Access"], + "0CCE9228-69AE-11D9-BED3-505054503030":["Sensitive Privilege Use","Privilege Use"], + "0CCE9229-69AE-11D9-BED3-505054503030":["Non Sensitive Privilege Use","Privilege Use"], + "0CCE922A-69AE-11D9-BED3-505054503030":["Other Privilege Use Events","Privilege Use"], + "0CCE922B-69AE-11D9-BED3-505054503030":["Process Creation","Detailed Tracking"], + "0CCE922C-69AE-11D9-BED3-505054503030":["Process Termination","Detailed Tracking"], + "0CCE922D-69AE-11D9-BED3-505054503030":["DPAPI Activity","Detailed Tracking"], + "0CCE922E-69AE-11D9-BED3-505054503030":["RPC Events","Detailed Tracking"], + "0CCE9248-69AE-11D9-BED3-505054503030":["Plug and Play Events","Detailed Tracking"], + "0CCE922F-69AE-11D9-BED3-505054503030":["Audit Policy Change","Policy Change"], + "0CCE9230-69AE-11D9-BED3-505054503030":["Authentication Policy Change","Policy Change"], + "0CCE9231-69AE-11D9-BED3-505054503030":["Authorization Policy Change","Policy Change"], + "0CCE9232-69AE-11D9-BED3-505054503030":["MPSSVC Rule-Level Policy Change","Policy Change"], + "0CCE9233-69AE-11D9-BED3-505054503030":["Filtering Platform Policy Change","Policy Change"], + "0CCE9234-69AE-11D9-BED3-505054503030":["Other Policy Change Events","Policy Change"], + "0CCE9235-69AE-11D9-BED3-505054503030":["User Account Management","Account Management"], + "0CCE9236-69AE-11D9-BED3-505054503030":["Computer Account Management","Account Management"], + "0CCE9237-69AE-11D9-BED3-505054503030":["Security Group Management","Account Management"], + "0CCE9238-69AE-11D9-BED3-505054503030":["Distribution Group Management","Account Management"], + "0CCE9239-69AE-11D9-BED3-505054503030":["Application Group Management","Account Management"], + "0CCE923A-69AE-11D9-BED3-505054503030":["Other Account Management Events","Account Management"], + "0CCE923B-69AE-11D9-BED3-505054503030":["Directory Service Access","Account Management"], + "0CCE923C-69AE-11D9-BED3-505054503030":["Directory Service Changes","Account Management"], + "0CCE923D-69AE-11D9-BED3-505054503030":["Directory Service Replication","Account Management"], + "0CCE923E-69AE-11D9-BED3-505054503030":["Detailed Directory Service Replication","Account Management"], + "0CCE923F-69AE-11D9-BED3-505054503030":["Credential Validation","Account Logon"], + "0CCE9240-69AE-11D9-BED3-505054503030":["Kerberos Service Ticket Operations","Account Logon"], + "0CCE9241-69AE-11D9-BED3-505054503030":["Other Account Logon Events","Account Logon"], + "0CCE9242-69AE-11D9-BED3-505054503030":["Kerberos Authentication Service","Account Logon"], }; + // Descriptions of failure status codes. // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4625 + // https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4776 var logonFailureStatus = { "0xc000005e": "There are currently no logon servers available to service the logon request.", "0xc0000064": "User logon with misspelled or bad user account", @@ -199,6 +359,7 @@ var security = (function () { "0xc0000234": "User logon with account locked", "0xc00002ee": "Failure Reason: An Error occurred during Logon", "0xc0000413": "Logon Failure: The machine you are logging onto is protected by an authentication firewall. The specified account is not allowed to authenticate to the machine.", + "0xc0000371": "The local account store does not contain secret material for the specified account", "0x0": "Status OK.", }; @@ -1187,17 +1348,31 @@ var security = (function () { return msobjsMessageTable[code]; }; - var addActionDesc = function(evt){ + var addEventFields = function(evt){ var code = evt.Get("event.code"); if (!code) { return; } - var eventActionDescription = eventActionTypes[code]; + var eventActionDescription = eventActionTypes[code][2]; if (eventActionDescription) { - evt.Put("event.action", eventActionDescription); + evt.AppendTo("event.category", eventActionTypes[code][0]); + evt.AppendTo("event.type", eventActionTypes[code][1]); + evt.Put("event.action", eventActionTypes[code][2]); } }; + var addEventOutcome = function(evt) { + var auditResult = evt.Get("winlog.keywords"); + if (!auditResult) { + return; + } + var eventOutcome = eventOutcomes[auditResult]; + if (eventOutcome === undefined) { + return; + } + evt.Put("event.outcome", eventOutcome); + }; + var addLogonType = function(evt) { var code = evt.Get("winlog.event_data.LogonType"); if (!code) { @@ -1242,38 +1417,30 @@ var security = (function () { evt.Put("winlog.logon.failure.sub_status", descriptiveFailureStatus); }; - var addUACDescription = function (evt) { + var addUACDescription = function(evt) { var code = evt.Get("winlog.event_data.NewUacValue"); if (!code) { return; } var uacCode = parseInt(code); - if (isNaN(uacCode)) { - return; - } var uacResult = []; for (var i = 0; i < uacFlags.length; i++) { if ((uacCode | uacFlags[i][0]) === uacCode) { uacResult.push(uacFlags[i][1]); } } - if (uacResult.length > 0) { - evt.Put("winlog.event_data.NewUacList", uacResult); + if (uacResult) { + evt.Put("winlog.event_data.NewUACList", uacResult); } - - // Parse list of values like "%%2080 %%2082 %%2084". - var uacList = evt.Get("winlog.event_data.UserAccountControl"); + var uacList = evt.Get("winlog.event_data.UserAccountControl").replace(/\s/g, '').split("%%").filter(String); if (!uacList) { return; } - uacList = uacList.replace(/\s/g, "").split("%%").filter(String); - if (uacList.length > 0) { - evt.Put("winlog.event_data.UserAccountControl", uacList); - } - }; + evt.Put("winlog.event_data.UserAccountControl", uacList); + }; - var addAuditInfo = function (evt) { - var subcategoryGuid = evt.Get("winlog.event_data.SubcategoryGuid").replace("{", "").replace("}", "").toUpperCase(); + var addAuditInfo = function(evt) { + var subcategoryGuid = evt.Get("winlog.event_data.SubcategoryGuid").replace("{", '').replace("}", '').toUpperCase(); if (!subcategoryGuid) { return; } @@ -1282,15 +1449,83 @@ var security = (function () { } evt.Put("winlog.event_data.Category", auditDescription[subcategoryGuid][1]); evt.Put("winlog.event_data.SubCategory", auditDescription[subcategoryGuid][0]); - var coded_actions = evt.Get("winlog.event_data.AuditPolicyChanges").split(","); - var action_results = []; - for (var j = 0; j < coded_actions.length; j++) { - var action_code = coded_actions[j].replace("%%", "").replace(" ", ""); - action_results.push(auditActions[action_code]); + var codedActions = evt.Get("winlog.event_data.AuditPolicyChanges").split(","); + var actionResults = []; + for (var j = 0; j < codedActions.length; j++) { + var actionCode = codedActions[j].replace("%%", '').replace(' ', ''); + actionResults.push(auditActions[actionCode]); } - evt.Put("winlog.event_data.AuditPolicyChangesDescription", action_results); + evt.Put("winlog.event_data.AuditPolicyChangesDescription", actionResults); }; + var addTicketOptionsDescription = function(evt) { + var code = evt.Get("winlog.event_data.TicketOptions"); + if (!code) { + return; + } + var tktCode = parseInt(code, 16).toString(2); + var tktResult = []; + var tktCodeLen = tktCode.length; + for (var i = tktCodeLen; i >= 0; i--) { + if (tktCode[i] == 1) { + tktResult.push(ticketOptions[(32-tktCodeLen)+i]); + } + } + if (tktResult) { + evt.Put("winlog.event_data.TicketOptionsDescription", tktResult); + } + }; + + var addTicketEncryptionType = function(evt) { + var code = evt.Get("winlog.event_data.TicketEncryptionType"); + if (!code) { + return; + } + var encTypeCode = code.toLowerCase(); + evt.Put("winlog.event_data.TicketEncryptionTypeDescription", ticketEncryptionTypes[encTypeCode]); + }; + + var addTicketStatus = function(evt) { + var code = evt.Get("winlog.event_data.Status"); + if (!code) { + return; + } + evt.Put("winlog.event_data.StatusDescription", kerberosTktStatusCodes[code]); + }; + + var addSessionData = new processor.Chain() + .Convert({ + fields: [ + {from: "winlog.event_data.AccountName", to: "user.name"}, + {from: "winlog.event_data.AccountDomain", to: "user.domain"}, + {from: "winlog.event_data.ClientAddress", to: "source.ip"}, + {from: "winlog.event_data.ClientName", to: "source.domain"}, + {from: "winlog.event_data.LogonID", to: "winlog.logon.id"}, + ], + ignore_missing: true, + }) + .Add(function(evt) { + var user = evt.Get("winlog.event_data.AccountName"); + evt.AppendTo('related.user', user); + }) + .Build(); + + var addServiceFields = new processor.Chain() + .Convert({ + fields: [ + {from: "winlog.event_data.ServiceName", to: "service.name"}, + ], + ignore_missing: true, + }) + .Add(function(evt) { + var code = evt.Get("winlog.event_data.ServiceType"); + if (!code) { + return; + } + evt.Put("service.type", serviceTypes[code]); + }) + .Build(); + var copyTargetUser = new processor.Chain() .Convert({ fields: [ @@ -1300,9 +1535,13 @@ var security = (function () { ], ignore_missing: true, }) - .Add(function (evt) { + .Add(function(evt) { var user = evt.Get("winlog.event_data.TargetUserName"); - evt.AppendTo("related.user", user); + if (/.@*/.test(user)) { + user = user.split('@')[0]; + evt.Put('user.name', user); + } + evt.AppendTo('related.user', user); }) .Build(); @@ -1348,7 +1587,7 @@ var security = (function () { }) .Add(function(evt) { var user = evt.Get("winlog.event_data.SubjectUserName"); - evt.AppendTo("related.user", user); + evt.AppendTo('related.user', user); }) .Build(); @@ -1361,9 +1600,9 @@ var security = (function () { ], ignore_missing: true, }) - .Add(function (evt) { + .Add(function(evt) { var user = evt.Get("winlog.user_data.SubjectUserName"); - evt.AppendTo("related.user", user); + evt.AppendTo('related.user', user); }) .Build(); @@ -1398,7 +1637,7 @@ var security = (function () { ignore_missing: true, fail_on_error: false, }) - .Add(function (evt) { + .Add(function(evt) { var name = evt.Get("process.name"); if (name) { return; @@ -1411,24 +1650,6 @@ var security = (function () { }) .Build(); - var addAuthSuccess = new processor.AddFields({ - fields: { - "event.category": "authentication", - "event.type": "authentication_success", - "event.outcome": "success", - }, - target: "", - }); - - var addAuthFailed = new processor.AddFields({ - fields: { - "event.category": "authentication", - "event.type": "authentication_failure", - "event.outcome": "failure", - }, - target: "", - }); - var renameNewProcessFields = new processor.Chain() .Convert({ fields: [ @@ -1440,7 +1661,7 @@ var security = (function () { ignore_missing: true, fail_on_error: false, }) - .Add(function (evt) { + .Add(function(evt) { var name = evt.Get("process.name"); if (name) { return; @@ -1451,7 +1672,7 @@ var security = (function () { } evt.Put("process.name", path.basename(exe)); }) - .Add(function (evt) { + .Add(function(evt) { var name = evt.Get("process.parent.name"); if (name) { return; @@ -1462,7 +1683,7 @@ var security = (function () { } evt.Put("process.parent.name", path.basename(exe)); }) - .Add(function (evt) { + .Add(function(evt) { var cl = evt.Get("winlog.event_data.CommandLine"); if (!cl) { return; @@ -1477,65 +1698,105 @@ var security = (function () { .Add(copyTargetUser) .Add(copyTargetUserLogonId) .Add(addLogonType) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) .Build(); - // Handles both 4624 and 4648. + // Handles both 4624 var logonSuccess = new processor.Chain() - .Add(addAuthSuccess) .Add(copyTargetUser) .Add(copyTargetUserLogonId) .Add(addLogonType) .Add(renameCommonAuthFields) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + var user = evt.Get("winlog.event_data.SubjectUserName"); + if (user) { + var res = /^-$/.test(user); + if (!res) { + evt.AppendTo('related.user', user); + } + } + }) + .Build(); + + // Handles both 4648 + var event4648 = new processor.Chain() + .Add(copyTargetUser) + .Add(copySubjectUserLogonId) + .Add(renameCommonAuthFields) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + var user = evt.Get("winlog.event_data.SubjectUserName"); + if (user) { + var res = /^-$/.test(user); + if (!res) { + evt.AppendTo('related.user', user); + } + } + }) .Build(); var event4625 = new processor.Chain() - .Add(addAuthFailed) .Add(copyTargetUser) - .Add(copyTargetUserLogonId) + .Add(copySubjectUserLogonId) .Add(addLogonType) .Add(addFailureCode) .Add(addFailureStatus) .Add(addFailureSubStatus) .Add(renameCommonAuthFields) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) .Build(); var event4672 = new processor.Chain() .Add(copySubjectUser) .Add(copySubjectUserLogonId) - .Add(function (evt) { + .Add(function(evt) { var privs = evt.Get("winlog.event_data.PrivilegeList"); if (!privs) { return; } evt.Put("winlog.event_data.PrivilegeList", privs.split(/\s+/)); }) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) .Build(); var event4688 = new processor.Chain() .Add(copySubjectUser) + .Add(copySubjectUserLogonId) .Add(renameNewProcessFields) - .Add(addActionDesc) - .Add(function (evt) { - evt.Put("event.category", "process"); - evt.Put("event.type", "process_start"); - }) - .Add(function (evt) { + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { var user = evt.Get("winlog.event_data.TargetUserName"); - evt.AppendTo("related.user", user); + var res = /^-$/.test(user); + if (!res) { + evt.AppendTo('related.user', user); + } }) .Build(); var event4689 = new processor.Chain() .Add(copySubjectUser) + .Add(copySubjectUserLogonId) + .Add(renameCommonAuthFields) + .Add(addEventFields) + .Add(addEventOutcome) + .Build(); + + var event4697 = new processor.Chain() + .Add(copySubjectUser) + .Add(copySubjectUserLogonId) .Add(renameCommonAuthFields) - .Add(addActionDesc) - .Add(function (evt) { - evt.Put("event.category", "process"); - evt.Put("event.type", "process_end"); + .Add(addServiceFields) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + evt.AppendTo("event.type", "change"); }) .Build(); @@ -1544,22 +1805,26 @@ var security = (function () { .Add(copySubjectUserLogonId) .Add(renameCommonAuthFields) .Add(addUACDescription) - .Add(addActionDesc) - .Add(function (evt) { + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { var user = evt.Get("winlog.event_data.TargetUserName"); - evt.AppendTo("related.user", user); + evt.AppendTo('related.user', user); + evt.AppendTo("event.type", "user"); }) .Build(); var userRenamed = new processor.Chain() .Add(copySubjectUser) .Add(copySubjectUserLogonId) - .Add(addActionDesc) - .Add(function (evt) { - var user_new = evt.Get("winlog.event_data.NewTargetUserName"); - evt.AppendTo("related.user", user_new); - var user_old = evt.Get("winlog.event_data.OldTargetUserName"); - evt.AppendTo("related.user", user_old); + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + var userNew = evt.Get("winlog.event_data.NewTargetUserName"); + evt.AppendTo('related.user', userNew); + var userOld = evt.Get("winlog.event_data.OldTargetUserName"); + evt.AppendTo('related.user', userOld); + evt.AppendTo("event.type", "user"); }) .Build(); @@ -1568,14 +1833,28 @@ var security = (function () { .Add(copySubjectUserLogonId) .Add(copyTargetUserToGroup) .Add(renameCommonAuthFields) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + evt.AppendTo("event.type", "group"); + var member = evt.Get("winlog.event_data.MemberName"); + if (!member) { + return; + } + evt.AppendTo("related.user", member.split(',')[0].replace('CN=', '').replace('cn=', '')); + }) + .Build(); var auditLogCleared = new processor.Chain() .Add(copySubjectUserFromUserData) .Add(copySubjectUserLogonIdFromUserData) .Add(renameCommonAuthFields) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + evt.AppendTo("event.type", "change"); + }) .Build(); var auditChanged = new processor.Chain() @@ -1583,12 +1862,17 @@ var security = (function () { .Add(copySubjectUserLogonId) .Add(renameCommonAuthFields) .Add(addAuditInfo) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + evt.AppendTo("event.type", "change"); + }) .Build(); var auditLogMgmt = new processor.Chain() .Add(renameCommonAuthFields) - .Add(addActionDesc) + .Add(addEventFields) + .Add(addEventOutcome) .Build(); var computerMgmtEvts = new processor.Chain() @@ -1596,14 +1880,97 @@ var security = (function () { .Add(copySubjectUserLogonId) .Add(copyTargetUserToComputerObject) .Add(renameCommonAuthFields) - .Add(addActionDesc) .Add(addUACDescription) - .Add(function (evt) { + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { var privs = evt.Get("winlog.event_data.PrivilegeList"); if (!privs) { return; } evt.Put("winlog.event_data.PrivilegeList", privs.split(/\s+/)); + evt.AppendTo("event.type", "admin"); + }) + .Build(); + + var sessionEvts = new processor.Chain() + .Add(addSessionData) + .Add(addEventFields) + .Add(addEventOutcome) + .Build(); + + var event4964 = new processor.Chain() + .Add(copyTargetUser) + .Add(copyTargetUserLogonId) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + evt.AppendTo("event.type", "group"); + }) + .Build(); + + var kerberosTktEvts = new processor.Chain() + .Add(copyTargetUser) + .Add(renameCommonAuthFields) + .Add(addTicketOptionsDescription) + .Add(addTicketEncryptionType) + .Add(addTicketStatus) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + var ip = evt.Get("source.ip"); + if (/::ffff:/.test(ip)) { + evt.Put("source.ip", ip.replace("::ffff:", "")); + } + }) + .Build(); + + var event4776 = new processor.Chain() + .Add(copyTargetUser) + .Add(addFailureStatus) + .Add(addEventFields) + .Add(addEventOutcome) + .Build(); + + var scheduledTask = new processor.Chain() + .Add(copySubjectUser) + .Add(copySubjectUserLogonId) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + evt.AppendTo("event.type", "admin"); + }) + .Build(); + + var sensitivePrivilege = new processor.Chain() + .Add(copySubjectUser) + .Add(copySubjectUserLogonId) + .Add(renameCommonAuthFields) + .Add(addEventFields) + .Add(addEventOutcome) + .Add(function(evt) { + var privs = evt.Get("winlog.event_data.PrivilegeList"); + if (!privs) { + return; + } + evt.Put("winlog.event_data.PrivilegeList", privs.split(/\s+/)); + }) + .Add(function(evt){ + var maskCodes = evt.Get("winlog.event_data.AccessMask"); + if (!maskCodes) { + return; + } + var maskList = maskCodes.replace(/\s+/g, '').split("%%").filter(String); + evt.Put("winlog.event_data.AccessMask", maskList); + var maskResults = []; + for (var j = 0; j < maskList.length; j++) { + var description = msobjsMessageTable[maskList[j]]; + if (description === undefined) { + return; + } + maskResults.push(description); + } + evt.Put("winlog.event_data.AccessMaskDescription", maskResults); }) .Build(); @@ -1637,17 +2004,41 @@ var security = (function () { 4647: logoff.Run, // 4648 - A logon was attempted using explicit credentials. - 4648: logonSuccess.Run, + 4648: event4648.Run, // 4672 - Special privileges assigned to new logon. 4672: event4672.Run, + // 4673 - A privileged service was called. + 4673: sensitivePrivilege.Run, + + // 4674 - An operation was attempted on a privileged object. + 4674: sensitivePrivilege.Run, + // 4688 - A new process has been created. 4688: event4688.Run, // 4689 - A process has exited. 4689: event4689.Run, + // 4697 - A service was installed in the system. + 4697: event4697.Run, + + // 4698 - A scheduled task was created. + 4698: scheduledTask.Run, + + // 4699 - A scheduled task was deleted. + 4699: scheduledTask.Run, + + // 4700 - A scheduled task was enabled. + 4700: scheduledTask.Run, + + // 4701 - A scheduled task was disabled. + 4701: scheduledTask.Run, + + // 4702 - A scheduled task was updated. + 4702: scheduledTask.Run, + // 4719 - System audit policy was changed. 4719: auditChanged.Run, @@ -1780,6 +2171,27 @@ var security = (function () { // 4767 - A user account was unlocked. 4767: userMgmtEvts.Run, + // 4768 - A Kerberos authentication ticket TGT was requested. + 4768: kerberosTktEvts.Run, + + // 4769 - A Kerberos service ticket was requested. + 4769: kerberosTktEvts.Run, + + // 4770 - A Kerberos service ticket was renewed. + 4770: kerberosTktEvts.Run, + + // 4771 - Kerberos pre-authentication failed. + 4771: kerberosTktEvts.Run, + + // 4776 - The computer attempted to validate the credentials for an account. + 4776: event4776.Run, + + // 4778 - A session was reconnected to a Window Station. + 4778: sessionEvts.Run, + + // 4779 - A session was disconnected from a Window Station. + 4779: sessionEvts.Run, + // 4781 - The name of an account was changed. 4781: userRenamed.Run, @@ -1789,7 +2201,10 @@ var security = (function () { // 4799 - A security-enabled local group membership was enumerated. 4799: groupMgmtEvts.Run, - process: function (evt) { + // 4964 - Special groups have been assigned to a new logon. + 4964: event4964.Run, + + process: function(evt) { var eventId = evt.Get("winlog.event_id"); var processor = this[eventId]; if (processor === undefined) { diff --git a/x-pack/winlogbeat/module/security/test/testdata/1100.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/1100.evtx.golden.json index e981d6042ea..6a2e7aa85ea 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/1100.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/1100.evtx.golden.json @@ -3,10 +3,13 @@ "@timestamp": "2019-11-07T10:37:04.2260925Z", "event": { "action": "logging-service-shutdown", + "category": "process", "code": 1100, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Eventlog" + "outcome": "success", + "provider": "Microsoft-Windows-Eventlog", + "type": "end" }, "host": { "name": "WIN-41OB2LO92CR.wlbeat.local" diff --git a/x-pack/winlogbeat/module/security/test/testdata/1102.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/1102.evtx.golden.json index 16f6e120b8a..d124c8154dd 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/1102.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/1102.evtx.golden.json @@ -2,11 +2,17 @@ { "@timestamp": "2019-11-07T10:34:29.0559196Z", "event": { - "action": "changed-audit-config", + "action": "audit-log-cleared", + "category": "iam", "code": 1102, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Eventlog" + "outcome": "success", + "provider": "Microsoft-Windows-Eventlog", + "type": [ + "admin", + "change" + ] }, "host": { "name": "WIN-41OB2LO92CR.wlbeat.local" diff --git a/x-pack/winlogbeat/module/security/test/testdata/1104.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/1104.evtx.golden.json index e75caf10328..9e0b25160e0 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/1104.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/1104.evtx.golden.json @@ -3,10 +3,13 @@ "@timestamp": "2019-11-08T07:56:17.3217049Z", "event": { "action": "logging-full", + "category": "iam", "code": 1104, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Eventlog" + "outcome": "success", + "provider": "Microsoft-Windows-Eventlog", + "type": "admin" }, "host": { "name": "WIN-41OB2LO92CR.wlbeat.local" diff --git a/x-pack/winlogbeat/module/security/test/testdata/1105.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/1105.evtx.golden.json index ca72947620e..ae6ba7ee57c 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/1105.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/1105.evtx.golden.json @@ -3,10 +3,13 @@ "@timestamp": "2019-11-07T16:22:14.8425353Z", "event": { "action": "auditlog-archieved", + "category": "iam", "code": 1105, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Eventlog" + "outcome": "success", + "provider": "Microsoft-Windows-Eventlog", + "type": "admin" }, "host": { "name": "WIN-41OB2LO92CR.wlbeat.local" diff --git a/x-pack/winlogbeat/module/security/test/testdata/4719.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4719.evtx.golden.json index 8780c91d12d..48e9297a3e0 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4719.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4719.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-11-07T15:22:57.6553291Z", "event": { "action": "changed-audit-config", + "category": "iam", "code": 4719, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "admin", + "change" + ] }, "host": { "name": "WIN-41OB2LO92CR.wlbeat.local" diff --git a/x-pack/winlogbeat/module/security/test/testdata/4741.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4741.evtx.golden.json index cd4bd32fb46..ead2058c418 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4741.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4741.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-18T16:22:12.3112534Z", "event": { "action": "added-computer-account", + "category": "iam", "code": 4741, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "admin" + ] }, "host": { "name": "DC_TEST2k12.TEST.SAAS" @@ -39,7 +45,7 @@ "HomeDirectory": "-", "HomePath": "-", "LogonHours": "%%1793", - "NewUacList": [ + "NewUACList": [ "SCRIPT", "ENCRYPTED_TEXT_PWD_ALLOWED" ], diff --git a/x-pack/winlogbeat/module/security/test/testdata/4742.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4742.evtx.golden.json index 423f7e92280..6e6d21d1d9f 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4742.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4742.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-18T16:22:12.3425087Z", "event": { "action": "changed-computer-account", + "category": "iam", "code": 4742, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "admin" + ] }, "host": { "name": "DC_TEST2k12.TEST.SAAS" @@ -40,7 +46,7 @@ "HomeDirectory": "-", "HomePath": "-", "LogonHours": "-", - "NewUacList": [ + "NewUACList": [ "ENCRYPTED_TEXT_PWD_ALLOWED" ], "NewUacValue": "0x84", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4743.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4743.evtx.golden.json index a64f1684596..c3dd849dfcf 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4743.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4743.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-18T16:25:21.5781833Z", "event": { "action": "deleted-computer-account", + "category": "iam", "code": 4743, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "admin" + ] }, "host": { "name": "DC_TEST2k12.TEST.SAAS" diff --git a/x-pack/winlogbeat/module/security/test/testdata/4744.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4744.evtx.golden.json index efad3a186bd..ee173fa174b 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4744.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4744.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-18T16:26:46.8744233Z", "event": { "action": "added-distribution-group-account", + "category": "iam", "code": 4744, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4745.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4745.evtx.golden.json index 115c5ba452f..6763c6e314b 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4745.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4745.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-18T16:29:05.0175739Z", "event": { "action": "changed-distribution-group-account", + "category": "iam", "code": 4745, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4746.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4746.evtx.golden.json index bb1f2e0fe39..4f6767b86f1 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4746.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4746.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-18T16:31:01.6117458Z", "event": { "action": "added-member-to-distribution-group", + "category": "iam", "code": 4746, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", @@ -19,7 +25,10 @@ "level": "information" }, "related": { - "user": "at_adm" + "user": [ + "at_adm", + "Administrator" + ] }, "user": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4747.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4747.evtx.golden.json index 734c1f25acc..1e49b60bf5a 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4747.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4747.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-18T16:35:16.6816525Z", "event": { "action": "removed-member-from-distribution-group", + "category": "iam", "code": 4747, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", @@ -19,7 +25,10 @@ "level": "information" }, "related": { - "user": "at_adm" + "user": [ + "at_adm", + "Administrator" + ] }, "user": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4748.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4748.evtx.golden.json index 529c63c93fb..7028e3eabcf 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4748.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4748.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:01:45.9824133Z", "event": { "action": "deleted-distribution-group-account", + "category": "iam", "code": 4748, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4749.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4749.evtx.golden.json index e00d62d4e0f..5d8b63f88fb 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4749.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4749.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:03:42.7234679Z", "event": { "action": "added-distribution-group-account", + "category": "iam", "code": 4749, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4750.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4750.evtx.golden.json index 5cc18e986c1..adc07bcf0bb 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4750.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4750.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:10:57.4737631Z", "event": { "action": "changed-distribution-group-account", + "category": "iam", "code": 4750, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4751.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4751.evtx.golden.json index acad53e1f9d..19365fcd0b0 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4751.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4751.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:20:29.0889568Z", "event": { "action": "added-member-to-distribution-group", + "category": "iam", "code": 4751, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", @@ -19,7 +25,10 @@ "level": "information" }, "related": { - "user": "at_adm" + "user": [ + "at_adm", + "Administrator" + ] }, "user": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4752.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4752.evtx.golden.json index 6daa89967bd..0ec7e223ca8 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4752.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4752.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:21:23.6444225Z", "event": { "action": "removed-member-from-distribution-group", + "category": "iam", "code": 4752, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", @@ -19,7 +25,10 @@ "level": "information" }, "related": { - "user": "at_adm" + "user": [ + "at_adm", + "Administrator" + ] }, "user": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4753.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4753.evtx.golden.json index a202dced9ea..2522fe24547 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4753.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4753.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:24:36.5952761Z", "event": { "action": "deleted-distribution-group-account", + "category": "iam", "code": 4753, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4759.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4759.evtx.golden.json index f7f1d4e03dd..ca734884d50 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4759.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4759.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:26:26.1432582Z", "event": { "action": "added-distribution-group-account", + "category": "iam", "code": 4759, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4760.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4760.evtx.golden.json index dee61d9d371..fd63349af6b 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4760.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4760.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:28:21.0305977Z", "event": { "action": "changed-distribution-group-account", + "category": "iam", "code": 4760, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4761.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4761.evtx.golden.json index ded73373373..541326dabdc 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4761.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4761.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:29:38.4487328Z", "event": { "action": "added-member-to-distribution-group", + "category": "iam", "code": 4761, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", @@ -19,7 +25,10 @@ "level": "information" }, "related": { - "user": "at_adm" + "user": [ + "at_adm", + "Administrator" + ] }, "user": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4762.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4762.evtx.golden.json index 4b346ef8e59..ff9647a360e 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4762.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4762.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:33:25.9678735Z", "event": { "action": "removed-member-from-distribution-group", + "category": "iam", "code": 4762, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "TEST", @@ -19,7 +25,10 @@ "level": "information" }, "related": { - "user": "at_adm" + "user": [ + "at_adm", + "Administrator" + ] }, "user": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/4763.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/4763.evtx.golden.json index d4069947156..a600ede656d 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/4763.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/4763.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-12-19T08:34:23.1623432Z", "event": { "action": "deleted-distribution-group-account", + "category": "iam", "code": 4763, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "group" + ] }, "group": { "domain": "TEST", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4673.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4673.evtx new file mode 100644 index 00000000000..643fadac216 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4673.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4673.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4673.evtx.golden.json new file mode 100644 index 00000000000..d0a1cd0e18d --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4673.evtx.golden.json @@ -0,0 +1,68 @@ +[ + { + "@timestamp": "2020-04-06T06:39:04.5491199Z", + "event": { + "action": "privileged-service-called", + "category": "iam", + "code": 4673, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "admin" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "process": { + "executable": "C:\\Windows\\System32\\lsass.exe", + "name": "lsass.exe", + "pid": 496 + }, + "related": { + "user": "DC_TEST2K12$" + }, + "user": { + "domain": "TEST", + "id": "S-1-5-18", + "name": "DC_TEST2K12$" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "ObjectServer": "NT Local Security Authority / Authentication Service", + "PrivilegeList": [ + "SeTcbPrivilege" + ], + "Service": "LsaRegisterLogonProcess()", + "SubjectDomainName": "TEST", + "SubjectLogonId": "0x3e7", + "SubjectUserName": "DC_TEST2K12$", + "SubjectUserSid": "S-1-5-18" + }, + "event_id": 4673, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x3e7" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 504 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5109160, + "task": "Sensitive Privilege Use" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4674.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4674.evtx new file mode 100644 index 00000000000..b4808dce3f1 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4674.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4674.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4674.evtx.golden.json new file mode 100644 index 00000000000..8e0e6c2a6f5 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4674.evtx.golden.json @@ -0,0 +1,78 @@ +[ + { + "@timestamp": "2020-04-06T06:38:31.1087891Z", + "event": { + "action": "privileged-operation", + "category": "iam", + "code": 4674, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "admin" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "process": { + "executable": "C:\\Windows\\System32\\svchost.exe", + "name": "svchost.exe", + "pid": 884 + }, + "related": { + "user": "at_adm" + }, + "user": { + "domain": "TEST", + "id": "S-1-5-21-1717121054-434620538-60925301-2794", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "AccessMask": [ + "1538", + "1542" + ], + "AccessMaskDescription": [ + "READ_CONTROL", + "ACCESS_SYS_SEC" + ], + "HandleId": "0x1ee0", + "ObjectName": "C:\\Windows\\System32\\Tasks\\Microsoft\\Windows\\PLA\\Server Manager Performance Monitor", + "ObjectServer": "Security", + "ObjectType": "File", + "PrivilegeList": [ + "SeSecurityPrivilege" + ], + "SubjectDomainName": "TEST", + "SubjectLogonId": "0x8aa365b", + "SubjectUserName": "at_adm", + "SubjectUserSid": "S-1-5-21-1717121054-434620538-60925301-2794" + }, + "event_id": 4674, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x8aa365b" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 504 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5109140, + "task": "Sensitive Privilege Use" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4697.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4697.evtx new file mode 100644 index 00000000000..b878c3bcd3a Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4697.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4697.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4697.evtx.golden.json new file mode 100644 index 00000000000..4f95860bf30 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4697.evtx.golden.json @@ -0,0 +1,71 @@ +[ + { + "@timestamp": "2020-04-02T14:34:08.8896056Z", + "event": { + "action": "service-installed", + "category": "iam", + "code": 4697, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "admin", + "change" + ] + }, + "host": { + "name": "WIN-41OB2LO92CR.wlbeat.local" + }, + "log": { + "level": "information" + }, + "related": { + "user": "Administrator" + }, + "service": { + "name": "winlogbeat", + "type": "Win32 Own Process" + }, + "user": { + "domain": "WLBEAT", + "id": "S-1-5-21-101361758-2486510592-3018839910-500", + "name": "Administrator" + }, + "winlog": { + "activity_id": "{74b64d41-08ce-0000-454f-b674ce08d601}", + "api": "wineventlog", + "channel": "Security", + "computer_name": "WIN-41OB2LO92CR.wlbeat.local", + "event_data": { + "ServiceAccount": "LocalSystem", + "ServiceFileName": "\"C:\\Program Files\\Winlogbeat\\winlogbeat.exe\" -c \"C:\\Program Files\\Winlogbeat\\winlogbeat.yml\" -path.home \"C:\\Program Files\\Winlogbeat\" -path.data \"C:\\ProgramData\\winlogbeat\" -path.logs \"C:\\ProgramData\\winlogbeat\\logs\" -E logging.files.redirect_stderr=true", + "ServiceName": "winlogbeat", + "ServiceStartType": "2", + "ServiceType": "0x10", + "SubjectDomainName": "WLBEAT", + "SubjectLogonId": "0x4c323", + "SubjectUserName": "Administrator", + "SubjectUserSid": "S-1-5-21-101361758-2486510592-3018839910-500" + }, + "event_id": 4697, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x4c323" + }, + "opcode": "Info", + "process": { + "pid": 792, + "thread": { + "id": 2492 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 90108, + "task": "Security System Extension" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4698.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4698.evtx new file mode 100644 index 00000000000..ec779713044 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4698.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4698.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4698.evtx.golden.json new file mode 100644 index 00000000000..f7a098c73ba --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4698.evtx.golden.json @@ -0,0 +1,63 @@ +[ + { + "@timestamp": "2020-04-01T14:34:34.6061085Z", + "event": { + "action": "scheduled-task-created", + "category": "iam", + "code": 4698, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "admin" + ] + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "user": { + "domain": "TEST", + "id": "S-1-5-21-1717121054-434620538-60925301-2794", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "SubjectDomainName": "TEST", + "SubjectLogonId": "0x60d1ca6", + "SubjectUserName": "at_adm", + "SubjectUserSid": "S-1-5-21-1717121054-434620538-60925301-2794", + "TaskContent": "\u003c?xml version=\"1.0\" encoding=\"UTF-16\"?\u003e\n\u003cTask version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"\u003e\n \u003cRegistrationInfo\u003e\n \u003cDate\u003e2020-04-01T16:34:34.574883\u003c/Date\u003e\n \u003cAuthor\u003eTEST\\at_adm\u003c/Author\u003e\n \u003c/RegistrationInfo\u003e\n \u003cTriggers\u003e\n \u003cTimeTrigger\u003e\n \u003cStartBoundary\u003e2020-04-01T16:33:41.3123848\u003c/StartBoundary\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003c/TimeTrigger\u003e\n \u003c/Triggers\u003e\n \u003cPrincipals\u003e\n \u003cPrincipal id=\"Author\"\u003e\n \u003cRunLevel\u003eLeastPrivilege\u003c/RunLevel\u003e\n \u003cUserId\u003eTEST\\at_adm\u003c/UserId\u003e\n \u003cLogonType\u003eInteractiveToken\u003c/LogonType\u003e\n \u003c/Principal\u003e\n \u003c/Principals\u003e\n \u003cSettings\u003e\n \u003cMultipleInstancesPolicy\u003eIgnoreNew\u003c/MultipleInstancesPolicy\u003e\n \u003cDisallowStartIfOnBatteries\u003etrue\u003c/DisallowStartIfOnBatteries\u003e\n \u003cStopIfGoingOnBatteries\u003etrue\u003c/StopIfGoingOnBatteries\u003e\n \u003cAllowHardTerminate\u003etrue\u003c/AllowHardTerminate\u003e\n \u003cStartWhenAvailable\u003efalse\u003c/StartWhenAvailable\u003e\n \u003cRunOnlyIfNetworkAvailable\u003efalse\u003c/RunOnlyIfNetworkAvailable\u003e\n \u003cIdleSettings\u003e\n \u003cStopOnIdleEnd\u003etrue\u003c/StopOnIdleEnd\u003e\n \u003cRestartOnIdle\u003efalse\u003c/RestartOnIdle\u003e\n \u003c/IdleSettings\u003e\n \u003cAllowStartOnDemand\u003etrue\u003c/AllowStartOnDemand\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003cHidden\u003efalse\u003c/Hidden\u003e\n \u003cRunOnlyIfIdle\u003efalse\u003c/RunOnlyIfIdle\u003e\n \u003cWakeToRun\u003efalse\u003c/WakeToRun\u003e\n \u003cExecutionTimeLimit\u003eP3D\u003c/ExecutionTimeLimit\u003e\n \u003cPriority\u003e7\u003c/Priority\u003e\n \u003c/Settings\u003e\n \u003cActions Context=\"Author\"\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\calc.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\mspaint.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003c/Actions\u003e\n\u003c/Task\u003e", + "TaskName": "\\test1" + }, + "event_id": 4698, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x60d1ca6" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 3684 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5043782, + "task": "Other Object Access Events" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4699.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4699.evtx new file mode 100644 index 00000000000..877b2a43def Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4699.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4699.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4699.evtx.golden.json new file mode 100644 index 00000000000..924af062c97 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4699.evtx.golden.json @@ -0,0 +1,63 @@ +[ + { + "@timestamp": "2020-04-01T14:35:47.822282Z", + "event": { + "action": "scheduled-task-deleted", + "category": "iam", + "code": 4699, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "admin" + ] + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "user": { + "domain": "TEST", + "id": "S-1-5-21-1717121054-434620538-60925301-2794", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "SubjectDomainName": "TEST", + "SubjectLogonId": "0x60d1ca6", + "SubjectUserName": "at_adm", + "SubjectUserSid": "S-1-5-21-1717121054-434620538-60925301-2794", + "TaskContent": "\u003c?xml version=\"1.0\" encoding=\"UTF-16\"?\u003e\n\u003cTask version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"\u003e\n \u003cRegistrationInfo\u003e\n \u003cDate\u003e2020-04-01T16:34:34.574883\u003c/Date\u003e\n \u003cAuthor\u003eTEST\\at_adm\u003c/Author\u003e\n \u003c/RegistrationInfo\u003e\n \u003cTriggers\u003e\n \u003cTimeTrigger\u003e\n \u003cStartBoundary\u003e2020-04-01T16:33:41.3123848\u003c/StartBoundary\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003c/TimeTrigger\u003e\n \u003c/Triggers\u003e\n \u003cPrincipals\u003e\n \u003cPrincipal id=\"Author\"\u003e\n \u003cRunLevel\u003eLeastPrivilege\u003c/RunLevel\u003e\n \u003cUserId\u003eTEST\\at_adm\u003c/UserId\u003e\n \u003cLogonType\u003eInteractiveToken\u003c/LogonType\u003e\n \u003c/Principal\u003e\n \u003c/Principals\u003e\n \u003cSettings\u003e\n \u003cMultipleInstancesPolicy\u003eIgnoreNew\u003c/MultipleInstancesPolicy\u003e\n \u003cDisallowStartIfOnBatteries\u003etrue\u003c/DisallowStartIfOnBatteries\u003e\n \u003cStopIfGoingOnBatteries\u003etrue\u003c/StopIfGoingOnBatteries\u003e\n \u003cAllowHardTerminate\u003etrue\u003c/AllowHardTerminate\u003e\n \u003cStartWhenAvailable\u003efalse\u003c/StartWhenAvailable\u003e\n \u003cRunOnlyIfNetworkAvailable\u003efalse\u003c/RunOnlyIfNetworkAvailable\u003e\n \u003cIdleSettings\u003e\n \u003cStopOnIdleEnd\u003etrue\u003c/StopOnIdleEnd\u003e\n \u003cRestartOnIdle\u003efalse\u003c/RestartOnIdle\u003e\n \u003c/IdleSettings\u003e\n \u003cAllowStartOnDemand\u003etrue\u003c/AllowStartOnDemand\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003cHidden\u003efalse\u003c/Hidden\u003e\n \u003cRunOnlyIfIdle\u003efalse\u003c/RunOnlyIfIdle\u003e\n \u003cWakeToRun\u003efalse\u003c/WakeToRun\u003e\n \u003cExecutionTimeLimit\u003eP3D\u003c/ExecutionTimeLimit\u003e\n \u003cPriority\u003e7\u003c/Priority\u003e\n \u003c/Settings\u003e\n \u003cActions Context=\"Author\"\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\calc.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003c/Actions\u003e\n\u003c/Task\u003e", + "TaskName": "\\test1" + }, + "event_id": 4699, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x60d1ca6" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 3684 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5043801, + "task": "Other Object Access Events" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4700.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4700.evtx new file mode 100644 index 00000000000..e45df8bf238 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4700.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4700.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4700.evtx.golden.json new file mode 100644 index 00000000000..6004373ad7d --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4700.evtx.golden.json @@ -0,0 +1,63 @@ +[ + { + "@timestamp": "2020-04-01T14:35:14.8732455Z", + "event": { + "action": "scheduled-task-enabled", + "category": "iam", + "code": 4700, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "admin" + ] + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "user": { + "domain": "TEST", + "id": "S-1-5-21-1717121054-434620538-60925301-2794", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "SubjectDomainName": "TEST", + "SubjectLogonId": "0x60d1ca6", + "SubjectUserName": "at_adm", + "SubjectUserSid": "S-1-5-21-1717121054-434620538-60925301-2794", + "TaskContent": "\u003c?xml version=\"1.0\" encoding=\"UTF-16\"?\u003e\n\u003cTask version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"\u003e\n \u003cRegistrationInfo\u003e\n \u003cDate\u003e2020-04-01T16:34:34.574883\u003c/Date\u003e\n \u003cAuthor\u003eTEST\\at_adm\u003c/Author\u003e\n \u003c/RegistrationInfo\u003e\n \u003cTriggers\u003e\n \u003cTimeTrigger\u003e\n \u003cStartBoundary\u003e2020-04-01T16:33:41.3123848\u003c/StartBoundary\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003c/TimeTrigger\u003e\n \u003c/Triggers\u003e\n \u003cPrincipals\u003e\n \u003cPrincipal id=\"Author\"\u003e\n \u003cRunLevel\u003eLeastPrivilege\u003c/RunLevel\u003e\n \u003cUserId\u003eTEST\\at_adm\u003c/UserId\u003e\n \u003cLogonType\u003eInteractiveToken\u003c/LogonType\u003e\n \u003c/Principal\u003e\n \u003c/Principals\u003e\n \u003cSettings\u003e\n \u003cMultipleInstancesPolicy\u003eIgnoreNew\u003c/MultipleInstancesPolicy\u003e\n \u003cDisallowStartIfOnBatteries\u003etrue\u003c/DisallowStartIfOnBatteries\u003e\n \u003cStopIfGoingOnBatteries\u003etrue\u003c/StopIfGoingOnBatteries\u003e\n \u003cAllowHardTerminate\u003etrue\u003c/AllowHardTerminate\u003e\n \u003cStartWhenAvailable\u003efalse\u003c/StartWhenAvailable\u003e\n \u003cRunOnlyIfNetworkAvailable\u003efalse\u003c/RunOnlyIfNetworkAvailable\u003e\n \u003cIdleSettings\u003e\n \u003cStopOnIdleEnd\u003etrue\u003c/StopOnIdleEnd\u003e\n \u003cRestartOnIdle\u003efalse\u003c/RestartOnIdle\u003e\n \u003c/IdleSettings\u003e\n \u003cAllowStartOnDemand\u003etrue\u003c/AllowStartOnDemand\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003cHidden\u003efalse\u003c/Hidden\u003e\n \u003cRunOnlyIfIdle\u003efalse\u003c/RunOnlyIfIdle\u003e\n \u003cWakeToRun\u003efalse\u003c/WakeToRun\u003e\n \u003cExecutionTimeLimit\u003eP3D\u003c/ExecutionTimeLimit\u003e\n \u003cPriority\u003e7\u003c/Priority\u003e\n \u003c/Settings\u003e\n \u003cActions Context=\"Author\"\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\calc.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\mspaint.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003c/Actions\u003e\n\u003c/Task\u003e", + "TaskName": "\\test1" + }, + "event_id": 4700, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x60d1ca6" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 3684 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5043792, + "task": "Other Object Access Events" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4701.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4701.evtx new file mode 100644 index 00000000000..4ade77eb664 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4701.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4701.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4701.evtx.golden.json new file mode 100644 index 00000000000..229ab491f58 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4701.evtx.golden.json @@ -0,0 +1,63 @@ +[ + { + "@timestamp": "2020-04-01T14:35:04.7030004Z", + "event": { + "action": "scheduled-task-disabled", + "category": "iam", + "code": 4701, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "admin" + ] + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "user": { + "domain": "TEST", + "id": "S-1-5-21-1717121054-434620538-60925301-2794", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "SubjectDomainName": "TEST", + "SubjectLogonId": "0x60d1ca6", + "SubjectUserName": "at_adm", + "SubjectUserSid": "S-1-5-21-1717121054-434620538-60925301-2794", + "TaskContent": "\u003c?xml version=\"1.0\" encoding=\"UTF-16\"?\u003e\n\u003cTask version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"\u003e\n \u003cRegistrationInfo\u003e\n \u003cDate\u003e2020-04-01T16:34:34.574883\u003c/Date\u003e\n \u003cAuthor\u003eTEST\\at_adm\u003c/Author\u003e\n \u003c/RegistrationInfo\u003e\n \u003cTriggers\u003e\n \u003cTimeTrigger\u003e\n \u003cStartBoundary\u003e2020-04-01T16:33:41.3123848\u003c/StartBoundary\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003c/TimeTrigger\u003e\n \u003c/Triggers\u003e\n \u003cPrincipals\u003e\n \u003cPrincipal id=\"Author\"\u003e\n \u003cRunLevel\u003eLeastPrivilege\u003c/RunLevel\u003e\n \u003cUserId\u003eTEST\\at_adm\u003c/UserId\u003e\n \u003cLogonType\u003eInteractiveToken\u003c/LogonType\u003e\n \u003c/Principal\u003e\n \u003c/Principals\u003e\n \u003cSettings\u003e\n \u003cMultipleInstancesPolicy\u003eIgnoreNew\u003c/MultipleInstancesPolicy\u003e\n \u003cDisallowStartIfOnBatteries\u003etrue\u003c/DisallowStartIfOnBatteries\u003e\n \u003cStopIfGoingOnBatteries\u003etrue\u003c/StopIfGoingOnBatteries\u003e\n \u003cAllowHardTerminate\u003etrue\u003c/AllowHardTerminate\u003e\n \u003cStartWhenAvailable\u003efalse\u003c/StartWhenAvailable\u003e\n \u003cRunOnlyIfNetworkAvailable\u003efalse\u003c/RunOnlyIfNetworkAvailable\u003e\n \u003cIdleSettings\u003e\n \u003cStopOnIdleEnd\u003etrue\u003c/StopOnIdleEnd\u003e\n \u003cRestartOnIdle\u003efalse\u003c/RestartOnIdle\u003e\n \u003c/IdleSettings\u003e\n \u003cAllowStartOnDemand\u003etrue\u003c/AllowStartOnDemand\u003e\n \u003cEnabled\u003efalse\u003c/Enabled\u003e\n \u003cHidden\u003efalse\u003c/Hidden\u003e\n \u003cRunOnlyIfIdle\u003efalse\u003c/RunOnlyIfIdle\u003e\n \u003cWakeToRun\u003efalse\u003c/WakeToRun\u003e\n \u003cExecutionTimeLimit\u003eP3D\u003c/ExecutionTimeLimit\u003e\n \u003cPriority\u003e7\u003c/Priority\u003e\n \u003c/Settings\u003e\n \u003cActions Context=\"Author\"\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\calc.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\mspaint.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003c/Actions\u003e\n\u003c/Task\u003e", + "TaskName": "\\test1" + }, + "event_id": 4701, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x60d1ca6" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 3684 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5043789, + "task": "Other Object Access Events" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4702.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4702.evtx new file mode 100644 index 00000000000..0f888825b63 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4702.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4702.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4702.evtx.golden.json new file mode 100644 index 00000000000..bd8fbbfd483 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4702.evtx.golden.json @@ -0,0 +1,63 @@ +[ + { + "@timestamp": "2020-04-01T14:35:36.2637108Z", + "event": { + "action": "scheduled-task-updated", + "category": "iam", + "code": 4702, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "admin" + ] + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "user": { + "domain": "TEST", + "id": "S-1-5-21-1717121054-434620538-60925301-2794", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "SubjectDomainName": "TEST", + "SubjectLogonId": "0x60d1ca6", + "SubjectUserName": "at_adm", + "SubjectUserSid": "S-1-5-21-1717121054-434620538-60925301-2794", + "TaskContentNew": "\u003c?xml version=\"1.0\" encoding=\"UTF-16\"?\u003e\n\u003cTask version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"\u003e\n \u003cRegistrationInfo\u003e\n \u003cDate\u003e2020-04-01T16:34:34.574883\u003c/Date\u003e\n \u003cAuthor\u003eTEST\\at_adm\u003c/Author\u003e\n \u003c/RegistrationInfo\u003e\n \u003cTriggers\u003e\n \u003cTimeTrigger\u003e\n \u003cStartBoundary\u003e2020-04-01T16:33:41.3123848\u003c/StartBoundary\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003c/TimeTrigger\u003e\n \u003c/Triggers\u003e\n \u003cPrincipals\u003e\n \u003cPrincipal id=\"Author\"\u003e\n \u003cRunLevel\u003eLeastPrivilege\u003c/RunLevel\u003e\n \u003cUserId\u003eTEST\\at_adm\u003c/UserId\u003e\n \u003cLogonType\u003eInteractiveToken\u003c/LogonType\u003e\n \u003c/Principal\u003e\n \u003c/Principals\u003e\n \u003cSettings\u003e\n \u003cMultipleInstancesPolicy\u003eIgnoreNew\u003c/MultipleInstancesPolicy\u003e\n \u003cDisallowStartIfOnBatteries\u003etrue\u003c/DisallowStartIfOnBatteries\u003e\n \u003cStopIfGoingOnBatteries\u003etrue\u003c/StopIfGoingOnBatteries\u003e\n \u003cAllowHardTerminate\u003etrue\u003c/AllowHardTerminate\u003e\n \u003cStartWhenAvailable\u003efalse\u003c/StartWhenAvailable\u003e\n \u003cRunOnlyIfNetworkAvailable\u003efalse\u003c/RunOnlyIfNetworkAvailable\u003e\n \u003cIdleSettings\u003e\n \u003cStopOnIdleEnd\u003etrue\u003c/StopOnIdleEnd\u003e\n \u003cRestartOnIdle\u003efalse\u003c/RestartOnIdle\u003e\n \u003c/IdleSettings\u003e\n \u003cAllowStartOnDemand\u003etrue\u003c/AllowStartOnDemand\u003e\n \u003cEnabled\u003etrue\u003c/Enabled\u003e\n \u003cHidden\u003efalse\u003c/Hidden\u003e\n \u003cRunOnlyIfIdle\u003efalse\u003c/RunOnlyIfIdle\u003e\n \u003cWakeToRun\u003efalse\u003c/WakeToRun\u003e\n \u003cExecutionTimeLimit\u003eP3D\u003c/ExecutionTimeLimit\u003e\n \u003cPriority\u003e7\u003c/Priority\u003e\n \u003c/Settings\u003e\n \u003cActions Context=\"Author\"\u003e\n \u003cExec\u003e\n \u003cCommand\u003e%windir%\\system32\\calc.exe\u003c/Command\u003e\n \u003c/Exec\u003e\n \u003c/Actions\u003e\n\u003c/Task\u003e", + "TaskName": "\\test1" + }, + "event_id": 4702, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x60d1ca6" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 1284 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5043795, + "task": "Other Object Access Events" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4768.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4768.evtx new file mode 100644 index 00000000000..1ff236d3db3 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4768.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4768.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4768.evtx.golden.json new file mode 100644 index 00000000000..4cddbdcea1f --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4768.evtx.golden.json @@ -0,0 +1,71 @@ +[ + { + "@timestamp": "2020-04-01T08:45:44.1717416Z", + "event": { + "action": "kerberos-authentication-ticket-requested", + "category": "authentication", + "code": 4768, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "start" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "source": { + "ip": "::1", + "port": 0 + }, + "user": { + "domain": "TEST.SAAS", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "PreAuthType": "2", + "ServiceName": "krbtgt", + "ServiceSid": "S-1-5-21-1717121054-434620538-60925301-502", + "Status": "0x0", + "StatusDescription": "KDC_ERR_NONE", + "TargetDomainName": "TEST.SAAS", + "TargetSid": "S-1-5-21-1717121054-434620538-60925301-2794", + "TargetUserName": "at_adm", + "TicketEncryptionType": "0x12", + "TicketEncryptionTypeDescription": "AES256-CTS-HMAC-SHA1-96", + "TicketOptions": "0x40810010", + "TicketOptionsDescription": [ + "Renewable-ok", + "Name-canonicalize", + "Renewable", + "Forwardable" + ] + }, + "event_id": 4768, + "keywords": [ + "Audit Success" + ], + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 2868 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5040235, + "task": "Kerberos Authentication Service" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4769.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4769.evtx new file mode 100644 index 00000000000..ddc9436667e Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4769.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4769.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4769.evtx.golden.json new file mode 100644 index 00000000000..0e17ff381f6 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4769.evtx.golden.json @@ -0,0 +1,70 @@ +[ + { + "@timestamp": "2020-04-01T08:45:44.1717416Z", + "event": { + "action": "kerberos-service-ticket-requested", + "category": "authentication", + "code": 4769, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "start" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "source": { + "ip": "::1", + "port": 0 + }, + "user": { + "domain": "TEST.SAAS", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "LogonGuid": "{46f85809-d26e-96f5-fbf2-73bd761a2d68}", + "ServiceName": "DC_TEST2K12$", + "ServiceSid": "S-1-5-21-1717121054-434620538-60925301-1110", + "Status": "0x0", + "StatusDescription": "KDC_ERR_NONE", + "TargetDomainName": "TEST.SAAS", + "TargetUserName": "at_adm@TEST.SAAS", + "TicketEncryptionType": "0x12", + "TicketEncryptionTypeDescription": "AES256-CTS-HMAC-SHA1-96", + "TicketOptions": "0x40810000", + "TicketOptionsDescription": [ + "Name-canonicalize", + "Renewable", + "Forwardable" + ], + "TransmittedServices": "-" + }, + "event_id": 4769, + "keywords": [ + "Audit Success" + ], + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 2868 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5040236, + "task": "Kerberos Service Ticket Operations" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4770.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4770.evtx new file mode 100644 index 00000000000..8a2f171f64a Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4770.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4770.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4770.evtx.golden.json new file mode 100644 index 00000000000..f41ce8ef476 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4770.evtx.golden.json @@ -0,0 +1,65 @@ +[ + { + "@timestamp": "2020-04-01T07:32:55.0104462Z", + "event": { + "action": "kerberos-service-ticket-renewed", + "category": "authentication", + "code": 4770, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "start" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "DC_TEST2K12$" + }, + "source": { + "ip": "::1", + "port": 0 + }, + "user": { + "domain": "TEST.SAAS", + "name": "DC_TEST2K12$" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "ServiceName": "krbtgt", + "ServiceSid": "S-1-5-21-1717121054-434620538-60925301-502", + "TargetDomainName": "TEST.SAAS", + "TargetUserName": "DC_TEST2K12$@TEST.SAAS", + "TicketEncryptionType": "0x12", + "TicketEncryptionTypeDescription": "AES256-CTS-HMAC-SHA1-96", + "TicketOptions": "0x10002", + "TicketOptionsDescription": [ + "Renew", + "Name-canonicalize" + ] + }, + "event_id": 4770, + "keywords": [ + "Audit Success" + ], + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 4468 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5039598, + "task": "Kerberos Service Ticket Operations" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4771.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4771.evtx new file mode 100644 index 00000000000..d3e8a80a371 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4771.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4771.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4771.evtx.golden.json new file mode 100644 index 00000000000..7321a262d93 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4771.evtx.golden.json @@ -0,0 +1,66 @@ +[ + { + "@timestamp": "2020-03-31T07:50:27.1681182Z", + "event": { + "action": "kerberos-preauth-failed", + "category": "authentication", + "code": 4771, + "kind": "event", + "module": "security", + "outcome": "failure", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "start" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "MPUIG" + }, + "source": { + "ip": "192.168.5.44", + "port": 53366 + }, + "user": { + "name": "MPUIG" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "PreAuthType": "0", + "ServiceName": "krbtgt/test.saas", + "Status": "0x12", + "StatusDescription": "KDC_ERR_CLIENT_REVOKED", + "TargetSid": "S-1-5-21-1717121054-434620538-60925301-3057", + "TargetUserName": "MPUIG", + "TicketOptions": "0x40810010", + "TicketOptionsDescription": [ + "Renewable-ok", + "Name-canonicalize", + "Renewable", + "Forwardable" + ] + }, + "event_id": 4771, + "keywords": [ + "Audit Failure" + ], + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 4552 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5027836, + "task": "Kerberos Authentication Service" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4776.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4776.evtx new file mode 100644 index 00000000000..e9017eff858 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4776.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4776.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4776.evtx.golden.json new file mode 100644 index 00000000000..23a60fcb72e --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4776.evtx.golden.json @@ -0,0 +1,58 @@ +[ + { + "@timestamp": "2020-04-01T08:45:42.1873153Z", + "event": { + "action": "credential-validated", + "category": "authentication", + "code": 4776, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "start" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "user": { + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "PackageName": "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0", + "Status": "0x0", + "TargetUserName": "at_adm", + "Workstation": "EQP01777" + }, + "event_id": 4776, + "keywords": [ + "Audit Success" + ], + "logon": { + "failure": { + "status": "Status OK." + } + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 1864 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5040222, + "task": "Credential Validation" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4778.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4778.evtx new file mode 100644 index 00000000000..5d8653c23ff Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4778.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4778.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4778.evtx.golden.json new file mode 100644 index 00000000000..f6723e5bada --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4778.evtx.golden.json @@ -0,0 +1,63 @@ +[ + { + "@timestamp": "2020-04-05T16:33:32.3888253Z", + "event": { + "action": "session-reconnected", + "category": "authentication", + "code": 4778, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "start" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "source": { + "domain": "EQP01777", + "ip": "10.100.150.9" + }, + "user": { + "domain": "TEST", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "AccountDomain": "TEST", + "AccountName": "at_adm", + "ClientAddress": "10.100.150.9", + "ClientName": "EQP01777", + "LogonID": "0x76fea87", + "SessionName": "RDP-Tcp#127" + }, + "event_id": 4778, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x76fea87" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 4184 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5101675, + "task": "Other Logon/Logoff Events" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4779.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4779.evtx new file mode 100644 index 00000000000..29bc4f77453 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4779.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4779.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4779.evtx.golden.json new file mode 100644 index 00000000000..d3efbfe1bb2 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012_4779.evtx.golden.json @@ -0,0 +1,63 @@ +[ + { + "@timestamp": "2020-04-03T10:18:01.8822336Z", + "event": { + "action": "session-disconnected", + "category": "authentication", + "code": 4779, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "end" + }, + "host": { + "name": "DC_TEST2k12.TEST.SAAS" + }, + "log": { + "level": "information" + }, + "related": { + "user": "at_adm" + }, + "source": { + "domain": "EQP01777", + "ip": "10.100.150.17" + }, + "user": { + "domain": "TEST", + "name": "at_adm" + }, + "winlog": { + "api": "wineventlog", + "channel": "Security", + "computer_name": "DC_TEST2k12.TEST.SAAS", + "event_data": { + "AccountDomain": "TEST", + "AccountName": "at_adm", + "ClientAddress": "10.100.150.17", + "ClientName": "EQP01777", + "LogonID": "0x60d1ccb", + "SessionName": "RDP-Tcp#116" + }, + "event_id": 4779, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x60d1ccb" + }, + "opcode": "Info", + "process": { + "pid": 496, + "thread": { + "id": 3852 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 5069070, + "task": "Other Logon/Logoff Events" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012r2-logon.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012r2-logon.evtx.golden.json index 519a58ec959..2fda2af99bb 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2012r2-logon.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2012r2-logon.evtx.golden.json @@ -9,7 +9,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -23,7 +23,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -86,7 +89,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -100,7 +103,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -163,7 +169,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -177,7 +183,10 @@ "pid": 448 }, "related": { - "user": "vagrant" + "user": [ + "vagrant", + "VAGRANT-2012-R2$" + ] }, "source": { "domain": "VAGRANT-2012-R2", @@ -243,7 +252,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -257,7 +266,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -320,7 +332,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -397,7 +409,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -474,7 +486,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -551,7 +563,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -628,7 +640,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -708,7 +720,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -722,7 +734,10 @@ "pid": 2812 }, "related": { - "user": "DWM-2" + "user": [ + "DWM-2", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "Window Manager", @@ -785,7 +800,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -799,7 +814,10 @@ "pid": 2812 }, "related": { - "user": "vagrant" + "user": [ + "vagrant", + "VAGRANT-2012-R2$" + ] }, "source": { "domain": "VAGRANT-2012-R2", @@ -865,7 +883,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -879,7 +897,10 @@ "pid": 2188 }, "related": { - "user": "DWM-3" + "user": [ + "DWM-3", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "Window Manager", @@ -942,7 +963,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -956,7 +977,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -1019,7 +1043,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -1033,7 +1057,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -1096,7 +1123,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -1110,7 +1137,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -1173,7 +1203,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -1187,7 +1217,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -1250,7 +1283,7 @@ "module": "security", "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_success" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -1264,7 +1297,10 @@ "pid": 508 }, "related": { - "user": "SYSTEM" + "user": [ + "SYSTEM", + "VAGRANT-2012-R2$" + ] }, "user": { "domain": "NT AUTHORITY", @@ -1327,7 +1363,7 @@ "module": "security", "outcome": "failure", "provider": "Microsoft-Windows-Security-Auditing", - "type": "authentication_failure" + "type": "start" }, "host": { "name": "vagrant-2012-r2" @@ -1385,6 +1421,7 @@ "status": "This is either due to a bad username or authentication information", "sub_status": "User logon with misspelled or bad user account" }, + "id": "0x1008e", "type": "Interactive" }, "opcode": "Info", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-4672.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-4672.evtx.golden.json index 7aee5aeef9a..5a7f9be75a9 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-4672.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-4672.evtx.golden.json @@ -3,10 +3,13 @@ "@timestamp": "2018-05-18T23:09:03.2086661Z", "event": { "action": "logged-in-special", + "category": "iam", "code": 4672, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "admin" }, "host": { "name": "vagrant-2016" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-logoff.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-logoff.evtx.golden.json index c1166103e9b..23c1159d403 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-logoff.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016-logoff.evtx.golden.json @@ -3,10 +3,13 @@ "@timestamp": "2019-05-17T11:06:58.210768Z", "event": { "action": "logged-out", + "category": "authentication", "code": 4634, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "end" }, "host": { "name": "WIN-41OB2LO92CR" @@ -58,10 +61,13 @@ "@timestamp": "2019-05-19T16:15:38.542273Z", "event": { "action": "logged-out", + "category": "authentication", "code": 4634, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": "end" }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4720_Account_Created.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4720_Account_Created.evtx.golden.json index f2092d9bb8d..9908eccb830 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4720_Account_Created.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4720_Account_Created.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:24:39.2933111Z", "event": { "action": "added-user-account", + "category": "iam", "code": 4720, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -37,7 +43,7 @@ "HomeDirectory": "%%1793", "HomePath": "%%1793", "LogonHours": "%%1797", - "NewUacList": [ + "NewUACList": [ "SCRIPT", "LOCKOUT" ], @@ -90,10 +96,16 @@ "@timestamp": "2019-09-06T13:25:21.8672707Z", "event": { "action": "added-user-account", + "category": "iam", "code": 4720, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -124,7 +136,7 @@ "HomeDirectory": "%%1793", "HomePath": "%%1793", "LogonHours": "%%1797", - "NewUacList": [ + "NewUACList": [ "SCRIPT", "LOCKOUT" ], diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4722_Account_Enabled.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4722_Account_Enabled.evtx.golden.json index 60548259535..6fa5bb63b42 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4722_Account_Enabled.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4722_Account_Enabled.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:28:46.1631928Z", "event": { "action": "enabled-user-account", + "category": "iam", "code": 4722, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -63,10 +69,16 @@ "@timestamp": "2019-09-06T13:29:08.5737904Z", "event": { "action": "enabled-user-account", + "category": "iam", "code": 4722, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4723_Password_Change.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4723_Password_Change.evtx.golden.json index f59261c6a02..270ef50ad1e 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4723_Password_Change.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4723_Password_Change.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:32:13.8554125Z", "event": { "action": "changed-password", + "category": "iam", "code": 4723, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "failure", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -61,10 +67,16 @@ "@timestamp": "2019-09-06T13:32:23.8855201Z", "event": { "action": "changed-password", + "category": "iam", "code": 4723, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4724_Password_Reset.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4724_Password_Reset.evtx.golden.json index 83b32607789..7a3c9767ab5 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4724_Password_Reset.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4724_Password_Reset.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:24:39.339071Z", "event": { "action": "reset-password", + "category": "iam", "code": 4724, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -63,10 +69,16 @@ "@timestamp": "2019-09-06T13:25:21.9005914Z", "event": { "action": "reset-password", + "category": "iam", "code": 4724, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4725_Account_Disabled.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4725_Account_Disabled.evtx.golden.json index 8c8d35b4b73..ccf014d68e3 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4725_Account_Disabled.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4725_Account_Disabled.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:28:40.0015275Z", "event": { "action": "disabled-user-account", + "category": "iam", "code": 4725, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -63,10 +69,16 @@ "@timestamp": "2019-09-06T13:28:55.2644212Z", "event": { "action": "disabled-user-account", + "category": "iam", "code": 4725, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4726_Account_Deleted.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4726_Account_Deleted.evtx.golden.json index dbdbea6bce8..df5544fdafc 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4726_Account_Deleted.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4726_Account_Deleted.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:35:25.5153959Z", "event": { "action": "deleted-user-account", + "category": "iam", "code": 4726, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -64,10 +70,16 @@ "@timestamp": "2019-09-06T13:35:29.6900555Z", "event": { "action": "deleted-user-account", + "category": "iam", "code": 4726, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4727.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4727.evtx.golden.json index 74625a93246..d85d9a40ea3 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4727.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4727.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:26:12.4955445Z", "event": { "action": "added-group-account", + "category": "iam", "code": 4727, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4728.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4728.evtx.golden.json index 7eb4b175035..eff3f51f52d 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4728.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4728.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:33:26.8613751Z", "event": { "action": "added-member-to-group", + "category": "iam", "code": 4728, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4729.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4729.evtx.golden.json index e932893c283..536d546b58d 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4729.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4729.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:33:45.5433159Z", "event": { "action": "removed-member-from-group", + "category": "iam", "code": 4729, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4730.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4730.evtx.golden.json index a859249e571..1e0a1fa75cd 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4730.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4730.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:34:01.6107262Z", "event": { "action": "deleted-group-account", + "category": "iam", "code": 4730, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4731.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4731.evtx.golden.json index d9e51ee82c1..fc1866628be 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4731.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4731.evtx.golden.json @@ -2,11 +2,17 @@ { "@timestamp": "2019-10-22T11:29:49.3586766Z", "event": { - "action": "added-member-to-group", + "action": "added-group-account", + "category": "iam", "code": 4731, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4732.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4732.evtx.golden.json index f6f929666fb..139ab72e02e 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4732.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4732.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:31:58.0398598Z", "event": { "action": "added-member-to-group", + "category": "iam", "code": 4732, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4733.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4733.evtx.golden.json index d94dde1207b..1bc815b3730 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4733.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4733.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:32:14.8941288Z", "event": { "action": "removed-member-from-group", + "category": "iam", "code": 4733, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4734.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4734.evtx.golden.json index b25a8a36949..3dc919714de 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4734.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4734.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:32:35.1274042Z", "event": { "action": "deleted-group-account", + "category": "iam", "code": 4734, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4735.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4735.evtx.golden.json index b746c834fc4..88c5d7e4c0c 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4735.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4735.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:32:30.425487Z", "event": { "action": "modified-group-account", + "category": "iam", "code": 4735, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4737.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4737.evtx.golden.json index 84dd00dadf3..4cb1c5cc4e3 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4737.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4737.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:33:57.2710608Z", "event": { "action": "modified-group-account", + "category": "iam", "code": 4737, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4738_Account_Changed.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4738_Account_Changed.evtx.golden.json index be1b1ec8aa7..30226b8ad7a 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4738_Account_Changed.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4738_Account_Changed.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:36:17.5667652Z", "event": { "action": "modified-user-account", + "category": "iam", "code": 4738, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -38,7 +44,7 @@ "HomeDirectory": "%%1793", "HomePath": "%%1793", "LogonHours": "%%1797", - "NewUacList": [ + "NewUACList": [ "LOCKOUT", "NORMAL_ACCOUNT" ], @@ -89,10 +95,16 @@ "@timestamp": "2019-09-06T13:36:36.3634107Z", "event": { "action": "modified-user-account", + "category": "iam", "code": 4738, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -124,7 +136,7 @@ "HomeDirectory": "%%1793", "HomePath": "%%1793", "LogonHours": "%%1797", - "NewUacList": [ + "NewUACList": [ "LOCKOUT", "NORMAL_ACCOUNT" ], diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4740_Account_Locked_Out.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4740_Account_Locked_Out.evtx.golden.json index a9f94e35dea..9e69876dcfd 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4740_Account_Locked_Out.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4740_Account_Locked_Out.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:39:43.0856521Z", "event": { "action": "locked-out-user-account", + "category": "iam", "code": 4740, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4754.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4754.evtx.golden.json index fd846c3ba05..c3cc298857f 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4754.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4754.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:34:33.783048Z", "event": { "action": "added-group-account", + "category": "iam", "code": 4754, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "creation", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4755.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4755.evtx.golden.json index e05ef6843a7..08312b06f0a 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4755.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4755.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:35:09.0701134Z", "event": { "action": "modified-group-account", + "category": "iam", "code": 4755, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4756.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4756.evtx.golden.json index 6d199101b60..1662f9e96ca 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4756.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4756.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:34:58.4130288Z", "event": { "action": "added-member-to-group", + "category": "iam", "code": 4756, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4757.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4757.evtx.golden.json index 65c09c2b92f..ad2dcbf68b2 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4757.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4757.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:35:09.0701919Z", "event": { "action": "removed-member-from-group", + "category": "iam", "code": 4757, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4758.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4758.evtx.golden.json index 6b41b468b8d..eb6d7f8873d 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4758.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4758.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:35:13.5502867Z", "event": { "action": "deleted-group-account", + "category": "iam", "code": 4758, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "deletion", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4764.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4764.evtx.golden.json index 6a876e9689d..7651be3f9c7 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4764.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4764.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-22T11:33:57.271141Z", "event": { "action": "type-changed-group-account", + "category": "iam", "code": 4764, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "group" + ] }, "group": { "domain": "WLBEAT", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4767_Account_Unlocked.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4767_Account_Unlocked.evtx.golden.json index 4a494e29010..c10208c9792 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4767_Account_Unlocked.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4767_Account_Unlocked.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:40:52.3149485Z", "event": { "action": "unlocked-user-account", + "category": "iam", "code": 4767, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4781_Account_Renamed.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4781_Account_Renamed.evtx.golden.json index d66432da5e4..717cb8c8cce 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4781_Account_Renamed.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4781_Account_Renamed.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-09-06T13:38:17.5566269Z", "event": { "action": "renamed-user-account", + "category": "iam", "code": 4781, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" @@ -66,10 +72,16 @@ "@timestamp": "2019-09-06T13:38:23.5161066Z", "event": { "action": "renamed-user-account", + "category": "iam", "code": 4781, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "change", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4798.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4798.evtx.golden.json index 470400162f0..cdc5eb60a82 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4798.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4798.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-08T10:20:34.0535453Z", "event": { "action": "group-membership-enumerated", + "category": "iam", "code": 4798, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "info", + "user" + ] }, "host": { "name": "WIN-41OB2LO92CR" diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4799.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4799.evtx.golden.json index ebcda23bdf1..9048b6b821f 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4799.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4799.evtx.golden.json @@ -3,10 +3,16 @@ "@timestamp": "2019-10-08T10:20:44.4724208Z", "event": { "action": "user-member-enumerated", + "category": "iam", "code": 4799, "kind": "event", "module": "security", - "provider": "Microsoft-Windows-Security-Auditing" + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "info", + "group" + ] }, "group": { "domain": "Builtin", diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4964.evtx b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4964.evtx new file mode 100644 index 00000000000..1d14b8cd234 Binary files /dev/null and b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4964.evtx differ diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4964.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4964.evtx.golden.json new file mode 100644 index 00000000000..930bc35db79 --- /dev/null +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2016_4964.evtx.golden.json @@ -0,0 +1,136 @@ +[ + { + "@timestamp": "2020-03-21T23:50:34.347458Z", + "event": { + "action": "logged-in-special", + "category": "iam", + "code": 4964, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "admin", + "group" + ] + }, + "host": { + "name": "WIN-41OB2LO92CR.wlbeat.local" + }, + "log": { + "level": "information" + }, + "related": { + "user": "Administrator" + }, + "user": { + "domain": "WLBEAT", + "id": "S-1-5-21-101361758-2486510592-3018839910-500", + "name": "Administrator" + }, + "winlog": { + "activity_id": "{af6b9825-ffd8-0000-2f9a-6bafd8ffd501}", + "api": "wineventlog", + "channel": "Security", + "computer_name": "WIN-41OB2LO92CR.wlbeat.local", + "event_data": { + "LogonGuid": "{00000000-0000-0000-0000-000000000000}", + "SidList": "\n\t\t%{S-1-5-21-101361758-2486510592-3018839910-519}", + "SubjectDomainName": "WLBEAT", + "SubjectLogonId": "0x3e7", + "SubjectUserName": "WIN-41OB2LO92CR$", + "SubjectUserSid": "S-1-5-18", + "TargetDomainName": "WLBEAT", + "TargetLogonGuid": "{c25cdf73-2322-651f-f4fb-db862c0e03a8}", + "TargetLogonId": "0x1d22ed", + "TargetUserName": "Administrator", + "TargetUserSid": "S-1-5-21-101361758-2486510592-3018839910-500" + }, + "event_id": 4964, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x1d22ed" + }, + "opcode": "Info", + "process": { + "pid": 788, + "thread": { + "id": 828 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 68259, + "task": "Special Logon" + } + }, + { + "@timestamp": "2020-03-24T16:36:59.5703294Z", + "event": { + "action": "logged-in-special", + "category": "iam", + "code": 4964, + "kind": "event", + "module": "security", + "outcome": "success", + "provider": "Microsoft-Windows-Security-Auditing", + "type": [ + "admin", + "group" + ] + }, + "host": { + "name": "WIN-41OB2LO92CR.wlbeat.local" + }, + "log": { + "level": "information" + }, + "related": { + "user": "Administrator" + }, + "user": { + "domain": "WLBEAT", + "id": "S-1-5-21-101361758-2486510592-3018839910-500", + "name": "Administrator" + }, + "winlog": { + "activity_id": "{a22b4bf4-ffdc-0000-ee4d-2ba2dcffd501}", + "api": "wineventlog", + "channel": "Security", + "computer_name": "WIN-41OB2LO92CR.wlbeat.local", + "event_data": { + "LogonGuid": "{00000000-0000-0000-0000-000000000000}", + "SidList": "\n\t\t%{S-1-5-21-101361758-2486510592-3018839910-512}\n\t\t%{S-1-5-21-101361758-2486510592-3018839910-519}\n\t\t%{S-1-5-21-101361758-2486510592-3018839910-1007}", + "SubjectDomainName": "WLBEAT", + "SubjectLogonId": "0x3e7", + "SubjectUserName": "WIN-41OB2LO92CR$", + "SubjectUserSid": "S-1-5-18", + "TargetDomainName": "WLBEAT", + "TargetLogonGuid": "{38fec9bc-577f-76f6-5d29-e0175ce19797}", + "TargetLogonId": "0x7c0be", + "TargetUserName": "Administrator", + "TargetUserSid": "S-1-5-21-101361758-2486510592-3018839910-500" + }, + "event_id": 4964, + "keywords": [ + "Audit Success" + ], + "logon": { + "id": "0x7c0be" + }, + "opcode": "Info", + "process": { + "pid": 784, + "thread": { + "id": 2608 + } + }, + "provider_guid": "{54849625-5478-4994-a5ba-3e3b0328c30d}", + "provider_name": "Microsoft-Windows-Security-Auditing", + "record_id": 68620, + "task": "Special Logon" + } + } +] \ No newline at end of file diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4688_Process_Created.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4688_Process_Created.evtx.golden.json index 9e92d3182a8..dbac75b2935 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4688_Process_Created.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4688_Process_Created.evtx.golden.json @@ -7,8 +7,9 @@ "code": 4688, "kind": "event", "module": "security", + "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "process_start" + "type": "start" }, "host": { "name": "vagrant" @@ -32,10 +33,7 @@ "pid": 4556 }, "related": { - "user": [ - "vagrant", - "-" - ] + "user": "vagrant" }, "user": { "domain": "VAGRANT", @@ -64,6 +62,9 @@ "keywords": [ "Audit Success" ], + "logon": { + "id": "0x274a2" + }, "opcode": "Info", "process": { "pid": 4, diff --git a/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4689_Process_Exited.evtx.golden.json b/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4689_Process_Exited.evtx.golden.json index 16a9d810899..98d0aafb51b 100644 --- a/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4689_Process_Exited.evtx.golden.json +++ b/x-pack/winlogbeat/module/security/test/testdata/security-windows2019_4689_Process_Exited.evtx.golden.json @@ -7,8 +7,9 @@ "code": 4689, "kind": "event", "module": "security", + "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "process_end" + "type": "end" }, "host": { "name": "vagrant" @@ -44,6 +45,9 @@ "keywords": [ "Audit Success" ], + "logon": { + "id": "0x274a2" + }, "opcode": "Info", "process": { "pid": 4, @@ -65,8 +69,9 @@ "code": 4689, "kind": "event", "module": "security", + "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "process_end" + "type": "end" }, "host": { "name": "vagrant" @@ -102,6 +107,9 @@ "keywords": [ "Audit Success" ], + "logon": { + "id": "0x274f1" + }, "opcode": "Info", "process": { "pid": 4, @@ -123,8 +131,9 @@ "code": 4689, "kind": "event", "module": "security", + "outcome": "success", "provider": "Microsoft-Windows-Security-Auditing", - "type": "process_end" + "type": "end" }, "host": { "name": "vagrant" @@ -160,6 +169,9 @@ "keywords": [ "Audit Success" ], + "logon": { + "id": "0x274a2" + }, "opcode": "Info", "process": { "pid": 4, diff --git a/x-pack/winlogbeat/module/sysmon/config/winlogbeat-sysmon.js b/x-pack/winlogbeat/module/sysmon/config/winlogbeat-sysmon.js index 83f24193a66..b882df875fc 100644 --- a/x-pack/winlogbeat/module/sysmon/config/winlogbeat-sysmon.js +++ b/x-pack/winlogbeat/module/sysmon/config/winlogbeat-sysmon.js @@ -301,20 +301,20 @@ var sysmon = (function () { evt.Put(nameField, path.basename(exe)); }; - var splitCommandLine = function(evt, field) { - var commandLine = evt.Get(field); + var splitCommandLine = function(evt, source, target) { + var commandLine = evt.Get(source); if (!commandLine) { return; } - evt.Put(field, winlogbeat.splitCommandLine(commandLine)); + evt.Put(target, winlogbeat.splitCommandLine(commandLine)); }; var splitProcessArgs = function(evt) { - splitCommandLine(evt, "process.args"); + splitCommandLine(evt, "process.command_line", "process.args"); }; var splitParentProcessArgs = function(evt) { - splitCommandLine(evt, "process.parent.args"); + splitCommandLine(evt, "process.parent.command_line", "process.parent.args"); }; var addUser = function(evt) { @@ -468,12 +468,12 @@ var sysmon = (function () { {from: "winlog.event_data.ProcessGuid", to: "process.entity_id"}, {from: "winlog.event_data.ProcessId", to: "process.pid", type: "long"}, {from: "winlog.event_data.Image", to: "process.executable"}, - {from: "winlog.event_data.CommandLine", to: "process.args"}, + {from: "winlog.event_data.CommandLine", to: "process.command_line"}, {from: "winlog.event_data.CurrentDirectory", to: "process.working_directory"}, {from: "winlog.event_data.ParentProcessGuid", to: "process.parent.entity_id"}, {from: "winlog.event_data.ParentProcessId", to: "process.parent.pid", type: "long"}, {from: "winlog.event_data.ParentImage", to: "process.parent.executable"}, - {from: "winlog.event_data.ParentCommandLine", to: "process.parent.args"}, + {from: "winlog.event_data.ParentCommandLine", to: "process.parent.command_line"}, ], mode: "rename", ignore_missing: true, diff --git a/x-pack/winlogbeat/module/sysmon/test/testdata/sysmon-9.01.evtx.golden.json b/x-pack/winlogbeat/module/sysmon/test/testdata/sysmon-9.01.evtx.golden.json index 52d0cbafb74..b083f5aba41 100644 --- a/x-pack/winlogbeat/module/sysmon/test/testdata/sysmon-9.01.evtx.golden.json +++ b/x-pack/winlogbeat/module/sysmon/test/testdata/sysmon-9.01.evtx.golden.json @@ -101,6 +101,7 @@ "args": [ "C:\\Windows\\Sysmon.exe" ], + "command_line": "C:\\Windows\\Sysmon.exe", "entity_id": "{42f11c3b-ce01-5c8f-0000-0010c73e2a00}", "executable": "C:\\Windows\\Sysmon.exe", "name": "Sysmon.exe", @@ -108,6 +109,7 @@ "args": [ "C:\\Windows\\system32\\services.exe" ], + "command_line": "C:\\Windows\\system32\\services.exe", "entity_id": "{42f11c3b-6e1a-5c8c-0000-0010f14d0000}", "executable": "C:\\Windows\\System32\\services.exe", "name": "services.exe", @@ -177,6 +179,7 @@ "C:\\Windows\\system32\\wbem\\unsecapp.exe", "-Embedding" ], + "command_line": "C:\\Windows\\system32\\wbem\\unsecapp.exe -Embedding", "entity_id": "{42f11c3b-ce01-5c8f-0000-00102c412a00}", "executable": "C:\\Windows\\System32\\wbem\\unsecapp.exe", "name": "unsecapp.exe", @@ -186,6 +189,7 @@ "-k", "DcomLaunch" ], + "command_line": "C:\\Windows\\system32\\svchost.exe -k DcomLaunch", "entity_id": "{42f11c3b-6e1b-5c8c-0000-00102f610000}", "executable": "C:\\Windows\\System32\\svchost.exe", "name": "svchost.exe", @@ -345,6 +349,7 @@ "C:\\Windows\\system32\\wbem\\wmiprvse.exe", "-Embedding" ], + "command_line": "C:\\Windows\\system32\\wbem\\wmiprvse.exe -Embedding", "entity_id": "{42f11c3b-ce03-5c8f-0000-0010e9462a00}", "executable": "C:\\Windows\\System32\\wbem\\WmiPrvSE.exe", "name": "WmiPrvSE.exe", @@ -354,6 +359,7 @@ "-k", "DcomLaunch" ], + "command_line": "C:\\Windows\\system32\\svchost.exe -k DcomLaunch", "entity_id": "{42f11c3b-6e1b-5c8c-0000-00102f610000}", "executable": "C:\\Windows\\System32\\svchost.exe", "name": "svchost.exe", diff --git a/x-pack/winlogbeat/winlogbeat.reference.yml b/x-pack/winlogbeat/winlogbeat.reference.yml index a7a7b562ca8..8db00a73f5a 100644 --- a/x-pack/winlogbeat/winlogbeat.reference.yml +++ b/x-pack/winlogbeat/winlogbeat.reference.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/winlogbeat/index.html -#======================= Winlogbeat specific options =========================== +# ======================== Winlogbeat specific options ========================= # The registry file is where Winlogbeat persists its state so that the beat can # resume after shutdown or an outage. The default is .winlogbeat.yml in the @@ -22,6 +22,7 @@ # forwarded, ignore_older, level, event_id, provider, and include_xml. Please # visit the documentation for the complete details of each option. # https://go.es.io/WinlogbeatConfig + winlogbeat.event_logs: - name: Application ignore_older: 72h @@ -42,9 +43,22 @@ winlogbeat.event_logs: id: sysmon file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js + - name: ForwardedEvents + tags: [forwarded] + processors: + - script: + when.equals.winlog.channel: Security + lang: javascript + id: security + file: ${path.home}/module/security/config/winlogbeat-security.js + - script: + when.equals.winlog.channel: Microsoft-Windows-Sysmon/Operational + lang: javascript + id: sysmon + file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js -#================================ General ====================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -152,7 +166,7 @@ winlogbeat.event_logs: # default is the number of logical CPUs available in the system. #max_procs: -#================================ Processors =================================== +# ================================= Processors ================================= # Processors are used to reduce the number of fields in the exported event or to # enhance the event with external metadata. This section defines a list of @@ -169,153 +183,153 @@ winlogbeat.event_logs: # values: # #processors: -#- include_fields: -# fields: ["cpu"] -#- drop_fields: -# fields: ["cpu.user", "cpu.system"] +# - include_fields: +# fields: ["cpu"] +# - drop_fields: +# fields: ["cpu.user", "cpu.system"] # # The following example drops the events that have the HTTP response code 200: # #processors: -#- drop_event: -# when: -# equals: -# http.code: 200 +# - drop_event: +# when: +# equals: +# http.code: 200 # # The following example renames the field a to b: # #processors: -#- rename: -# fields: -# - from: "a" -# to: "b" +# - rename: +# fields: +# - from: "a" +# to: "b" # # The following example tokenizes the string into fields: # #processors: -#- dissect: -# tokenizer: "%{key1} - %{key2}" -# field: "message" -# target_prefix: "dissect" +# - dissect: +# tokenizer: "%{key1} - %{key2}" +# field: "message" +# target_prefix: "dissect" # # The following example enriches each event with metadata from the cloud # provider about the host machine. It works on EC2, GCE, DigitalOcean, # Tencent Cloud, and Alibaba Cloud. # #processors: -#- add_cloud_metadata: ~ +# - add_cloud_metadata: ~ # # The following example enriches each event with the machine's local time zone # offset from UTC. # #processors: -#- add_locale: -# format: offset +# - add_locale: +# format: offset # # The following example enriches each event with docker metadata, it matches # given fields to an existing container id and adds info from that container: # #processors: -#- add_docker_metadata: -# host: "unix:///var/run/docker.sock" -# match_fields: ["system.process.cgroup.id"] -# match_pids: ["process.pid", "process.ppid"] -# match_source: true -# match_source_index: 4 -# match_short_id: false -# cleanup_timeout: 60 -# labels.dedot: false -# # To connect to Docker over TLS you must specify a client and CA certificate. -# #ssl: -# # certificate_authority: "/etc/pki/root/ca.pem" -# # certificate: "/etc/pki/client/cert.pem" -# # key: "/etc/pki/client/cert.key" +# - add_docker_metadata: +# host: "unix:///var/run/docker.sock" +# match_fields: ["system.process.cgroup.id"] +# match_pids: ["process.pid", "process.ppid"] +# match_source: true +# match_source_index: 4 +# match_short_id: false +# cleanup_timeout: 60 +# labels.dedot: false +# # To connect to Docker over TLS you must specify a client and CA certificate. +# #ssl: +# # certificate_authority: "/etc/pki/root/ca.pem" +# # certificate: "/etc/pki/client/cert.pem" +# # key: "/etc/pki/client/cert.key" # # The following example enriches each event with docker metadata, it matches # container id from log path available in `source` field (by default it expects # it to be /var/lib/docker/containers/*/*.log). # #processors: -#- add_docker_metadata: ~ +# - add_docker_metadata: ~ # # The following example enriches each event with host metadata. # #processors: -#- add_host_metadata: ~ +# - add_host_metadata: ~ # # The following example enriches each event with process metadata using # process IDs included in the event. # #processors: -#- add_process_metadata: -# match_pids: ["system.process.ppid"] -# target: system.process.parent +# - add_process_metadata: +# match_pids: ["system.process.ppid"] +# target: system.process.parent # # The following example decodes fields containing JSON strings # and replaces the strings with valid JSON objects. # #processors: -#- decode_json_fields: -# fields: ["field1", "field2", ...] -# process_array: false -# max_depth: 1 -# target: "" -# overwrite_keys: false +# - decode_json_fields: +# fields: ["field1", "field2", ...] +# process_array: false +# max_depth: 1 +# target: "" +# overwrite_keys: false # #processors: -#- decompress_gzip_field: -# from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - decompress_gzip_field: +# from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true # # The following example copies the value of message to message_copied # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: message_copied -# fail_on_error: true -# ignore_missing: false +# fail_on_error: true +# ignore_missing: false # # The following example truncates the value of message to 1024 bytes # #processors: -#- truncate_fields: -# fields: -# - message -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# - truncate_fields: +# fields: +# - message +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example preserves the raw message under event.original # #processors: -#- copy_fields: -# fields: +# - copy_fields: +# fields: # - from: message # to: event.original -# fail_on_error: false -# ignore_missing: true -#- truncate_fields: -# fields: -# - event.original -# max_bytes: 1024 -# fail_on_error: false -# ignore_missing: true +# fail_on_error: false +# ignore_missing: true +# - truncate_fields: +# fields: +# - event.original +# max_bytes: 1024 +# fail_on_error: false +# ignore_missing: true # # The following example URL-decodes the value of field1 to field2 # #processors: -#- urldecode: -# fields: -# - from: "field1" -# to: "field2" -# ignore_missing: false -# fail_on_error: true +# - urldecode: +# fields: +# - from: "field1" +# to: "field2" +# ignore_missing: false +# fail_on_error: true -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Winlogbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -328,11 +342,11 @@ winlogbeat.event_logs: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ====================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------- +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Boolean flag to enable or disable the output module. #enabled: true @@ -452,7 +466,28 @@ output.elasticsearch: # The pin is a base64 encoded string of the SHA-256 fingerprint. #ssl.ca_sha256: "" -#----------------------------- Logstash output --------------------------------- + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # Boolean flag to enable or disable the output module. #enabled: true @@ -566,7 +601,7 @@ output.elasticsearch: # timing out. The default is 30s. #timeout: 30s -#------------------------------- Kafka output ---------------------------------- +# -------------------------------- Kafka Output -------------------------------- #output.kafka: # Boolean flag to enable or disable the output module. #enabled: true @@ -720,6 +755,9 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + # Authentication type to use with Kerberos. Available options: keytab, password. #kerberos.auth_type: password @@ -742,7 +780,7 @@ output.elasticsearch: # Kerberos realm. #kerberos.realm: ELASTIC -#------------------------------- Redis output ---------------------------------- +# -------------------------------- Redis Output -------------------------------- #output.redis: # Boolean flag to enable or disable the output module. #enabled: true @@ -860,7 +898,7 @@ output.elasticsearch: # never, once, and freely. Default is never. #ssl.renegotiation: never -#------------------------------- File output ----------------------------------- +# -------------------------------- File Output --------------------------------- #output.file: # Boolean flag to enable or disable the output module. #enabled: true @@ -894,7 +932,7 @@ output.elasticsearch: # Permissions to use for file creation. The default is 0600. #permissions: 0600 -#----------------------------- Console output --------------------------------- +# ------------------------------- Console Output ------------------------------- #output.console: # Boolean flag to enable or disable the output module. #enabled: true @@ -907,7 +945,7 @@ output.elasticsearch: # Configure escaping HTML symbols in strings. #escape_html: false -#================================= Paths ====================================== +# =================================== Paths ==================================== # The home path for the Winlogbeat installation. This is the default base path # for all other path settings and for miscellaneous files that come with the @@ -933,11 +971,13 @@ output.elasticsearch: # the default for the logs path is a logs subdirectory inside the home path. #path.logs: ${path.home}/logs -#================================ Keystore ========================================== +# ================================== Keystore ================================== + # Location of the Keystore containing the keys and their sensitive values. #keystore.path: "${path.config}/beats.keystore" -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= + # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards are disabled by default and can be enabled either by setting the # options here, or by using the `-setup` CLI flag or the `setup` command. @@ -981,8 +1021,7 @@ output.elasticsearch: # Maximum number of retries before exiting with an error, 0 for unlimited retrying. #setup.dashboards.retry.maximum: 0 - -#============================== Template ===================================== +# ================================== Template ================================== # A template is used to set the mapping in Elasticsearch # By default template loading is enabled and the template is loaded. @@ -1036,7 +1075,7 @@ setup.template.settings: #_source: #enabled: false -#============================== Setup ILM ===================================== +# ====================== Index Lifecycle Management (ILM) ====================== # Configure index lifecycle management (ILM). These settings create a write # alias and add additional settings to the index template. When ILM is enabled, @@ -1050,13 +1089,13 @@ setup.template.settings: # Set the prefix used in the index lifecycle write alias name. The default alias # name is 'winlogbeat-%{[agent.version]}'. -#setup.ilm.rollover_alias: "winlogbeat" +#setup.ilm.rollover_alias: 'winlogbeat' # Set the rollover index pattern. The default is "%{now/d}-000001". #setup.ilm.pattern: "{now/d}-000001" # Set the lifecycle policy name. The default policy name is -# 'winlogbeat'. +# 'beatname'. #setup.ilm.policy_name: "mypolicy" # The path to a JSON file that contains a lifecycle policy configuration. Used @@ -1071,7 +1110,7 @@ setup.template.settings: # Overwrite the lifecycle policy at startup. The default is false. #setup.ilm.overwrite: false -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -1126,9 +1165,8 @@ setup.kibana: # Configure curve types for ECDHE-based cipher suites #ssl.curve_types: [] +# ================================== Logging =================================== - -#================================ Logging ====================================== # There are four options for the log output: file, stderr, syslog, eventlog # The file output is the default. @@ -1195,8 +1233,7 @@ logging.files: # Set to true to log messages in JSON format. #logging.json: false - -#============================== X-Pack Monitoring =============================== +# ============================= X-Pack Monitoring ============================== # Winlogbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -1306,6 +1343,27 @@ logging.files: # never, once, and freely. Default is never. #ssl.renegotiation: never + # Enable Kerberos support. Kerberos is automatically enabled if any Kerberos setting is set. + #kerberos.enabled: true + + # Authentication type to use with Kerberos. Available options: keytab, password. + #kerberos.auth_type: password + + # Path to the keytab file. It is used when auth_type is set to keytab. + #kerberos.keytab: /etc/elastic.keytab + + # Path to the Kerberos configuration. + #kerberos.config_path: /etc/krb5.conf + + # Name of the Kerberos user. + #kerberos.username: elastic + + # Password of the Kerberos user. It is used when auth_type is set to password. + #kerberos.password: changeme + + # Kerberos realm. + #kerberos.realm: ELASTIC + #metrics.period: 10s #state.period: 1m @@ -1317,7 +1375,8 @@ logging.files: # and `monitoring.elasticsearch.password` settings. The format is `:`. #monitoring.cloud.auth: -#================================ HTTP Endpoint ====================================== +# =============================== HTTP Endpoint ================================ + # Each beat can expose internal metrics through a HTTP endpoint. For security # reasons the endpoint is disabled by default. This feature is currently experimental. # Stats can be access through http://localhost:5066/stats . For pretty JSON output @@ -1341,12 +1400,13 @@ logging.files: # `http.user`. #http.named_pipe.security_descriptor: -#============================= Process Security ================================ +# ============================== Process Security ============================== # Enable or disable seccomp system call filtering on Linux. Default is enabled. #seccomp.enabled: true -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: false + diff --git a/x-pack/winlogbeat/winlogbeat.yml b/x-pack/winlogbeat/winlogbeat.yml index bc5dbc294d2..bb852a289db 100644 --- a/x-pack/winlogbeat/winlogbeat.yml +++ b/x-pack/winlogbeat/winlogbeat.yml @@ -7,7 +7,7 @@ # You can find the full configuration reference here: # https://www.elastic.co/guide/en/beats/winlogbeat/index.html -#======================= Winlogbeat specific options =========================== +# ======================== Winlogbeat specific options ========================= # event_logs specifies a list of event logs to monitor as well as any # accompanying options. The YAML data type of event_logs is a list of @@ -17,6 +17,7 @@ # forwarded, ignore_older, level, event_id, provider, and include_xml. Please # visit the documentation for the complete details of each option. # https://go.es.io/WinlogbeatConfig + winlogbeat.event_logs: - name: Application ignore_older: 72h @@ -37,7 +38,21 @@ winlogbeat.event_logs: id: sysmon file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js -#==================== Elasticsearch template settings ========================== + - name: ForwardedEvents + tags: [forwarded] + processors: + - script: + when.equals.winlog.channel: Security + lang: javascript + id: security + file: ${path.home}/module/security/config/winlogbeat-security.js + - script: + when.equals.winlog.channel: Microsoft-Windows-Sysmon/Operational + lang: javascript + id: sysmon + file: ${path.home}/module/sysmon/config/winlogbeat-sysmon.js + +# ====================== Elasticsearch template settings ======================= setup.template.settings: index.number_of_shards: 1 @@ -45,7 +60,7 @@ setup.template.settings: #_source.enabled: false -#================================ General ===================================== +# ================================== General =================================== # The name of the shipper that publishes the network data. It can be used to group # all the transactions sent by a single shipper in the web interface. @@ -60,8 +75,7 @@ setup.template.settings: #fields: # env: staging - -#============================== Dashboards ===================================== +# ================================= Dashboards ================================= # These settings control loading the sample dashboards to the Kibana index. Loading # the dashboards is disabled by default and can be enabled either by setting the # options here or by using the `setup` command. @@ -73,7 +87,7 @@ setup.template.settings: # website. #setup.dashboards.url: -#============================== Kibana ===================================== +# =================================== Kibana =================================== # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API. # This requires a Kibana endpoint configuration. @@ -90,7 +104,7 @@ setup.kibana: # the Default Space will be used. #space.id: -#============================= Elastic Cloud ================================== +# =============================== Elastic Cloud ================================ # These settings simplify using Winlogbeat with the Elastic Cloud (https://cloud.elastic.co/). @@ -103,11 +117,11 @@ setup.kibana: # `output.elasticsearch.password` settings. The format is `:`. #cloud.auth: -#================================ Outputs ===================================== +# ================================== Outputs =================================== # Configure what output to use when sending the data collected by the beat. -#-------------------------- Elasticsearch output ------------------------------ +# ---------------------------- Elasticsearch Output ---------------------------- output.elasticsearch: # Array of hosts to connect to. hosts: ["localhost:9200"] @@ -120,7 +134,7 @@ output.elasticsearch: #username: "elastic" #password: "changeme" -#----------------------------- Logstash output -------------------------------- +# ------------------------------ Logstash Output ------------------------------- #output.logstash: # The Logstash hosts #hosts: ["localhost:5044"] @@ -135,16 +149,13 @@ output.elasticsearch: # Client Certificate Key #ssl.key: "/etc/pki/client/cert.key" -#================================ Processors ===================================== - -# Configure processors to enhance or manipulate events generated by the beat. - +# ================================= Processors ================================= processors: - - add_host_metadata: ~ + - add_host_metadata: + when.not.contains.tags: forwarded - add_cloud_metadata: ~ - - add_docker_metadata: ~ -#================================ Logging ===================================== +# ================================== Logging =================================== # Sets log level. The default log level is info. # Available log levels are: error, warning, info, debug @@ -155,8 +166,8 @@ processors: # "publish", "service". #logging.selectors: ["*"] -#============================== X-Pack Monitoring =============================== -# winlogbeat can export internal metrics to a central Elasticsearch monitoring +# ============================= X-Pack Monitoring ============================== +# Winlogbeat can export internal metrics to a central Elasticsearch monitoring # cluster. This requires xpack monitoring to be enabled in Elasticsearch. The # reporting is disabled by default. @@ -177,7 +188,8 @@ processors: # uncomment the following line. #monitoring.elasticsearch: -#================================= Migration ================================== +# ================================= Migration ================================== # This allows to enable 6.7 migration aliases #migration.6_to_7.enabled: true +