diff --git a/salt/common/tools/sbin/so-image-pull b/salt/common/tools/sbin/so-image-pull new file mode 100644 index 000000000..f86d08ca2 --- /dev/null +++ b/salt/common/tools/sbin/so-image-pull @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Copyright 2014,2015,2016,2017,2018,2019,2020,2021 Security Onion Solutions, LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. /usr/sbin/so-common +. /usr/sbin/so-image-common + +usage() { + read -r -d '' message <<- EOM + usage: so-image-pull [-h] IMAGE [IMAGE ...] + + positional arguments: + IMAGE One or more 'so-' prefixed images to download and verify. + + optional arguments: + -h, --help Show this help message and exit. + EOM + echo "$message" + exit 1 +} + +if [[ $# -eq 0 || $# -gt 1 ]] || [[ $1 == '-h' || $1 == '--help' ]]; then + usage +fi + +TRUSTED_CONTAINERS=("$@") +set_version + +for image in "${TRUSTED_CONTAINERS[@]}"; do + if ! docker images | grep "$image" | grep ":5000" | grep -q "$VERSION"; then + update_docker_containers "$image" "" "" "" + else + echo "$image:$VERSION image exists." 1>&2 + fi +done diff --git a/salt/common/tools/sbin/so-learn b/salt/common/tools/sbin/so-learn index f4a8ef90e..d87649cd2 100644 --- a/salt/common/tools/sbin/so-learn +++ b/salt/common/tools/sbin/so-learn @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from itertools import chain from typing import List import signal @@ -26,6 +27,7 @@ import argparse import textwrap import yaml import multiprocessing +import docker minion_pillar_dir = '/opt/so/saltstack/local/pillar/minions' so_status_conf = '/opt/so/conf/so-status/so-status.conf' @@ -112,7 +114,6 @@ def mod_so_status(action: str, item: str): if action == 'remove': pass if action == 'add': containers.append(f'so-{item}\n') - [containers.remove(c_name) for c_name in containers if c_name == '\n'] # remove extra newlines conf.seek(0) @@ -132,19 +133,57 @@ def create_pillar_if_not_exist(pillar:str, content: dict): return content -def apply(module_list: List): +def salt_call(module: str): return_code = 0 - for module in module_list: - salt_cmd = ['salt-call', 'state.apply', '-l', 'quiet', f'learn.{module}', 'queue=True'] - print(f' Applying salt state for {module} module...') - salt_proc = subprocess.run(salt_cmd, stdout=subprocess.DEVNULL) - if salt_proc.returncode != 0: - print(f' [ERROR] Failed to apply salt state for {module} module.') - return_code = salt_proc.returncode + salt_cmd = ['salt-call', 'state.apply', '-l', 'quiet', f'learn.{module}', 'queue=True'] + + print(f' Applying salt state for {module} module...') + return_code = subprocess.run(salt_cmd, stdout=subprocess.DEVNULL).returncode + if return_code != 0: + print(f' [ERROR] Failed to apply salt state for {module} module.') + return_code = salt_proc.returncode + return return_code -def check_apply(args: dict): +def pull_image(module: str): + container_basename = f'so-{module}' + + client = docker.from_env() + image_list = client.images.list(filters={ 'dangling': False }) + tag_list = list(chain.from_iterable(list(map(lambda x: x.attrs.get('RepoTags'), image_list)))) + basename_match = list(filter(lambda x: f'{container_basename}' in x, tag_list)) + local_registry_match = list(filter(lambda x: ':5000' in x, basename_match)) + + if len(local_registry_match) > 0: + print(f' Pulling missing image for {module}:') + pull_command = ['so-image-pull', container_basename] + + return_code = subprocess.run(pull_command, stdout=subprocess.DEVNULL).returncode + if return_code != 0: + print(f' [ERROR] Failed to pull image so-{module}, skipping state.') + + return return_code + + +def apply(module_list: List, enable: bool): + return_code = 0 + for module in module_list: + if enable: + temp_return = pull_image(module) + if temp_return == 0: + temp_return = salt_call(module) + else: + temp_return = salt_call(module) + + # Only update return_code if a command returned a non-zero return + if temp_return != 0: + return_code = temp_return + + return return_code + + +def check_apply(args: dict, enable: bool): if args.apply: print('Configuration updated. Applying changes:') return apply(args.modules) @@ -157,7 +196,7 @@ def check_apply(args: dict): return 0 else: print('Applying changes:') - return apply(args.modules) + return apply(args.modules, enable) def enable_disable_modules(args, enable: bool): @@ -170,6 +209,7 @@ def enable_disable_modules(args, enable: bool): for module, details in pillar_modules.items(): details['enabled'] = enable mod_so_status(action_str, module) + if enable: pull_image(module) args.pillar_dict.update() write_pillar(args.pillar, args.pillar_dict) else: @@ -187,7 +227,7 @@ def enable_disable_modules(args, enable: bool): args.pillar_dict.update() write_pillar(args.pillar, args.pillar_dict) - cmd_ret = check_apply(args) + cmd_ret = check_apply(args, enable) return cmd_ret