diff options
37 files changed, 2574 insertions, 0 deletions
diff --git a/samples/scenarios/service_delivery_controller.yaml b/samples/scenarios/service_delivery_controller.yaml new file mode 100644 index 0000000..c3a9411 --- /dev/null +++ b/samples/scenarios/service_delivery_controller.yaml @@ -0,0 +1 @@ +<Toplevel yaml for entire sample scenario (applciation) goes here> diff --git a/samples/services/nginx/docker/build_lb.sh b/samples/services/nginx/docker/build_lb.sh new file mode 100755 index 0000000..46efec0 --- /dev/null +++ b/samples/services/nginx/docker/build_lb.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-ns-nginx-lb"} + +docker build -f subservices/lb/Dockerfile -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/samples/services/nginx/docker/build_proxy.sh b/samples/services/nginx/docker/build_proxy.sh new file mode 100755 index 0000000..6b6937c --- /dev/null +++ b/samples/services/nginx/docker/build_proxy.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-ns-nginx-proxy"} + +docker build -f subservices/proxy/Dockerfile -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/samples/services/nginx/docker/build_server.sh b/samples/services/nginx/docker/build_server.sh new file mode 100755 index 0000000..a884213 --- /dev/null +++ b/samples/services/nginx/docker/build_server.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-ns-nginx-server"} + +docker build -f subservices/server/Dockerfile -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/samples/services/nginx/docker/grpc/build_proto.sh b/samples/services/nginx/docker/grpc/build_proto.sh new file mode 100755 index 0000000..a98080a --- /dev/null +++ b/samples/services/nginx/docker/grpc/build_proto.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. nginx.proto diff --git a/samples/services/nginx/docker/grpc/nginx.proto b/samples/services/nginx/docker/grpc/nginx.proto new file mode 100644 index 0000000..3779a82 --- /dev/null +++ b/samples/services/nginx/docker/grpc/nginx.proto @@ -0,0 +1,51 @@ +// Copyright (c) Authors of Clover +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Apache License, Version 2.0 +// which accompanies this distribution, and is available at +// http://www.apache.org/licenses/LICENSE-2.0 + +syntax = "proto3"; + +package nginx; + +// The controller service definition. +service Controller { + + rpc ModifyProxy (ConfigProxy) returns (NginxReply) {} + rpc ModifyServer (ConfigServer) returns (NginxReply) {} + rpc ModifyLB (ConfigLB) returns (NginxReply) {} + rpc ProcessAlerts (AlertMessage) returns (NginxReply) {} +} + +message AlertMessage { + string event_id = 1; + string redis_key = 2; +} + +message ConfigProxy { + string server_port = 1; + string server_name = 2; + string location_path = 3; + string proxy_path = 4; + string mirror_path = 5; +} + +message ConfigServer { + string server_port = 1; + string server_name = 2; + string site_root = 3; + string site_index = 4; +} + +message ConfigLB { + string server_port = 1; + string server_name = 2; + string slb_list = 3; + string slb_group = 4; + string lb_path = 5; +} + +message NginxReply { + string message = 1; +} diff --git a/samples/services/nginx/docker/grpc/nginx_client.py b/samples/services/nginx/docker/grpc/nginx_client.py new file mode 100644 index 0000000..dfefb08 --- /dev/null +++ b/samples/services/nginx/docker/grpc/nginx_client.py @@ -0,0 +1,87 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +from __future__ import print_function +from kubernetes import client, config + +import grpc +import argparse +import pickle + +import nginx_pb2 +import nginx_pb2_grpc + + +def run(args): + # get pod ip for grpc + pod_ip = get_podip(args['service_name']) + if pod_ip == '': + return "Cant find service with name: {}".format(args['service_name']) + nginx_grpc = pod_ip + ':50054' + channel = grpc.insecure_channel(nginx_grpc) + stub = nginx_pb2_grpc.ControllerStub(channel) + + # modify config + if args['service_type'] == 'lb': + modify_lb(stub) + elif args['service_type'] == 'proxy': + modify_proxy(stub) + elif args['service_type'] == 'server': + modify_server(stub) + else: + return "Invalid service type: {}".format(args['service_type']) + return "Modification complete" + + +def get_podip(pod_name): + config.load_kube_config() + v1 = client.CoreV1Api() + ret = v1.list_pod_for_all_namespaces(watch=False) + ip = '' + for i in ret.items: + if i.metadata.name.lower().find(pod_name.lower()) != -1: + print(i.status.pod_ip) + ip = i.status.pod_ip + return str(ip) + + +def modify_proxy(stub): + response = stub.ModifyProxy(nginx_pb2.ConfigProxy( + server_port='9180', server_name='http-proxy', + location_path='/', proxy_path='http://clover-server:9180', + mirror_path='http://snort-ids:80')) + print(response.message) + + +def modify_server(stub): + response = stub.ModifyServer(nginx_pb2.ConfigServer( + server_port='9180', server_name='clover-server', + site_root='/var/www/html', site_index='index.nginx-debian.html')) + print(response.message) + + +def modify_lb(stub): + slb_list = pickle.dumps( + ['clover-server1', 'clover-server2', 'clover-server3']) + response = stub.ModifyLB(nginx_pb2.ConfigLB( + server_port='9188', server_name='http-lb', + slb_list=slb_list, + slb_group='cloverlb', lb_path='/')) + print(response.message) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--service_type', required=True, + help='The service to reconfigure') + parser.add_argument( + '--service_name', required=True, + help='The service to reconfigure') + + args = parser.parse_args() + print(run(vars(args))) diff --git a/samples/services/nginx/docker/grpc/nginx_grpc_server.py b/samples/services/nginx/docker/grpc/nginx_grpc_server.py new file mode 100644 index 0000000..6f2de0f --- /dev/null +++ b/samples/services/nginx/docker/grpc/nginx_grpc_server.py @@ -0,0 +1,142 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + + +from concurrent import futures +import time +import sys +import grpc +import subprocess +import pickle +import logging +import nginx_pb2 +import nginx_pb2_grpc + +from jinja2 import Template + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 +GRPC_PORT = '[::]:50054' + + +class Controller(nginx_pb2_grpc.ControllerServicer): + + def __init__(self, service_type): + logging.basicConfig(filename='nginx.log', level=logging.DEBUG) + self.service_type = service_type + self.out_file = '/etc/nginx/nginx.conf' + # self.out_file = 'testfile' + if service_type == "proxy": + # self.template_file = 'templates/proxy.template' + self.template_file = '/grpc/templates/proxy.template' + self.ModifyProxy(nginx_pb2.ConfigProxy( + server_port='9180', server_name='http-proxy', + location_path='/', proxy_path='http://clover-server:9180', + mirror_path='http://snort-ids:80'), "") + if service_type == "server": + # self.template_file = 'templates/server.template' + self.template_file = '/grpc/templates/server.template' + self.ModifyServer(nginx_pb2.ConfigServer( + server_port='9180', server_name='clover-server', + site_root='/var/www/html', + site_index='index.nginx-debian.html'), "") + if service_type == "lb": + # self.template_file = 'templates/lb.template' + self.template_file = '/grpc/templates/lb.template' + slb_list = pickle.dumps( + ['clover-server1', 'clover-server2', 'clover-server3']) + self.ModifyLB(nginx_pb2.ConfigLB( + server_port='9180', server_name='http-lb', + slb_list=slb_list, + slb_group='cloverlb', lb_path='/'), "") + + def ModifyProxy(self, r, context): + try: + with open(self.template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + server_port=r.server_port, + server_name=r.server_name, + location_path=r.location_path, + proxy_path=r.proxy_path, + mirror_path=r.mirror_path + ) + with open(self.out_file, "wb") as fh: + fh.write(output) + msg = "Modified nginx config" + self.RestartNginx() + except Exception as e: + logging.debug(e) + msg = "Failed to modify nginx config" + return nginx_pb2.NginxReply(message=msg) + + def ModifyServer(self, r, context): + try: + with open(self.template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + server_port=r.server_port, + server_name=r.server_name, + site_root=r.site_root, + site_index=r.site_index + ) + with open(self.out_file, "wb") as fh: + fh.write(output) + msg = "Modified nginx config" + self.RestartNginx() + except Exception as e: + logging.debug(e) + msg = "Failed to modify nginx config" + return nginx_pb2.NginxReply(message=msg) + + def ModifyLB(self, r, context): + try: + with open(self.template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + server_port=r.server_port, + server_name=r.server_name, + slb_list=pickle.loads(r.slb_list), + slb_group=r.slb_group, + lb_path=r.lb_path + ) + with open(self.out_file, "wb") as fh: + fh.write(output) + msg = "Modified nginx config" + self.RestartNginx() + except Exception as e: + logging.debug(e) + msg = "Failed to modify nginx config" + return nginx_pb2.NginxReply(message=msg) + + def RestartNginx(self): + subprocess.Popen( + ["service nginx restart"], shell=True) + + def ProcessAlerts(self, request, context): + try: + msg = "Processed alert" + except Exception as e: + logging.debug(e) + msg = "Failed to process alert" + return nginx_pb2.NginxReply(message=msg) + + +def serve(service_type): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + nginx_pb2_grpc.add_ControllerServicer_to_server( + Controller(service_type), server) + server.add_insecure_port(GRPC_PORT) + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop(0) + + +if __name__ == '__main__': + serve(sys.argv[1]) diff --git a/samples/services/nginx/docker/grpc/nginx_pb2.py b/samples/services/nginx/docker/grpc/nginx_pb2.py new file mode 100644 index 0000000..3600b6d --- /dev/null +++ b/samples/services/nginx/docker/grpc/nginx_pb2.py @@ -0,0 +1,360 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: nginx.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='nginx.proto', + package='nginx', + syntax='proto3', + serialized_pb=_b('\n\x0bnginx.proto\x12\x05nginx\"3\n\x0c\x41lertMessage\x12\x10\n\x08\x65vent_id\x18\x01 \x01(\t\x12\x11\n\tredis_key\x18\x02 \x01(\t\"w\n\x0b\x43onfigProxy\x12\x13\n\x0bserver_port\x18\x01 \x01(\t\x12\x13\n\x0bserver_name\x18\x02 \x01(\t\x12\x15\n\rlocation_path\x18\x03 \x01(\t\x12\x12\n\nproxy_path\x18\x04 \x01(\t\x12\x13\n\x0bmirror_path\x18\x05 \x01(\t\"_\n\x0c\x43onfigServer\x12\x13\n\x0bserver_port\x18\x01 \x01(\t\x12\x13\n\x0bserver_name\x18\x02 \x01(\t\x12\x11\n\tsite_root\x18\x03 \x01(\t\x12\x12\n\nsite_index\x18\x04 \x01(\t\"j\n\x08\x43onfigLB\x12\x13\n\x0bserver_port\x18\x01 \x01(\t\x12\x13\n\x0bserver_name\x18\x02 \x01(\t\x12\x10\n\x08slb_list\x18\x03 \x01(\t\x12\x11\n\tslb_group\x18\x04 \x01(\t\x12\x0f\n\x07lb_path\x18\x05 \x01(\t\"\x1d\n\nNginxReply\x12\x0f\n\x07message\x18\x01 \x01(\t2\xeb\x01\n\nController\x12\x36\n\x0bModifyProxy\x12\x12.nginx.ConfigProxy\x1a\x11.nginx.NginxReply\"\x00\x12\x38\n\x0cModifyServer\x12\x13.nginx.ConfigServer\x1a\x11.nginx.NginxReply\"\x00\x12\x30\n\x08ModifyLB\x12\x0f.nginx.ConfigLB\x1a\x11.nginx.NginxReply\"\x00\x12\x39\n\rProcessAlerts\x12\x13.nginx.AlertMessage\x1a\x11.nginx.NginxReply\"\x00\x62\x06proto3') +) + + + + +_ALERTMESSAGE = _descriptor.Descriptor( + name='AlertMessage', + full_name='nginx.AlertMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='event_id', full_name='nginx.AlertMessage.event_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='redis_key', full_name='nginx.AlertMessage.redis_key', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=73, +) + + +_CONFIGPROXY = _descriptor.Descriptor( + name='ConfigProxy', + full_name='nginx.ConfigProxy', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='server_port', full_name='nginx.ConfigProxy.server_port', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='server_name', full_name='nginx.ConfigProxy.server_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='location_path', full_name='nginx.ConfigProxy.location_path', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='proxy_path', full_name='nginx.ConfigProxy.proxy_path', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='mirror_path', full_name='nginx.ConfigProxy.mirror_path', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=75, + serialized_end=194, +) + + +_CONFIGSERVER = _descriptor.Descriptor( + name='ConfigServer', + full_name='nginx.ConfigServer', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='server_port', full_name='nginx.ConfigServer.server_port', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='server_name', full_name='nginx.ConfigServer.server_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='site_root', full_name='nginx.ConfigServer.site_root', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='site_index', full_name='nginx.ConfigServer.site_index', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=196, + serialized_end=291, +) + + +_CONFIGLB = _descriptor.Descriptor( + name='ConfigLB', + full_name='nginx.ConfigLB', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='server_port', full_name='nginx.ConfigLB.server_port', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='server_name', full_name='nginx.ConfigLB.server_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='slb_list', full_name='nginx.ConfigLB.slb_list', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='slb_group', full_name='nginx.ConfigLB.slb_group', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='lb_path', full_name='nginx.ConfigLB.lb_path', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=293, + serialized_end=399, +) + + +_NGINXREPLY = _descriptor.Descriptor( + name='NginxReply', + full_name='nginx.NginxReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='nginx.NginxReply.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=401, + serialized_end=430, +) + +DESCRIPTOR.message_types_by_name['AlertMessage'] = _ALERTMESSAGE +DESCRIPTOR.message_types_by_name['ConfigProxy'] = _CONFIGPROXY +DESCRIPTOR.message_types_by_name['ConfigServer'] = _CONFIGSERVER +DESCRIPTOR.message_types_by_name['ConfigLB'] = _CONFIGLB +DESCRIPTOR.message_types_by_name['NginxReply'] = _NGINXREPLY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +AlertMessage = _reflection.GeneratedProtocolMessageType('AlertMessage', (_message.Message,), dict( + DESCRIPTOR = _ALERTMESSAGE, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.AlertMessage) + )) +_sym_db.RegisterMessage(AlertMessage) + +ConfigProxy = _reflection.GeneratedProtocolMessageType('ConfigProxy', (_message.Message,), dict( + DESCRIPTOR = _CONFIGPROXY, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.ConfigProxy) + )) +_sym_db.RegisterMessage(ConfigProxy) + +ConfigServer = _reflection.GeneratedProtocolMessageType('ConfigServer', (_message.Message,), dict( + DESCRIPTOR = _CONFIGSERVER, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.ConfigServer) + )) +_sym_db.RegisterMessage(ConfigServer) + +ConfigLB = _reflection.GeneratedProtocolMessageType('ConfigLB', (_message.Message,), dict( + DESCRIPTOR = _CONFIGLB, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.ConfigLB) + )) +_sym_db.RegisterMessage(ConfigLB) + +NginxReply = _reflection.GeneratedProtocolMessageType('NginxReply', (_message.Message,), dict( + DESCRIPTOR = _NGINXREPLY, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.NginxReply) + )) +_sym_db.RegisterMessage(NginxReply) + + + +_CONTROLLER = _descriptor.ServiceDescriptor( + name='Controller', + full_name='nginx.Controller', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=433, + serialized_end=668, + methods=[ + _descriptor.MethodDescriptor( + name='ModifyProxy', + full_name='nginx.Controller.ModifyProxy', + index=0, + containing_service=None, + input_type=_CONFIGPROXY, + output_type=_NGINXREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='ModifyServer', + full_name='nginx.Controller.ModifyServer', + index=1, + containing_service=None, + input_type=_CONFIGSERVER, + output_type=_NGINXREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='ModifyLB', + full_name='nginx.Controller.ModifyLB', + index=2, + containing_service=None, + input_type=_CONFIGLB, + output_type=_NGINXREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='ProcessAlerts', + full_name='nginx.Controller.ProcessAlerts', + index=3, + containing_service=None, + input_type=_ALERTMESSAGE, + output_type=_NGINXREPLY, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTROLLER) + +DESCRIPTOR.services_by_name['Controller'] = _CONTROLLER + +# @@protoc_insertion_point(module_scope) diff --git a/samples/services/nginx/docker/grpc/nginx_pb2_grpc.py b/samples/services/nginx/docker/grpc/nginx_pb2_grpc.py new file mode 100644 index 0000000..2b36fc0 --- /dev/null +++ b/samples/services/nginx/docker/grpc/nginx_pb2_grpc.py @@ -0,0 +1,97 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import nginx_pb2 as nginx__pb2 + + +class ControllerStub(object): + """The controller service definition. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.ModifyProxy = channel.unary_unary( + '/nginx.Controller/ModifyProxy', + request_serializer=nginx__pb2.ConfigProxy.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + self.ModifyServer = channel.unary_unary( + '/nginx.Controller/ModifyServer', + request_serializer=nginx__pb2.ConfigServer.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + self.ModifyLB = channel.unary_unary( + '/nginx.Controller/ModifyLB', + request_serializer=nginx__pb2.ConfigLB.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + self.ProcessAlerts = channel.unary_unary( + '/nginx.Controller/ProcessAlerts', + request_serializer=nginx__pb2.AlertMessage.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + + +class ControllerServicer(object): + """The controller service definition. + """ + + def ModifyProxy(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ModifyServer(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ModifyLB(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ProcessAlerts(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ControllerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'ModifyProxy': grpc.unary_unary_rpc_method_handler( + servicer.ModifyProxy, + request_deserializer=nginx__pb2.ConfigProxy.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + 'ModifyServer': grpc.unary_unary_rpc_method_handler( + servicer.ModifyServer, + request_deserializer=nginx__pb2.ConfigServer.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + 'ModifyLB': grpc.unary_unary_rpc_method_handler( + servicer.ModifyLB, + request_deserializer=nginx__pb2.ConfigLB.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + 'ProcessAlerts': grpc.unary_unary_rpc_method_handler( + servicer.ProcessAlerts, + request_deserializer=nginx__pb2.AlertMessage.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'nginx.Controller', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/samples/services/nginx/docker/grpc/templates/lb.template b/samples/services/nginx/docker/grpc/templates/lb.template new file mode 100644 index 0000000..4866408 --- /dev/null +++ b/samples/services/nginx/docker/grpc/templates/lb.template @@ -0,0 +1,82 @@ +user www-data; +worker_processes auto; +pid /run/nginx.pid; + +events { + worker_connections 768; + # multi_accept on; +} + +http { + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + # server_tokens off; + + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # SSL Settings + ## + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + + ## + # Logging Settings + ## + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + ## + # Gzip Settings + ## + + gzip on; + gzip_disable "msie6"; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + ## + # Virtual Host Configs + ## + + include /etc/nginx/conf.d/*.conf; + #include /etc/nginx/sites-enabled/*; + + upstream {{ slb_group }} { + {%- for item in slb_list %} + server {{ item }}; + {%- endfor %} + } + + server { + listen {{ server_port }}; + server_name {{ server_name }}; + + location {{ lb_path }} { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_pass http://{{ slb_group }}; + } + + } + +} + diff --git a/samples/services/nginx/docker/grpc/templates/proxy.template b/samples/services/nginx/docker/grpc/templates/proxy.template new file mode 100644 index 0000000..72d611e --- /dev/null +++ b/samples/services/nginx/docker/grpc/templates/proxy.template @@ -0,0 +1,85 @@ +user www-data; +worker_processes auto; +pid /run/nginx.pid; + +events { + worker_connections 768; + # multi_accept on; +} + +http { + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + # server_tokens off; + + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # SSL Settings + ## + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + + ## + # Logging Settings + ## + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + ## + # Gzip Settings + ## + + gzip on; + gzip_disable "msie6"; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + ## + # Virtual Host Configs + ## + + include /etc/nginx/conf.d/*.conf; + #include /etc/nginx/sites-enabled/*; + + server { + listen {{ server_port }}; + server_name {{ server_name }}; + + location {{ location_path }} { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_pass {{ proxy_path }}; + + post_action @post_ids; + } + + location @post_ids { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_pass {{ mirror_path }}; + } + + } + +} + diff --git a/samples/services/nginx/docker/grpc/templates/server.template b/samples/services/nginx/docker/grpc/templates/server.template new file mode 100644 index 0000000..b5f8f1f --- /dev/null +++ b/samples/services/nginx/docker/grpc/templates/server.template @@ -0,0 +1,71 @@ +user www-data; +worker_processes auto; +pid /run/nginx.pid; + +events { + worker_connections 768; + # multi_accept on; +} + +http { + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + # server_tokens off; + + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # SSL Settings + ## + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + + ## + # Logging Settings + ## + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + ## + # Gzip Settings + ## + + gzip on; + gzip_disable "msie6"; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + ## + # Virtual Host Configs + ## + + include /etc/nginx/conf.d/*.conf; + #include /etc/nginx/sites-enabled/*; + + server { + listen {{ server_port }}; + server_name {{ server_name }}; + + root {{ site_root }}; + index {{ site_index }}; + } + +} + diff --git a/samples/services/nginx/docker/process/grpc_process.sh b/samples/services/nginx/docker/process/grpc_process.sh new file mode 100755 index 0000000..cc8aa60 --- /dev/null +++ b/samples/services/nginx/docker/process/grpc_process.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +python grpc/nginx_grpc_server.py $1 + diff --git a/samples/services/nginx/docker/process/nginx_process.sh b/samples/services/nginx/docker/process/nginx_process.sh new file mode 100755 index 0000000..00edc66 --- /dev/null +++ b/samples/services/nginx/docker/process/nginx_process.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +service nginx restart + diff --git a/samples/services/nginx/docker/process/start_process.sh b/samples/services/nginx/docker/process/start_process.sh new file mode 100755 index 0000000..b84d2d6 --- /dev/null +++ b/samples/services/nginx/docker/process/start_process.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +# Start the nginx process +./process/nginx_process.sh + +# Start the grpc server +./process/grpc_process.sh $1 -D diff --git a/samples/services/nginx/docker/subservices/lb/Dockerfile b/samples/services/nginx/docker/subservices/lb/Dockerfile new file mode 100644 index 0000000..125da0b --- /dev/null +++ b/samples/services/nginx/docker/subservices/lb/Dockerfile @@ -0,0 +1,28 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM ubuntu:16.04 +LABEL maintainer="Eddie Arrage" maintainer_email="eddie.arrage@huawei.com" +LABEL version="0.1" description="Clover - Nginx HTTP LB" + +RUN \ + apt-get update && apt-get install -y \ +# Some debug tool in container + wget \ + libdnet \ + net-tools \ +# Nginx as proxy + nginx \ + python-pip \ +&& \ +# Install required python packages + python -m pip install grpcio redis jinja2 + +COPY /process /process +COPY /grpc /grpc +CMD ./process/start_process.sh lb + diff --git a/samples/services/nginx/docker/subservices/proxy/Dockerfile b/samples/services/nginx/docker/subservices/proxy/Dockerfile new file mode 100644 index 0000000..0f061a5 --- /dev/null +++ b/samples/services/nginx/docker/subservices/proxy/Dockerfile @@ -0,0 +1,27 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM ubuntu:16.04 +LABEL maintainer="Eddie Arrage" maintainer_email="eddie.arrage@huawei.com" +LABEL version="0.1" description="Clover - Nginx HTTP Proxy" + +RUN \ + apt-get update && apt-get install -y \ +# Some debug tools in container + wget \ + libdnet \ + net-tools \ +# Nginx as proxy + nginx \ + python-pip \ +&& \ +# Install required python packages + python -m pip install grpcio redis jinja2 + +COPY /process /process +COPY /grpc /grpc +CMD ./process/start_process.sh proxy diff --git a/samples/services/nginx/docker/subservices/server/Dockerfile b/samples/services/nginx/docker/subservices/server/Dockerfile new file mode 100644 index 0000000..8bf9449 --- /dev/null +++ b/samples/services/nginx/docker/subservices/server/Dockerfile @@ -0,0 +1,28 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM ubuntu:16.04 +LABEL maintainer="Eddie Arrage" maintainer_email="eddie.arrage@huawei.com" +LABEL version="0.1" description="Clover - Nginx HTTP Server" + +RUN \ + apt-get update && apt-get install -y \ +# Some debug tools in container + wget \ + libdnet \ + net-tools \ +# Nginx as proxy + nginx \ + python-pip \ +&& \ +# Install required python packages + python -m pip install grpcio redis jinja2 + +COPY /process /process +COPY /grpc /grpc +CMD ./process/start_process.sh server + diff --git a/samples/services/nginx/yaml/manifest.template b/samples/services/nginx/yaml/manifest.template new file mode 100644 index 0000000..ebd5392 --- /dev/null +++ b/samples/services/nginx/yaml/manifest.template @@ -0,0 +1,35 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + template: + metadata: + labels: + app: {{ deploy_name }} + spec: + containers: + - name: {{ deploy_name }} + image: {{ image_path }}/{{ image_name }}:{{ image_tag }} + ports: + - containerPort: {{ grpc_port }} + - containerPort: {{ server_port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + ports: + - port: {{ grpc_port }} + name: grpc + - port: {{ server_port }} + name: http + selector: + app: {{ deploy_name }} +--- diff --git a/samples/services/nginx/yaml/render_yaml.py b/samples/services/nginx/yaml/render_yaml.py new file mode 100644 index 0000000..527ba8d --- /dev/null +++ b/samples/services/nginx/yaml/render_yaml.py @@ -0,0 +1,64 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import argparse + +from jinja2 import Template + + +def render_yaml(args): + template_file = 'manifest.template' + server_port = '9180' + grpc_port = '50054' + if args['service_type'] == 'lb': + out_file = 'lb.yaml' + deploy_name = 'http-lb' + elif args['service_type'] == 'proxy': + out_file = 'proxy.yaml' + deploy_name = 'proxy-access-control' + elif args['service_type'] == 'server': + out_file = 'server.yaml' + deploy_name = 'clover-server' + else: + return "Invalid service type: {}".format(args['service_type']) + + try: + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + image_path=args['image_path'], + image_name=args['image_name'], + image_tag=args['image_tag'], + deploy_name=deploy_name, + server_port=server_port, + grpc_port=grpc_port + ) + with open(out_file, "wb") as fh: + fh.write(output) + return "Generated manifest for {}".format(args['service_type']) + except Exception as e: + print(e) + return "Unable to generate manifest for {}".format( + args['service_type']) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--service_type', required=True, + help='The service to generate k8s manifest for') + parser.add_argument( + '--image_name', required=True, + help='The image name to use') + parser.add_argument( + '--image_path', default='localhost:5000', + help='The path to the images to use') + parser.add_argument( + '--image_tag', default='latest', + help='The image tag to use') + args = parser.parse_args() + print(render_yaml(vars(args))) diff --git a/samples/services/snort_ids/docker/Dockerfile b/samples/services/snort_ids/docker/Dockerfile new file mode 100644 index 0000000..50686ed --- /dev/null +++ b/samples/services/snort_ids/docker/Dockerfile @@ -0,0 +1,77 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +FROM fedora:27 +# Use fedora base for snort +LABEL maintainer="Eddie Arrage" maintainer_email="eddie.arrage@huawei.com" +LABEL version="0.1" description="Clover - Snort IDS / Content Inspect Service" + +RUN \ + yum update -y \ +&& \ + yum install -y \ +# Core snort install elements + https://www.snort.org/downloads/snort/daq-2.0.6-1.f21.x86_64.rpm \ + https://www.snort.org/downloads/snort/snort-2.9.11.1-1.f25.x86_64.rpm \ +# Debug packages that aren't required for normal operation + wget libdnet net-tools iputils procps \ + python-pip \ +# For snort to process inbound http client traffic, install nginx server + nginx \ + ldconfig \ +&& \ + mkdir -p /etc/snort/rules \ +&& \ + mkdir /usr/local/lib/snort_dynamicrules \ +&& \ + chmod -R 5775 /etc/snort \ +&& \ + chmod -R 5775 /var/log/snort \ +&& \ + chmod -R 5775 /usr/local/lib/snort_dynamicrules \ +&& \ + chown -R snort:snort /etc/snort \ +&& \ + chown -R snort:snort /var/log/snort \ +&& \ + chown -R snort:snort /usr/local/lib/snort_dynamicrules \ +&& \ + touch /etc/snort/rules/white_list.rules \ +&& \ + touch /etc/snort/rules/black_list.rules \ +&& \ + touch /etc/snort/rules/local.rules \ +# Install snort community rules for now +&& \ + wget https://www.snort.org/rules/community -O ~/community.tar.gz \ +&& \ + tar -xvf ~/community.tar.gz -C ~/ \ +&& \ + cp ~/community-rules/* /etc/snort/rules \ +&& \ +# Modify snort.conf file + sed -i 's/include \$RULE\_PATH/#include \$RULE\_PATH/' /etc/snort/snort.conf \ +&& \ + sed -i 's/var WHITE\_LIST\_PATH \.\.\/rules/var WHITE\_LIST\_PATH \/etc\/snort\/rules/' /etc/snort/snort.conf \ +&& \ + sed -i 's/var BLACK\_LIST\_PATH \.\.\/rules/var BLACK\_LIST\_PATH \/etc\/snort\/rules/' /etc/snort/snort.conf \ +&& \ + sed -i 's/\# output unified2\: filename merged\.log, limit 128, nostamp, mpls\_event\_types, vlan\_event\_types/output unified2\: filename snort\.log, limit 128/' /etc/snort/snort.conf \ +&& \ + sed -i 's/\#include \$RULE\_PATH\/local\.rules/include \$RULE\_PATH\/local\.rules\ninclude \$RULE\_PATH\/community\.rules/' /etc/snort/snort.conf \ +&& \ + cd /usr/lib64 \ +&& \ +# Account for libdnet issue + ln -s libdnet.so.1.0.1 libdnet.1 \ +&& \ +# Install required python libraries + python -m pip install grpcio redis idstools + +COPY /process /process +COPY /grpc /grpc +CMD ./process/start_process.sh diff --git a/samples/services/snort_ids/docker/build.sh b/samples/services/snort_ids/docker/build.sh new file mode 100755 index 0000000..e587d8b --- /dev/null +++ b/samples/services/snort_ids/docker/build.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +IMAGE_PATH=${IMAGE_PATH:-"localhost:5000"} +IMAGE_NAME=${IMAGE_NAME:-"clover-ns-snort-ids"} + +docker build -t $IMAGE_NAME . +docker tag $IMAGE_NAME $IMAGE_PATH/$IMAGE_NAME +docker push $IMAGE_PATH/$IMAGE_NAME diff --git a/samples/services/snort_ids/docker/grpc/build_proto.sh b/samples/services/snort_ids/docker/grpc/build_proto.sh new file mode 100755 index 0000000..69bfeb7 --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/build_proto.sh @@ -0,0 +1,9 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + + +python -m grpc_tools.protoc -I./ --python_out=. --grpc_python_out=. snort.proto diff --git a/samples/services/snort_ids/docker/grpc/nginx_pb2.py b/samples/services/snort_ids/docker/grpc/nginx_pb2.py new file mode 100644 index 0000000..3600b6d --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/nginx_pb2.py @@ -0,0 +1,360 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: nginx.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='nginx.proto', + package='nginx', + syntax='proto3', + serialized_pb=_b('\n\x0bnginx.proto\x12\x05nginx\"3\n\x0c\x41lertMessage\x12\x10\n\x08\x65vent_id\x18\x01 \x01(\t\x12\x11\n\tredis_key\x18\x02 \x01(\t\"w\n\x0b\x43onfigProxy\x12\x13\n\x0bserver_port\x18\x01 \x01(\t\x12\x13\n\x0bserver_name\x18\x02 \x01(\t\x12\x15\n\rlocation_path\x18\x03 \x01(\t\x12\x12\n\nproxy_path\x18\x04 \x01(\t\x12\x13\n\x0bmirror_path\x18\x05 \x01(\t\"_\n\x0c\x43onfigServer\x12\x13\n\x0bserver_port\x18\x01 \x01(\t\x12\x13\n\x0bserver_name\x18\x02 \x01(\t\x12\x11\n\tsite_root\x18\x03 \x01(\t\x12\x12\n\nsite_index\x18\x04 \x01(\t\"j\n\x08\x43onfigLB\x12\x13\n\x0bserver_port\x18\x01 \x01(\t\x12\x13\n\x0bserver_name\x18\x02 \x01(\t\x12\x10\n\x08slb_list\x18\x03 \x01(\t\x12\x11\n\tslb_group\x18\x04 \x01(\t\x12\x0f\n\x07lb_path\x18\x05 \x01(\t\"\x1d\n\nNginxReply\x12\x0f\n\x07message\x18\x01 \x01(\t2\xeb\x01\n\nController\x12\x36\n\x0bModifyProxy\x12\x12.nginx.ConfigProxy\x1a\x11.nginx.NginxReply\"\x00\x12\x38\n\x0cModifyServer\x12\x13.nginx.ConfigServer\x1a\x11.nginx.NginxReply\"\x00\x12\x30\n\x08ModifyLB\x12\x0f.nginx.ConfigLB\x1a\x11.nginx.NginxReply\"\x00\x12\x39\n\rProcessAlerts\x12\x13.nginx.AlertMessage\x1a\x11.nginx.NginxReply\"\x00\x62\x06proto3') +) + + + + +_ALERTMESSAGE = _descriptor.Descriptor( + name='AlertMessage', + full_name='nginx.AlertMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='event_id', full_name='nginx.AlertMessage.event_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='redis_key', full_name='nginx.AlertMessage.redis_key', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=73, +) + + +_CONFIGPROXY = _descriptor.Descriptor( + name='ConfigProxy', + full_name='nginx.ConfigProxy', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='server_port', full_name='nginx.ConfigProxy.server_port', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='server_name', full_name='nginx.ConfigProxy.server_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='location_path', full_name='nginx.ConfigProxy.location_path', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='proxy_path', full_name='nginx.ConfigProxy.proxy_path', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='mirror_path', full_name='nginx.ConfigProxy.mirror_path', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=75, + serialized_end=194, +) + + +_CONFIGSERVER = _descriptor.Descriptor( + name='ConfigServer', + full_name='nginx.ConfigServer', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='server_port', full_name='nginx.ConfigServer.server_port', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='server_name', full_name='nginx.ConfigServer.server_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='site_root', full_name='nginx.ConfigServer.site_root', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='site_index', full_name='nginx.ConfigServer.site_index', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=196, + serialized_end=291, +) + + +_CONFIGLB = _descriptor.Descriptor( + name='ConfigLB', + full_name='nginx.ConfigLB', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='server_port', full_name='nginx.ConfigLB.server_port', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='server_name', full_name='nginx.ConfigLB.server_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='slb_list', full_name='nginx.ConfigLB.slb_list', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='slb_group', full_name='nginx.ConfigLB.slb_group', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='lb_path', full_name='nginx.ConfigLB.lb_path', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=293, + serialized_end=399, +) + + +_NGINXREPLY = _descriptor.Descriptor( + name='NginxReply', + full_name='nginx.NginxReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='nginx.NginxReply.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=401, + serialized_end=430, +) + +DESCRIPTOR.message_types_by_name['AlertMessage'] = _ALERTMESSAGE +DESCRIPTOR.message_types_by_name['ConfigProxy'] = _CONFIGPROXY +DESCRIPTOR.message_types_by_name['ConfigServer'] = _CONFIGSERVER +DESCRIPTOR.message_types_by_name['ConfigLB'] = _CONFIGLB +DESCRIPTOR.message_types_by_name['NginxReply'] = _NGINXREPLY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +AlertMessage = _reflection.GeneratedProtocolMessageType('AlertMessage', (_message.Message,), dict( + DESCRIPTOR = _ALERTMESSAGE, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.AlertMessage) + )) +_sym_db.RegisterMessage(AlertMessage) + +ConfigProxy = _reflection.GeneratedProtocolMessageType('ConfigProxy', (_message.Message,), dict( + DESCRIPTOR = _CONFIGPROXY, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.ConfigProxy) + )) +_sym_db.RegisterMessage(ConfigProxy) + +ConfigServer = _reflection.GeneratedProtocolMessageType('ConfigServer', (_message.Message,), dict( + DESCRIPTOR = _CONFIGSERVER, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.ConfigServer) + )) +_sym_db.RegisterMessage(ConfigServer) + +ConfigLB = _reflection.GeneratedProtocolMessageType('ConfigLB', (_message.Message,), dict( + DESCRIPTOR = _CONFIGLB, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.ConfigLB) + )) +_sym_db.RegisterMessage(ConfigLB) + +NginxReply = _reflection.GeneratedProtocolMessageType('NginxReply', (_message.Message,), dict( + DESCRIPTOR = _NGINXREPLY, + __module__ = 'nginx_pb2' + # @@protoc_insertion_point(class_scope:nginx.NginxReply) + )) +_sym_db.RegisterMessage(NginxReply) + + + +_CONTROLLER = _descriptor.ServiceDescriptor( + name='Controller', + full_name='nginx.Controller', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=433, + serialized_end=668, + methods=[ + _descriptor.MethodDescriptor( + name='ModifyProxy', + full_name='nginx.Controller.ModifyProxy', + index=0, + containing_service=None, + input_type=_CONFIGPROXY, + output_type=_NGINXREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='ModifyServer', + full_name='nginx.Controller.ModifyServer', + index=1, + containing_service=None, + input_type=_CONFIGSERVER, + output_type=_NGINXREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='ModifyLB', + full_name='nginx.Controller.ModifyLB', + index=2, + containing_service=None, + input_type=_CONFIGLB, + output_type=_NGINXREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='ProcessAlerts', + full_name='nginx.Controller.ProcessAlerts', + index=3, + containing_service=None, + input_type=_ALERTMESSAGE, + output_type=_NGINXREPLY, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTROLLER) + +DESCRIPTOR.services_by_name['Controller'] = _CONTROLLER + +# @@protoc_insertion_point(module_scope) diff --git a/samples/services/snort_ids/docker/grpc/nginx_pb2_grpc.py b/samples/services/snort_ids/docker/grpc/nginx_pb2_grpc.py new file mode 100644 index 0000000..2b36fc0 --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/nginx_pb2_grpc.py @@ -0,0 +1,97 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import nginx_pb2 as nginx__pb2 + + +class ControllerStub(object): + """The controller service definition. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.ModifyProxy = channel.unary_unary( + '/nginx.Controller/ModifyProxy', + request_serializer=nginx__pb2.ConfigProxy.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + self.ModifyServer = channel.unary_unary( + '/nginx.Controller/ModifyServer', + request_serializer=nginx__pb2.ConfigServer.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + self.ModifyLB = channel.unary_unary( + '/nginx.Controller/ModifyLB', + request_serializer=nginx__pb2.ConfigLB.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + self.ProcessAlerts = channel.unary_unary( + '/nginx.Controller/ProcessAlerts', + request_serializer=nginx__pb2.AlertMessage.SerializeToString, + response_deserializer=nginx__pb2.NginxReply.FromString, + ) + + +class ControllerServicer(object): + """The controller service definition. + """ + + def ModifyProxy(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ModifyServer(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ModifyLB(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ProcessAlerts(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ControllerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'ModifyProxy': grpc.unary_unary_rpc_method_handler( + servicer.ModifyProxy, + request_deserializer=nginx__pb2.ConfigProxy.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + 'ModifyServer': grpc.unary_unary_rpc_method_handler( + servicer.ModifyServer, + request_deserializer=nginx__pb2.ConfigServer.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + 'ModifyLB': grpc.unary_unary_rpc_method_handler( + servicer.ModifyLB, + request_deserializer=nginx__pb2.ConfigLB.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + 'ProcessAlerts': grpc.unary_unary_rpc_method_handler( + servicer.ProcessAlerts, + request_deserializer=nginx__pb2.AlertMessage.FromString, + response_serializer=nginx__pb2.NginxReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'nginx.Controller', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/samples/services/snort_ids/docker/grpc/snort.proto b/samples/services/snort_ids/docker/grpc/snort.proto new file mode 100644 index 0000000..8d69baa --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/snort.proto @@ -0,0 +1,36 @@ +// Copyright (c) Authors of Clover +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Apache License, Version 2.0 +// which accompanies this distribution, and is available at +// http://www.apache.org/licenses/LICENSE-2.0 + + +syntax = "proto3"; +package snort; + +service Controller { + + rpc AddRules (AddRule) returns (SnortReply) {} + rpc StartSnort (ControlSnort) returns (SnortReply) {} + rpc StopSnort (ControlSnort) returns (SnortReply) {} +} + +message ControlSnort { + string pid = 1; +} + +message AddRule { + string protocol = 1; + string dest_port = 2; + string dest_ip = 3; + string src_port = 4; + string src_ip = 5; + string msg = 6; + string sid = 7; + string rev = 8; +} + +message SnortReply { + string message = 1; +} diff --git a/samples/services/snort_ids/docker/grpc/snort_alerts.py b/samples/services/snort_ids/docker/grpc/snort_alerts.py new file mode 100644 index 0000000..eda2bd6 --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/snort_alerts.py @@ -0,0 +1,57 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import redis +import logging +import grpc +import nginx_pb2 +import nginx_pb2_grpc +from idstools import unified2 + + +HOST_IP = 'redis' +PROXY_GRPC = 'http-proxy:50054' + +logging.basicConfig(filename='alert.log', level=logging.DEBUG) + +connect = False +while not connect: + try: + r = redis.StrictRedis(host=HOST_IP, port=6379, db=0) + r.delete('snort_events') + connect = True + except Exception as e: + logging.debug(e) + connect = False + +reader = unified2.SpoolRecordReader("/var/log/snort", + "", follow=True) + + +def sendGrpcAlert(event_id, redis_key): + try: + channel = grpc.insecure_channel(PROXY_GRPC) + stub = nginx_pb2_grpc.ControllerStub(channel) + stub.ProcessAlerts(nginx_pb2.AlertMessage( + event_id=event_id, redis_key=redis_key)) + except Exception as e: + logging.debug(e) + + +for record in reader: + try: + if isinstance(record, unified2.Event): + snort_event = "snort_event:" + str(record['event-id']) + r.sadd('snort_events', str(record['event-id'])) + r.hmset(snort_event, record) + sendGrpcAlert(str(record['event-id']), 'snort_events') + # elif isinstance(record, unified2.Packet): + # print("Packet:") + # elif isinstance(record, unified2.ExtraData): + # print("Extra-Data:") + except Exception as e: + logging.debug(e) diff --git a/samples/services/snort_ids/docker/grpc/snort_client.py b/samples/services/snort_ids/docker/grpc/snort_client.py new file mode 100644 index 0000000..d59b4ee --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/snort_client.py @@ -0,0 +1,106 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +from __future__ import print_function +from kubernetes import client, config + +import grpc +import argparse + +import snort_pb2 +import snort_pb2_grpc + + +def run(args, grpc_port='50052'): + # get pod ip for grpc + pod_ip = get_podip(args['service_name']) + if pod_ip == '': + return "Can not find service: {}".format(args['service_name']) + snort_grpc = pod_ip + ':' + grpc_port + # snort_grpc = 'localhost:50052' + channel = grpc.insecure_channel(snort_grpc) + stub = snort_pb2_grpc.ControllerStub(channel) + + # execute command in service + if args['cmd'] == 'addtcp': + return add_tcprule(stub) + elif args['cmd'] == 'addicmp': + return add_icmprule(stub) + elif args['cmd'] == 'start': + return start_snort(stub) + elif args['cmd'] == 'stop': + return stop_snort(stub) + else: + return "Invalid command: {}".format(args['cmd']) + + +def get_podip(pod_name): + ip = '' + if pod_name != '': + config.load_kube_config() + v1 = client.CoreV1Api() + ret = v1.list_pod_for_all_namespaces(watch=False) + for i in ret.items: + if i.metadata.name.lower().find(pod_name.lower()) != -1: + print("Pod IP: {}".format(i.status.pod_ip)) + ip = i.status.pod_ip + return str(ip) + return str(ip) + + +def add_tcprule(stub): + try: + response = stub.AddRules(snort_pb2.AddRule( + protocol='tcp', dest_port='any', dest_ip='$HOME_NET', + src_port='any', src_ip='any', msg='tcp test', sid='10000002', + rev='001')) + print(stop_snort(stub)) + print(start_snort(stub)) + except Exception as e: + return e + return response.message + + +def add_icmprule(stub): + try: + response = stub.AddRules(snort_pb2.AddRule( + protocol='icmp', dest_port='any', dest_ip='$HOME_NET', + src_port='any', src_ip='any', msg='icmp test', sid='10000001', + rev='001')) + print(stop_snort(stub)) + print(start_snort(stub)) + except Exception as e: + return e + return response.message + + +def start_snort(stub): + try: + response = stub.StartSnort(snort_pb2.ControlSnort(pid='0')) + except Exception as e: + return e + return response.message + + +def stop_snort(stub): + try: + response = stub.StopSnort(snort_pb2.ControlSnort(pid='0')) + except Exception as e: + return e + return response.message + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--service_name', required=True, + help='Snort service/pod name to reconfigure') + parser.add_argument( + '--cmd', required=True, + help='Command to execute in snort service') + args = parser.parse_args() + print(run(vars(args))) diff --git a/samples/services/snort_ids/docker/grpc/snort_pb2.py b/samples/services/snort_ids/docker/grpc/snort_pb2.py new file mode 100644 index 0000000..93641ef --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/snort_pb2.py @@ -0,0 +1,238 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: snort.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='snort.proto', + package='snort', + syntax='proto3', + serialized_pb=_b('\n\x0bsnort.proto\x12\x05snort\"\x1b\n\x0c\x43ontrolSnort\x12\x0b\n\x03pid\x18\x01 \x01(\t\"\x88\x01\n\x07\x41\x64\x64Rule\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x11\n\tdest_port\x18\x02 \x01(\t\x12\x0f\n\x07\x64\x65st_ip\x18\x03 \x01(\t\x12\x10\n\x08src_port\x18\x04 \x01(\t\x12\x0e\n\x06src_ip\x18\x05 \x01(\t\x12\x0b\n\x03msg\x18\x06 \x01(\t\x12\x0b\n\x03sid\x18\x07 \x01(\t\x12\x0b\n\x03rev\x18\x08 \x01(\t\"\x1d\n\nSnortReply\x12\x0f\n\x07message\x18\x01 \x01(\t2\xac\x01\n\nController\x12/\n\x08\x41\x64\x64Rules\x12\x0e.snort.AddRule\x1a\x11.snort.SnortReply\"\x00\x12\x36\n\nStartSnort\x12\x13.snort.ControlSnort\x1a\x11.snort.SnortReply\"\x00\x12\x35\n\tStopSnort\x12\x13.snort.ControlSnort\x1a\x11.snort.SnortReply\"\x00\x62\x06proto3') +) + + + + +_CONTROLSNORT = _descriptor.Descriptor( + name='ControlSnort', + full_name='snort.ControlSnort', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='pid', full_name='snort.ControlSnort.pid', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=22, + serialized_end=49, +) + + +_ADDRULE = _descriptor.Descriptor( + name='AddRule', + full_name='snort.AddRule', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='protocol', full_name='snort.AddRule.protocol', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dest_port', full_name='snort.AddRule.dest_port', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='dest_ip', full_name='snort.AddRule.dest_ip', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='src_port', full_name='snort.AddRule.src_port', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='src_ip', full_name='snort.AddRule.src_ip', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='msg', full_name='snort.AddRule.msg', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='sid', full_name='snort.AddRule.sid', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='rev', full_name='snort.AddRule.rev', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=52, + serialized_end=188, +) + + +_SNORTREPLY = _descriptor.Descriptor( + name='SnortReply', + full_name='snort.SnortReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='message', full_name='snort.SnortReply.message', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=190, + serialized_end=219, +) + +DESCRIPTOR.message_types_by_name['ControlSnort'] = _CONTROLSNORT +DESCRIPTOR.message_types_by_name['AddRule'] = _ADDRULE +DESCRIPTOR.message_types_by_name['SnortReply'] = _SNORTREPLY +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ControlSnort = _reflection.GeneratedProtocolMessageType('ControlSnort', (_message.Message,), dict( + DESCRIPTOR = _CONTROLSNORT, + __module__ = 'snort_pb2' + # @@protoc_insertion_point(class_scope:snort.ControlSnort) + )) +_sym_db.RegisterMessage(ControlSnort) + +AddRule = _reflection.GeneratedProtocolMessageType('AddRule', (_message.Message,), dict( + DESCRIPTOR = _ADDRULE, + __module__ = 'snort_pb2' + # @@protoc_insertion_point(class_scope:snort.AddRule) + )) +_sym_db.RegisterMessage(AddRule) + +SnortReply = _reflection.GeneratedProtocolMessageType('SnortReply', (_message.Message,), dict( + DESCRIPTOR = _SNORTREPLY, + __module__ = 'snort_pb2' + # @@protoc_insertion_point(class_scope:snort.SnortReply) + )) +_sym_db.RegisterMessage(SnortReply) + + + +_CONTROLLER = _descriptor.ServiceDescriptor( + name='Controller', + full_name='snort.Controller', + file=DESCRIPTOR, + index=0, + options=None, + serialized_start=222, + serialized_end=394, + methods=[ + _descriptor.MethodDescriptor( + name='AddRules', + full_name='snort.Controller.AddRules', + index=0, + containing_service=None, + input_type=_ADDRULE, + output_type=_SNORTREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='StartSnort', + full_name='snort.Controller.StartSnort', + index=1, + containing_service=None, + input_type=_CONTROLSNORT, + output_type=_SNORTREPLY, + options=None, + ), + _descriptor.MethodDescriptor( + name='StopSnort', + full_name='snort.Controller.StopSnort', + index=2, + containing_service=None, + input_type=_CONTROLSNORT, + output_type=_SNORTREPLY, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTROLLER) + +DESCRIPTOR.services_by_name['Controller'] = _CONTROLLER + +# @@protoc_insertion_point(module_scope) diff --git a/samples/services/snort_ids/docker/grpc/snort_pb2_grpc.py b/samples/services/snort_ids/docker/grpc/snort_pb2_grpc.py new file mode 100644 index 0000000..4a04299 --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/snort_pb2_grpc.py @@ -0,0 +1,80 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +import grpc + +import snort_pb2 as snort__pb2 + + +class ControllerStub(object): + # missing associated documentation comment in .proto file + pass + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.AddRules = channel.unary_unary( + '/snort.Controller/AddRules', + request_serializer=snort__pb2.AddRule.SerializeToString, + response_deserializer=snort__pb2.SnortReply.FromString, + ) + self.StartSnort = channel.unary_unary( + '/snort.Controller/StartSnort', + request_serializer=snort__pb2.ControlSnort.SerializeToString, + response_deserializer=snort__pb2.SnortReply.FromString, + ) + self.StopSnort = channel.unary_unary( + '/snort.Controller/StopSnort', + request_serializer=snort__pb2.ControlSnort.SerializeToString, + response_deserializer=snort__pb2.SnortReply.FromString, + ) + + +class ControllerServicer(object): + # missing associated documentation comment in .proto file + pass + + def AddRules(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def StartSnort(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def StopSnort(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ControllerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'AddRules': grpc.unary_unary_rpc_method_handler( + servicer.AddRules, + request_deserializer=snort__pb2.AddRule.FromString, + response_serializer=snort__pb2.SnortReply.SerializeToString, + ), + 'StartSnort': grpc.unary_unary_rpc_method_handler( + servicer.StartSnort, + request_deserializer=snort__pb2.ControlSnort.FromString, + response_serializer=snort__pb2.SnortReply.SerializeToString, + ), + 'StopSnort': grpc.unary_unary_rpc_method_handler( + servicer.StopSnort, + request_deserializer=snort__pb2.ControlSnort.FromString, + response_serializer=snort__pb2.SnortReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'snort.Controller', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/samples/services/snort_ids/docker/grpc/snort_server.py b/samples/services/snort_ids/docker/grpc/snort_server.py new file mode 100644 index 0000000..3c2fdb1 --- /dev/null +++ b/samples/services/snort_ids/docker/grpc/snort_server.py @@ -0,0 +1,98 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +from concurrent import futures +import time + +import grpc +import subprocess +import os +import logging + +import snort_pb2 +import snort_pb2_grpc + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 +GRPC_PORT = '[::]:50052' + + +class Controller(snort_pb2_grpc.ControllerServicer): + + def __init__(self): + logging.basicConfig(filename='snort.log', level=logging.DEBUG) + self.snort = 0 + self.StartSnort("", "") + + # Add custom rules + def AddRules(self, r, context): + try: + # file_local = 'testfile' + file_local = '/etc/snort/rules/local.rules' + f = open(file_local, 'a') + rule = 'alert {} {} {} -> {} {} '.format( + r.protocol, r.src_ip, r.src_port, r.dest_ip, r.dest_port) \ + + '(msg:"{}"; sid:{}; rev:{};)\n'.format(r.msg, r.sid, r.rev) + f.write(rule) + f.close + msg = "Added to local rules" + except Exception as e: + msg = "Failed to add to local rules" + logging.debug(e) + return snort_pb2.SnortReply(message=msg) + + def StartSnort(self, request, context): + try: + if self.snort == 0: + p = subprocess.Popen( + ["snort -i eth0 -u snort -g snort -c /etc/snort/snort.conf \ + -k none"], shell=True) + self.snort = p + msg = "Started Snort on pid: {}".format(p.pid) + else: + msg = "Snort already running" + except Exception as e: + self.snort = 0 + logging.debug(e) + msg = "Failed to start Snort" + return snort_pb2.SnortReply(message=msg) + + def StopSnort(self, request, context): + try: + subprocess.Popen.kill(self.snort) + msg1 = "Stopped Snort on pid: {}, ".format(self.snort.pid) + self.snort = 0 + except Exception as e: + msg1 = "Failed to stop Snort, " + logging.debug(e) + try: + # clear logs + logPath = '/var/log/snort' + logList = os.listdir(logPath) + for logName in logList: + os.remove(logPath+"/"+logName) + msg2 = "Cleared Snort logs" + except Exception as e: + msg2 = "Failed to clear logs" + logging.debug(e) + msg = msg1 + msg2 + return snort_pb2.SnortReply(message=msg) + + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + snort_pb2_grpc.add_ControllerServicer_to_server(Controller(), server) + server.add_insecure_port(GRPC_PORT) + server.start() + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except KeyboardInterrupt: + server.stop(0) + + +if __name__ == '__main__': + serve() diff --git a/samples/services/snort_ids/docker/process/alert_process.sh b/samples/services/snort_ids/docker/process/alert_process.sh new file mode 100755 index 0000000..6934c8a --- /dev/null +++ b/samples/services/snort_ids/docker/process/alert_process.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +# start nginx server to handle incoming http requests +/usr/sbin/nginx & + +# Process snort alerts +python grpc/snort_alerts.py + diff --git a/samples/services/snort_ids/docker/process/grpc_process.sh b/samples/services/snort_ids/docker/process/grpc_process.sh new file mode 100755 index 0000000..d58c468 --- /dev/null +++ b/samples/services/snort_ids/docker/process/grpc_process.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +python grpc/snort_server.py + diff --git a/samples/services/snort_ids/docker/process/start_process.sh b/samples/services/snort_ids/docker/process/start_process.sh new file mode 100755 index 0000000..8d94b80 --- /dev/null +++ b/samples/services/snort_ids/docker/process/start_process.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# + +# Alert script processes snort alerts +./process/alert_process.sh & + +# Main script to start grpc server that controls snort +./process/grpc_process.sh -D + diff --git a/samples/services/snort_ids/yaml/manifest.template b/samples/services/snort_ids/yaml/manifest.template new file mode 100644 index 0000000..178765b --- /dev/null +++ b/samples/services/snort_ids/yaml/manifest.template @@ -0,0 +1,38 @@ +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + template: + metadata: + labels: + app: {{ deploy_name }} + spec: + containers: + - name: {{ deploy_name }} + image: {{ image_path }}/{{ image_name }}:{{ image_tag }} + ports: + - containerPort: {{ grpc_port }} + - containerPort: {{ redis_port }} + - containerPort: {{ http_port }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ deploy_name }} + labels: + app: {{ deploy_name }} +spec: + ports: + - port: {{ grpc_port }} + name: grpc + - port: {{ redis_port }} + name: redis + - port: {{ http_port }} + name: http + selector: + app: {{ deploy_name }} +--- diff --git a/samples/services/snort_ids/yaml/render_yaml.py b/samples/services/snort_ids/yaml/render_yaml.py new file mode 100644 index 0000000..e23f540 --- /dev/null +++ b/samples/services/snort_ids/yaml/render_yaml.py @@ -0,0 +1,62 @@ +# Copyright (c) Authors of Clover +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 + +import argparse + +from jinja2 import Template + + +def render_yaml(args): + template_file = 'manifest.template' + out_file = 'snort.yaml' + + try: + with open(template_file) as f: + tmpl = Template(f.read()) + output = tmpl.render( + image_path=args['image_path'], + image_name=args['image_name'], + image_tag=args['image_tag'], + deploy_name=args['deploy_name'], + grpc_port=args['grpc_port'], + redis_port=args['redis_port'], + http_port=args['http_port'] + ) + with open(out_file, "wb") as fh: + fh.write(output) + return "Generated manifest for {}".format(args['deploy_name']) + except Exception as e: + print(e) + return "Unable to generate manifest for {}".format( + args['deploy_name']) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_name', default='clover-ns-snort-ids', + help='The image name to use') + parser.add_argument( + '--image_path', default='localhost:5000', + help='The path to the images to use') + parser.add_argument( + '--image_tag', default='latest', + help='The image tag to use') + parser.add_argument( + '--deploy_name', default='snort-ids', + help='The k8s deploy name to use') + parser.add_argument( + '--redis_port', default='6379', + help='The redis port to connect to for alerts') + parser.add_argument( + '--http_port', default='80', + help='Analyze http traffic on this port') + parser.add_argument( + '--grpc_port', default='50052', + help='The image tag to use') + args = parser.parse_args() + print(render_yaml(vars(args))) |