Add o365 script (#2)

* Add o365 script
* Add Jenkinsfile
This commit is contained in:
Alexey Pronin 2020-11-18 15:50:17 +03:00 committed by GitHub
parent de6d0ba8a9
commit 27dc788514
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 355 additions and 6 deletions

4
.gitmodules vendored Normal file
View File

@ -0,0 +1,4 @@
[submodule "build_utils"]
path = build_utils
url = git@github.com:rbkmoney/build_utils.git
branch = master

41
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,41 @@
#!groovy
// -*- mode: groovy -*-
build('wazuh-docker', 'docker-host') {
checkoutRepo()
loadBuildUtils()
def pipeDefault
def withWsCache
runStage('load pipeline') {
env.JENKINS_LIB = "build_utils/jenkins_lib"
pipeDefault = load("${env.JENKINS_LIB}/pipeDefault.groovy")
withWsCache = load("${env.JENKINS_LIB}/withWsCache.groovy")
}
pipeDefault() {
runStage('build wazuh image') {
sh "cd wazuh && make build_image"
}
runStage('build kibana image') {
sh "cd kibana && make build_image"
}
try {
if (masterlikeBranch()) {
runStage('push wazuh image') {
sh "cd wazuh && make push_image"
}
runStage('push kibana image') {
sh "cd kibana && make push_image"
}
}
} finally {
runStage('rm wazuh local image') {
sh 'cd wazuh && make rm_local_image'
}
runStage('rm kibana local image') {
sh 'cd kibana && make rm_local_image'
}
}
}
}

1
build_utils Submodule

@ -0,0 +1 @@
Subproject commit ccf618949b95590d572157b248289428abeaa2e5

8
kibana/Dockerfile → kibana/Dockerfile.sh Normal file → Executable file
View File

@ -1,5 +1,6 @@
#!/bin/bash
# Wazuh Docker Copyright (C) 2020 Wazuh Inc. (License GPLv2)
cat <<EOF
FROM centos:7 as plugin_builder
ARG KIBANA_PLUGIN_VERSION="v3.12.3-6.8.1"
@ -12,13 +13,13 @@ RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 && \
ADD ./config/build.sh /
RUN chmod +x /build.sh && \
mkdir /wazuh_app /source && \
/build.sh ${KIBANA_PLUGIN_VERSION}
/build.sh \${KIBANA_PLUGIN_VERSION}
FROM docker.elastic.co/kibana/kibana-oss:6.8.1
USER kibana
ARG ELASTIC_VERSION=6.8.1
ARG WAZUH_VERSION=3.12.3
ARG WAZUH_APP_VERSION="${WAZUH_VERSION}_${ELASTIC_VERSION}"
ARG WAZUH_APP_VERSION="\${WAZUH_VERSION}_\${ELASTIC_VERSION}"
COPY --from=plugin_builder /wazuh_app/wazuh_kibana-*.zip /tmp/wazuh_kibana.zip
@ -90,3 +91,4 @@ USER kibana
RUN NODE_OPTIONS="--max-old-space-size=2048" /usr/local/bin/kibana-docker --optimize
ENTRYPOINT ./entrypoint.sh
EOF

33
kibana/Makefile Normal file
View File

@ -0,0 +1,33 @@
SUBMODULES = build_utils
SUBTARGETS = $(patsubst %,%/.git,$(SUBMODULES))
UTILS_PATH := ../build_utils
TEMPLATES_PATH := .
# Name of the service
SERVICE_NAME := wazuh-kibana
# Service image default tag
SERVICE_IMAGE_TAG ?= $(shell git rev-parse HEAD)
# The tag for service image to be pushed with
SERVICE_IMAGE_PUSH_TAG ?= $(SERVICE_IMAGE_TAG)
# Base image for the service
BASE_IMAGE_NAME := empty
BASE_IMAGE_TAG := empty
BUILD_IMAGE_TAG := empty
CALL_ANYWHERE := all
CALL_W_CONTAINER := $(CALL_ANYWHERE)
-include $(UTILS_PATH)/make_lib/utils_container.mk
-include $(UTILS_PATH)/make_lib/utils_image.mk
.PHONY: $(CALL_W_CONTAINER)
# CALL_ANYWHERE
$(SUBTARGETS): %/.git: %
git submodule update --init $<
touch $@
submodules: $(SUBTARGETS)

View File

@ -73,10 +73,10 @@ RUN mkdir -p /etc/service/wazuh && \
COPY config/wazuh.runit.service /etc/service/wazuh/run
COPY config/wazuh-api.runit.service /etc/service/wazuh-api/run
COPY config/filebeat.runit.service /etc/service/filebeat/run
COPY config/office365.py /var/ossec/bin/office365.py
RUN chmod +x /etc/service/wazuh-api/run && \
chmod +x /etc/service/wazuh/run && \
chmod +x /etc/service/filebeat/run
RUN chmod +x /etc/service/wazuh-api/run /etc/service/wazuh/run \
/etc/service/filebeat/run /var/ossec/bin/office365.py
# Run all services
ENTRYPOINT ["/entrypoint.sh"]

85
wazuh/Dockerfile.sh Executable file
View File

@ -0,0 +1,85 @@
#!/bin/bash
# Wazuh Docker Copyright (C) 2020 Wazuh Inc. (License GPLv2)
cat <<EOF
FROM phusion/baseimage:bionic-1.0.0
ARG FILEBEAT_VERSION=6.8.1
ARG WAZUH_VERSION=3.12.3-1
ENV WAZUH_FILEBEAT_MODULE=wazuh-filebeat-0.1.tar.gz
ENV API_USER="foo" \
API_PASS="bar"
ARG TEMPLATE_VERSION="v3.12.3"
# Set repositories.
RUN apt update && apt-get upgrade -y -o Dpkg::Options::="--force-confold" && \
apt -y install curl apt-transport-https lsb-release gnupg2 software-properties-common && \
set -x && echo "deb https://packages.wazuh.com/3.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.list && \
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add - && \
curl --silent --location https://deb.nodesource.com/setup_10.x | bash - && \
groupadd -g 1000 ossec && useradd -u 1000 -g 1000 -d /var/ossec ossec
RUN add-apt-repository universe && apt-get update && apt-get upgrade -y -o Dpkg::Options::="--force-confold" && \
apt-get --no-install-recommends --no-install-suggests -y install openssl python-boto python-pip \
apt-transport-https vim expect nodejs python-cryptography libsasl2-modules wazuh-manager=\${WAZUH_VERSION} \
wazuh-api=\${WAZUH_VERSION} && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && rm -f \
/var/ossec/logs/alerts/*/*/*.log && rm -f /var/ossec/logs/alerts/*/*/*.json && rm -f \
/var/ossec/logs/archives/*/*/*.log && rm -f /var/ossec/logs/archives/*/*/*.json && rm -f \
/var/ossec/logs/firewall/*/*/*.log && rm -f /var/ossec/logs/firewall/*/*/*.json
# Install Wazuh Filebeat Module
RUN mkdir -p /usr/share/filebeat/module/wazuh && \
curl -s "https://packages.wazuh.com/3.x/filebeat/\${WAZUH_FILEBEAT_MODULE}" | tar -xvz -C /usr/share/filebeat/module && \
chmod 755 -R /usr/share/filebeat/module/wazuh
RUN curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-oss-\${FILEBEAT_VERSION}-amd64.deb && \
dpkg -i filebeat-oss-\${FILEBEAT_VERSION}-amd64.deb && rm -f filebeat-oss-\${FILEBEAT_VERSION}-amd64.deb
RUN curl -sL https://raw.githubusercontent.com/wazuh/wazuh/\$TEMPLATE_VERSION/extensions/elasticsearch/6.x/wazuh-template.json -o /etc/filebeat/wazuh-template.json && \
sed -i 's@"wazuh":@"doc":@' /etc/filebeat/wazuh-template.json && \
chmod go-w /etc/filebeat/wazuh-template.json
# Adding first run script and entrypoint
COPY config/data_dirs.env /data_dirs.env
COPY config/init.bash /init.bash
RUN mkdir /entrypoint-scripts
COPY config/entrypoint.sh /entrypoint.sh
COPY config/00-wazuh.sh /entrypoint-scripts/00-wazuh.sh
# Sync calls are due to https://github.com/docker/docker/issues/9547
RUN chmod 755 /entrypoint.sh && \
chmod 755 /entrypoint-scripts/00-wazuh.sh
COPY config/filebeat.yml /etc/filebeat/
RUN chmod go-w /etc/filebeat/filebeat.yml
# Setting volumes
VOLUME ["/etc/filebeat"]
VOLUME ["/var/lib/filebeat"]
VOLUME ["/var/log/filebeat/"]
VOLUME ["/var/ossec/data/logs/"]
VOLUME ["/wazuh-config-mount"]
# Services ports
EXPOSE 514/udp 1514/udp 1514/tcp 1515/tcp 1516/tcp 55000/tcp
# Adding services
RUN mkdir -p /etc/service/wazuh && \
mkdir /etc/service/wazuh-api && \
mkdir /etc/service/filebeat
COPY config/wazuh.runit.service /etc/service/wazuh/run
COPY config/wazuh-api.runit.service /etc/service/wazuh-api/run
COPY config/filebeat.runit.service /etc/service/filebeat/run
COPY config/office365.py /var/ossec/bin/office365.py
RUN chmod +x /etc/service/wazuh-api/run /etc/service/wazuh/run \
/etc/service/filebeat/run /var/ossec/bin/office365.py
# Run all services
ENTRYPOINT ["/entrypoint.sh"]
EOF

33
wazuh/Makefile Normal file
View File

@ -0,0 +1,33 @@
SUBMODULES = build_utils
SUBTARGETS = $(patsubst %,%/.git,$(SUBMODULES))
UTILS_PATH := ../build_utils
TEMPLATES_PATH := .
# Name of the service
SERVICE_NAME := wazuh
# Service image default tag
SERVICE_IMAGE_TAG ?= $(shell git rev-parse HEAD)
# The tag for service image to be pushed with
SERVICE_IMAGE_PUSH_TAG ?= $(SERVICE_IMAGE_TAG)
# Base image for the service
BASE_IMAGE_NAME := empty
BASE_IMAGE_TAG := empty
BUILD_IMAGE_TAG := empty
CALL_ANYWHERE := all
CALL_W_CONTAINER := $(CALL_ANYWHERE)
-include $(UTILS_PATH)/make_lib/utils_container.mk
-include $(UTILS_PATH)/make_lib/utils_image.mk
.PHONY: $(CALL_W_CONTAINER)
# CALL_ANYWHERE
$(SUBTARGETS): %/.git: %
git submodule update --init $<
touch $@
submodules: $(SUBTARGETS)

150
wazuh/config/office365.py Normal file
View File

@ -0,0 +1,150 @@
#!/var/ossec/framework/python/bin/python3
# Copyright (C) 2015-2020, Wazuh Inc.
# Created by Wazuh, Inc. <info@wazuh.com>.
# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2
import argparse
import sys
import json
import requests
import logging
import datetime
from socket import socket, AF_UNIX, SOCK_DGRAM
################################################## Global variables ##################################################
# Microsoft resource
resource = "https://manage.office.com"
# Office 365 management activity API available content types
availableContentTypes = ["Audit.AzureActiveDirectory", "Audit.Exchange", "Audit.SharePoint", "Audit.General", "DLP.All"]
# Wazuh manager analisysd socket address
socketAddr = '/var/ossec/queue/ossec/queue'
################################################## Common functions ##################################################
# Send event to Wazuh manager
def send_event(msg):
logging.debug('Sending {} to {} socket.'.format(msg, socketAddr))
string = '1:office_365:{}'.format(msg)
sock = socket(AF_UNIX, SOCK_DGRAM)
sock.connect(socketAddr)
sock.send(string.encode())
sock.close()
# Perform HTTP request
def make_request(method, url, headers, data=None):
response = requests.request(method, url, headers=headers, data=data)
# If the request succeed
if response.status_code >= 200 and response.status_code < 210:
return response
if method == "POST" and response.status_code == 400:
return response
else:
raise Exception('Request ', method, ' ', url, ' failed with ', response.status_code, ' - ', response.text)
# Obtain a token for accessing the Office 365 management activity API
def obtain_access_token(tenantId, clientId, clientSecret):
# Add header and payload
headers = {'Content-Type':'application/x-www-form-urlencoded'}
payload = 'client_id={}&scope={}/.default&grant_type=client_credentials&client_secret={}'.format(clientId, resource, clientSecret)
# Request token
response = make_request("POST", "https://login.microsoftonline.com/{}/oauth2/v2.0/token".format(tenantId), headers=headers, data=payload)
logging.info("Microsoft token was successfully fetched.")
return json.loads(response.text)['access_token']
# Perform an API request to Office 365 management API
def make_api_request(method, url, token):
# Create a valid header using the token
headers = {'Content-Type':'application/json', 'Authorization':'Bearer {0}'.format(token)}
# Make API request
response = make_request(method, url, headers=headers)
# If this is a POST request just return
if (method == "POST"):
return None
json_data = json.loads(response.text)
# If NextPageUri is included in the header and it has content in it
if 'NextPageUri' in response.headers.keys() and response.headers['NextPageUri']:
logging.info("New data page detected in {}.".format(url))
# Request new page and append to existing data
record = make_api_request(method, response.headers['NextPageUri'], token)
json_data.extend(record)
return json_data
# Manage content type subscriptions
def manage_content_type_subscriptions(contentTypes, clientId, token):
# For every available content type
for contentType in availableContentTypes:
# If it was added as a parameter then start the subscription
if contentType in contentTypes:
make_api_request("POST", "{}/api/v1.0/{}/activity/feed/subscriptions/start?contentType={}".format(resource, clientId, contentType), token)
logging.info("{} subscription was successfully started.".format(contentType))
################################################## Main workflow ##################################################
if __name__ == "__main__":
# Parse arguments
parser = argparse.ArgumentParser(description='Wazuh - Office 365 activity information.')
parser.add_argument('--contentTypes', metavar='contentTypes', type=str, nargs='+', required = True, help='Office 365 activity content type subscriptions.')
parser.add_argument('--hours', metavar='hours', type=int, required = True, help='How many hours to fetch activity logs.')
parser.add_argument('--tenantId', metavar='tenantId', type=str, required = True, help='Application tenant ID.')
parser.add_argument('--clientId', metavar='clientId', type=str, required = True, help='Application client ID.')
parser.add_argument('--clientSecret', metavar='clientSecret', type=str, required = True, help='Client secret.')
parser.add_argument('--debug', action='store_true', required = False, help='Enable debug mode logging.')
args = parser.parse_args()
# Start logging config
if args.debug:
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s: [%(levelname)s] %(message)s', datefmt="%Y-%m-%d %H:%M:%S",)
else:
logging.basicConfig(level=logging.INFO, format='%(asctime)s: [%(levelname)s] %(message)s', datefmt="%Y-%m-%d %H:%M:%S",)
# Disable warnings
requests.packages.urllib3.disable_warnings()
try:
# Obtain access token
token = obtain_access_token(args.tenantId, args.clientId, args.clientSecret)
response =make_api_request("GET", "{}/api/v1.0/{}/activity/feed/subscriptions/list".format(resource, args.clientId), token)
# Start/stop subscriptions depending on the content_types parameter
manage_content_type_subscriptions(args.contentTypes, args.clientId, token)
# Build time range filter
currentTime = datetime.datetime.now(datetime.timezone.utc)
endTime = str(currentTime).replace(' ', 'T').rsplit('.', maxsplit=1)[0]
startTime = str(currentTime - datetime.timedelta(hours=args.hours)).replace(' ', 'T').rsplit('.', maxsplit=1)[0]
# For every content_type in the content_types parameter
for contentType in args.contentTypes:
# If it is a valid content_type
if contentType in availableContentTypes:
# List the subscription content
subscription_content = make_api_request("GET", "{}/api/v1.0/{}/activity/feed/subscriptions/content?contentType={}&startTime={}&endTime={}".format(resource, args.clientId, contentType, startTime, endTime), token)
logging.info("{} subscription was successfully listed.".format(contentType))
# For every blob in the subscription
for blob in subscription_content:
# Request activity information
data = make_api_request("GET", blob["contentUri"], token)
logging.info("Blob in {} subscription was successfully fetched.".format(contentType))
# Loop every event and send it to the Wazuh manager
for event in data:
office_365_event = {}
office_365_event['office_365'] = event
send_event(json.dumps(office_365_event))
except Exception as e:
logging.error("Error while retrieving Office 365 activity logs: {}.".format(e))