Merge branch 'auto_grading071220' into auto_grading180621

# Conflicts:
#	lib/batch/batch_secgen.rb
#	lib/readers/system_reader.rb
#	lib/schemas/scenario_schema.xsd
#	lib/templates/Puppetfile.erb
#	modules/generators/structured_content/metactf_challenge/secgen_metadata.xml
#	modules/utilities/unix/ctf/metactf/manifests/configure.pp
#	modules/utilities/unix/ctf/metactf/manifests/install.pp
This commit is contained in:
thomashaw
2021-06-18 10:13:07 +01:00
597 changed files with 14896 additions and 1616 deletions

View File

@@ -19,9 +19,9 @@ GIT
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (2.3.6)
CFPropertyList (3.0.1)
PriorityQueue (0.1.2)
activesupport (5.2.3)
activesupport (5.2.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@@ -34,21 +34,21 @@ GEM
concurrent-ruby (1.1.5)
credy (0.2.1)
thor (~> 0.19.1)
deep_merge (1.2.1)
digest-simple (1.1.0)
digest-siphash (1.0.1)
digest-simple
digest-whirlpool (1.0.3)
duplicate (1.1.1)
facter (2.5.1)
CFPropertyList (~> 2.2)
faker (1.9.3)
i18n (>= 0.7)
faraday (0.13.1)
facter (2.5.6)
faker (2.7.0)
i18n (>= 1.6, < 1.8)
faraday (0.14.0)
multipart-post (>= 1.2, < 3)
faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0)
fast_gettext (1.1.2)
ffi (1.11.1)
ffi (1.11.3)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
@@ -56,21 +56,21 @@ GEM
gettext (3.2.9)
locale (>= 2.0.5)
text (>= 1.3.0)
gettext-setup (0.30)
gettext-setup (0.31)
fast_gettext (~> 1.1.0)
gettext (>= 3.0.2)
locale
gpgmeh (0.1.6)
activesupport (>= 2.3)
nio4r (~> 2.2)
hiera (3.5.0)
hocon (1.2.5)
hiera (3.6.0)
hocon (1.3.0)
httpclient (2.8.3)
huffman (0.0.1)
PriorityQueue
activesupport
ruby-graphviz
i18n (1.6.0)
i18n (1.7.0)
concurrent-ruby (~> 1.0)
json (2.2.0)
librarian-puppet (3.0.0)
@@ -80,17 +80,18 @@ GEM
librarianp (0.6.4)
thor (~> 0.15)
locale (2.1.2)
mini_exiftool (2.9.0)
mini_exiftool (2.9.1)
mini_exiftool_vendored (9.2.7.v1)
mini_exiftool (>= 1.6.0)
mini_portile2 (2.4.0)
minitar (0.8)
minitest (5.11.3)
multi_json (1.13.1)
minitar (0.9)
minitest (5.13.0)
multi_json (1.14.1)
multipart-post (2.1.1)
mustermann (1.0.3)
net-ntp (2.1.3)
nio4r (2.3.1)
nokogiri (1.10.3)
nio4r (2.5.2)
nokogiri (1.10.5)
mini_portile2 (~> 2.4.0)
nori (2.6.0)
ovirt-engine-sdk (4.3.0)
@@ -100,49 +101,61 @@ GEM
pcaprub (0.13.0)
pg (1.1.4)
process_helper (0.1.2)
puppet (6.4.2)
CFPropertyList (~> 2.2)
puppet (6.11.1)
concurrent-ruby (~> 1.0)
deep_merge (~> 1.0)
facter (> 2.0.1, < 4)
fast_gettext (~> 1.1.2)
fast_gettext (~> 1.1)
hiera (>= 3.2.1, < 4)
httpclient (~> 2.8)
locale (~> 2.1)
multi_json (~> 1.10)
puppet-resource_api (~> 1.5)
semantic_puppet (~> 1.0)
puppet-resource_api (1.8.3)
puppet-resource_api (1.8.7)
hocon (>= 1.0)
puppet_forge (2.2.9)
faraday (>= 0.9.0, < 0.14.0)
puppet_forge (2.3.1)
faraday (>= 0.9.0, < 0.15.0, != 0.13.1)
faraday_middleware (>= 0.9.0, < 0.13.0)
gettext-setup (~> 0.11)
minitar
semantic_puppet (~> 1.0)
rake (12.3.2)
rdoc (6.1.1)
redcarpet (3.4.0)
rmagick (3.2.0)
rqrcode (0.10.1)
rack (2.0.7)
rack-protection (2.0.7)
rack
rake (13.0.1)
rdoc (6.2.0)
redcarpet (3.5.0)
rmagick (4.0.0)
rqrcode (1.1.2)
chunky_png (~> 1.0)
rqrcode_core (~> 0.1)
rqrcode_core (0.1.1)
rsa (0.1.4)
rsync (1.0.9)
ruby-graphviz (1.2.4)
rubyzip (1.2.3)
scrypt (3.0.6)
rubyzip (1.3.0)
scrypt (3.0.7)
ffi-compiler (>= 1.0, < 2.0)
semantic_puppet (1.0.2)
sinatra (2.0.7)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.7)
tilt (~> 2.0)
smbhash (1.0.2)
spidr (0.6.0)
spidr (0.6.1)
nokogiri (~> 1.3)
sshkey (2.0.0)
text (1.3.1)
thor (0.19.4)
thread_safe (0.3.6)
tilt (2.0.10)
tzinfo (1.2.5)
thread_safe (~> 0.1)
wordlist (0.1.1)
spidr (~> 0.2)
yard (0.9.19)
yard (0.9.20)
zip-zip (0.3)
rubyzip (>= 1.0.0)
zipruby (0.3.6)
@@ -186,6 +199,7 @@ DEPENDENCIES
rsa
ruby-graphviz
scrypt
sinatra
smbhash
sshkey
wordlist
@@ -194,4 +208,4 @@ DEPENDENCIES
zipruby
BUNDLED WITH
1.11.2
1.17.3

View File

@@ -143,7 +143,7 @@ def parse_opts(opts)
when '--failed'
options[:failed] = true
when '--affinity-group'
options[:affinity_group] = true
options[:affinity_group] = true
else
Print.err 'Invalid argument'
exit(false)

View File

@@ -78,6 +78,9 @@ VAGRANT_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/Vagrantfile.erb"
PUPPET_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/Puppetfile.erb"
AUDITBEAT_RULES_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/auditbeat_goal_rules.erb"
ELASTALERT_RULES_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/elastalert_goal_rules.erb"
## INTEGER CONSTANTS ##
RETRIES_LIMIT = 10

View File

@@ -0,0 +1,16 @@
require "json"
# With thanks, from https://gist.github.com/ascendbruce/7070951
class JSONFunctions
def self.is_json?(value)
result = JSON.parse(value)
result.is_a?(Hash)
rescue JSON::ParserError, TypeError
false
end
# prepare eval string by removing all characters other than #{}[].'_/a-zA-Z0-9
def self.sanitise_eval_string(string)
string.gsub(/[^A-Za-z0-9\[\]'\/\_\#\{\}.]/, '')
end
end

View File

@@ -14,35 +14,43 @@ class Print
def self.bright_yellow(text); colorize(text, "\e[93m"); end
def self.bold(text); colorize(text, "\e[2m"); end
def self.debug(msg)
def self.debug(msg, logger=nil)
logger.debug(msg) if logger
puts purple(' ' + msg)
end
def self.verbose(msg)
def self.verbose(msg, logger=nil)
logger.info(msg) if logger
puts grey(' ' + msg)
end
def self.err(msg)
def self.err(msg, logger=nil)
logger.error(msg) if logger
$stderr.puts red(msg)
end
def self.info(msg)
def self.info(msg, logger=nil)
logger.info(msg) if logger
puts green(msg)
end
def self.std(msg)
def self.std(msg, logger=nil)
logger.info(msg) if logger
puts yellow(msg)
end
def self.warn(msg)
def self.warn(msg, logger=nil)
logger.warn(msg) if logger
puts bright_yellow(msg)
end
# local encoders/generators write messages to stderr (stdout used to return values)
def self.local(msg)
def self.local(msg, logger=nil)
logger.info(msg) if logger
$stderr.puts cyan(msg)
end
def self.local_verbose(msg)
def self.local_verbose(msg, logger=nil)
logger.info(msg) if logger
$stderr.puts cyan(' ' + msg)
end

101
lib/helpers/rules.rb Normal file
View File

@@ -0,0 +1,101 @@
require_relative './print.rb'
require_relative './scenario.rb'
class Rules
# Generate audit and alerting rules
def self.generate_auditbeat_rules(goals)
rules = []
goals.each do |goal|
# Generate auditbeat rules based on rule type
rule_type = RuleTypes.get_rule_type(goal['goal_type'])
case rule_type
when RuleTypes::READ_FILE
rules << greedy_auditbeat_rule(goal['file_path'], 'r')
when RuleTypes::MODIFY_FILE
when RuleTypes::ACCESS_ACCOUNT
when RuleTypes::SERVICE_DOWN
when RuleTypes::SYSTEM_DOWN
else
Print.err('Unknown goal type')
raise
end
end
rules
end
# Generates a greedy read or write rule for auditbeat (e.g. /home/user/file_name resolves to /home)
def self.greedy_auditbeat_rule(path, r_w)
base_path = path.split('/')[0..1].join('/') + '/'
key = base_path.gsub(/[^A-Za-z0-9\-\_]/, '')
"-w #{base_path} -p #{r_w} -k #{key}"
end
def self.generate_elastalert_rule(hostname, module_name, goal, counter)
rule = ''
# switch case to determine which type of rule we're returning (read file, etc.)
rule_type = RuleTypes.get_rule_type(goal['goal_type'])
case rule_type
when RuleTypes::READ_FILE
rule = generate_elastalert_rule_rf(hostname, module_name, goal, counter)
when RuleTypes::MODIFY_FILE
# rule = generate_elastalert_rule_mf(hostname, module_name, goal, sub_goal)
when RuleTypes::ACCESS_ACCOUNT
# rule = generate_elastalert_rule_aa(hostname, module_name, goal, sub_goal)
when RuleTypes::SERVICE_DOWN
# rule = generate_elastalert_rule_svcd(hostname, module_name, goal, sub_goal)
when RuleTypes::SYSTEM_DOWN
# rule = generate_elastalert_rule_sysd(hostname, module_name, goal, sub_goal)
else
raise 'unknown_goal_type'
end
rule
end
def self.generate_elastalert_rule_rf(hostname, module_name, goal, counter)
"name: #{get_ea_rulename(hostname, module_name, goal, counter)}\n" +
"type: any\n" +
"index: auditbeat-*\n" +
"filter:\n" +
" - query:\n" +
" query_string:\n" +
' query: "combined_path: \"' + goal['file_path'] + '\" AND auditd.result: success AND event.action: opened-file"' + "\n" +
"alert:\n" +
" - \"elastalert.modules.alerter.exec.ExecAlerter\"\n" +
"command: [\"/usr/bin/ruby\", \"/opt/alert_actioner/alert_router.rb\"]\n" +
"pipe_match_json: true\n" +
"realert:\n" +
" minutes: 0\n"
end
def self.get_ea_rulename(hostname, module_name, goal, counter)
rule_type = RuleTypes.get_rule_type(goal['goal_type'])
return "#{hostname}-#{module_name}-#{rule_type}-#{counter}"
end
class RuleTypes
READ_FILE = 'rf'
MODIFY_FILE = 'mf'
ACCESS_ACCOUNT = 'aa'
SERVICE_DOWN = 'svcd'
SYSTEM_DOWN = 'sysd'
def self.get_rule_type(rule_type)
case rule_type
when 'read_file'
READ_FILE
when 'modify_file'
MODIFY_FILE
when 'access_account'
ACCESS_ACCOUNT
when 'service_down'
SERVICE_DOWN
when 'system_down'
SYSTEM_DOWN
else
raise 'unknown_rule_type'
end
end
end
end

15
lib/helpers/scenario.rb Normal file
View File

@@ -0,0 +1,15 @@
class ScenarioHelper
def self.get_scenario_name(scenario_path)
scenario_path.split('/').last.split('.').first + '-'
end
def self.get_prefix(options, scenario_name)
options[:prefix] ? (options[:prefix] + '-' + scenario_name) : ('SecGen-' + scenario_name)
end
def self.get_hostname(options, scenario_path, system_name)
"#{get_prefix(options, get_scenario_name(scenario_path))}#{system_name}".tr('_', '-')
end
end

View File

@@ -1,12 +1,16 @@
require_relative '../helpers/constants.rb'
require_relative '../helpers/json_functions.rb'
require 'digest/md5'
require 'securerandom'
require 'duplicate'
require 'yaml'
class Module
#Vulnerability attributes hash
attr_accessor :module_path # vulnerabilities/unix/ftp/vsftp_234_backdoor
attr_accessor :module_type # vulnerability|service|utility
attr_accessor :attributes # attributes are hashes that contain arrays of values
attr_accessor :attributes # attributes are hashes that contain arrays of values
# Each attribute is stored in a hash containing an array of values (because elements such as author can repeat).
# Module *selectors*, store filters in the attributes hash.
# XML validity ensures valid and complete information.
@@ -22,6 +26,7 @@ class Module
attr_accessor :conflicts
attr_accessor :requires
attr_accessor :goals
attr_accessor :puppet_file
attr_accessor :puppet_other_path
attr_accessor :local_calc_file
@@ -34,6 +39,7 @@ class Module
self.module_type = module_type
self.conflicts = []
self.requires = []
self.goals = []
self.attributes = {}
self.output = []
self.write_to_module_with_id = write_output_variable = ''
@@ -52,10 +58,11 @@ class Module
# @return [Object] a string for console output
def to_s
(<<-END)
#{module_type}: #{module_path}
#{module_type}: #{module_path}
attributes: #{attributes.inspect}
conflicts: #{conflicts.inspect}
requires: #{requires.inspect}
goals: #{goals.inspect}
puppet file: #{puppet_file}
puppet path: #{puppet_other_path}
END
@@ -76,6 +83,7 @@ class Module
# id: #{unique_id}
# attributes: #{attributes.inspect}
# conflicts: #{conflicts.inspect}
# goals: #{goals.inspect}
# requires: #{requires.inspect}#{input}#{out}
END
end
@@ -89,7 +97,7 @@ class Module
# @return [Object] the module path with _ rather than / for use as a variable name
def module_path_name
module_path_name = module_path.clone
module_path_name.gsub!('/','_')
module_path_name.gsub!('/', '_')
end
# @return [Object] a list of attributes that can be used to re-select the same modules
@@ -97,7 +105,7 @@ class Module
attr_flattened = {}
attributes.each do |key, array|
unless "#{key}" == 'module_type' || "#{key}" == 'conflict' || "#{key}" == 'default_input' || "#{key}" == 'requires'
unless "#{key}" == 'module_type' || "#{key}" == 'conflict' || "#{key}" == 'default_input' || "#{key}" == 'requires' || "#{key}" == 'goals'
# creates a valid regexp that can match the original module
attr_flattened["#{key}"] = Regexp.escape(array.join('~~~')).gsub(/\n\w*/, '.*').gsub(/\\ /, ' ').gsub(/~~~/, '|')
end
@@ -176,8 +184,93 @@ class Module
end
end
# Get unique rule id for the module based on a rule key/value pair
def get_unique_rule_id(prefix, system_name, rule_key, rule_value)
# TODO: This might be too long, see if there is a length limit for rule identities
prefix = prefix + "_" if prefix and prefix != ''
"#{prefix}#{system_name}_#{module_path_end}_#{rule_key}_#{rule_value.gsub(/[^\w]/, '_')}"
end
def printable_name
"#{self.attributes['name'].first} (#{self.module_path})"
end
# Resolve the string interpolation for received inputs
# e.g. convert "/home#{accounts[0].username}/#{leaked_files}" into the correct string.
def resolve_received_inputs
received_inputs_to_hash
self.received_inputs.each do |input|
# Resolve the received inputs which contain #{}
input[1].each_with_index do |string, c|
input[1][c] = interp_string(string) if contains_interp(string)
end
end
received_inputs_to_json_str
end
# Resolve the string interpolation for goals
def resolve_goals(hostname)
new_goals = []
self.goals.each do |goal|
new_goal = {}
# Add hostname to module goals
new_goal.merge!({'hostname' => hostname}) unless goal.has_key? 'hostname'
# Interpolate values that require it
goal.each_key do |key|
value = goal[key]
new_goal.merge!(key => (contains_interp(value) ? interp_string(value) : value))
end
new_goals << new_goal
end
self.goals = new_goals
end
def contains_interp(string)
string.include?('#{') and string.include?('}')
end
def received_inputs_to_hash
self.received_inputs.each do |_, array|
array.each_with_index do |value, i|
if JSONFunctions.is_json?(value)
array[i] = JSON.parse(value)
end
end
end
end
def received_inputs_to_json_str
self.received_inputs.each do |_, array|
array.each_with_index do |value, i|
if value.is_a? Hash
array[i] = value.to_json
end
end
end
end
def interp_string(string)
begin
# identify the indices of the #{ characters within the string
start_indices = string.enum_for(:scan, /#\{/).map {Regexp.last_match.begin(0)}
reference_string = "self.received_inputs"
start_indices.each_with_index do |index, counter|
rolling_index = index + 2 # we add 2 for the #{ characters
if counter > 0
rolling_index = reference_string.length + index + 2
end
string.insert(rolling_index, reference_string)
end
string = JSONFunctions.sanitise_eval_string(string)
# evaluate and parse evaluated string into required data types(e.g. "['a',['b','c']]" into ['a',['b','c']])
YAML.load(instance_eval("\"#{string}\""))
rescue NoMethodError, SyntaxError, Psych::Exception => err
Print.err "#{err}"
raise 'failed'
end
end
end

View File

@@ -2,30 +2,42 @@ require 'json'
require 'base64'
require 'duplicate'
require_relative '../helpers/scenario'
class System
attr_accessor :name
attr_accessor :hostname
attr_accessor :attributes # (basebox selection)
attr_accessor :module_selectors # (filters)
attr_accessor :module_selections # (after resolution)
attr_accessor :num_actioned_module_conflicts
attr_accessor :memory # (RAM allocation for the system)
attr_accessor :options # (command line options hash)
attr_accessor :scenario_path # (path to scenario file associated with this system)
attr_accessor :goals # scenario-level goals []
# Attributes for resetting retry loop
attr_accessor :available_mods #(command line options hash)
attr_accessor :original_datastores #(command line options hash)
attr_accessor :original_module_selectors #(command line options hash)
attr_accessor :original_available_modules #(command line options hash)
attr_accessor :available_mods
attr_accessor :original_datastores
attr_accessor :original_module_selectors
attr_accessor :original_available_modules
# Initalizes System object
# @param [Object] name of the system
# @param [Object] attributes such as base box selection
# @param [Object] module_selectors these are modules that define filters for selecting the actual modules to use
def initialize(name, attributes, module_selectors)
def initialize(name, attributes, module_selectors, scenario_file, options)
self.name = name
self.attributes = attributes
self.module_selectors = module_selectors
self.module_selections = []
self.num_actioned_module_conflicts = 0
self.memory = "512"
self.options = options
self.scenario_path = scenario_file
self.goals = []
set_hostname
end
# selects from the available modules, based on the selection filters that have been specified
@@ -260,8 +272,8 @@ class System
# parse the datastore
parsed_datastore_element = JSON.parse(datastore_retrieved.first)
# Sanitise with whitelist of used characters: ' [ ]
access_json = datastore_access_json.gsub(/[^A-Za-z0-9\[\]'_]/, '')
# Sanitise with whitelist
access_json = JSONFunctions.sanitise_eval_string(datastore_access_json)
# get data from access_json string
begin
@@ -463,4 +475,36 @@ class System
modules_to_add
end
def has_module(module_name)
has_module = false
module_selections.each do |mod|
if mod.module_path_end == module_name
has_module = true
end
end
has_module
end
def get_module(module_name)
selected_module = nil
module_selections.each do |mod|
if mod.module_path_end == module_name
selected_module = mod
end
end
selected_module
end
def set_options(opts)
self.options = opts if opts != nil and self.options == {}
end
def set_hostname
self.hostname = ScenarioHelper.get_hostname(self.options, self.scenario_path, self.name)
end
def get_hostname
set_hostname
self.hostname
end
end

View File

@@ -1,7 +1,9 @@
require 'erb'
require_relative '../helpers/constants.rb'
require_relative '../helpers/rules.rb'
require_relative 'xml_scenario_generator.rb'
require_relative 'xml_marker_generator.rb'
require_relative 'xml_alertaction_config_generator.rb'
require_relative 'ctfd_generator.rb'
require 'fileutils'
require 'librarian'
@@ -30,11 +32,12 @@ class ProjectFilesCreator
@scenario = scenario
@time = Time.new.to_s
@options = options
@scenario_networks = Hash.new { |h, k| h[k] = 1 }
@scenario_networks = Hash.new {|h, k| h[k] = 1}
@option_range_map = {}
# Packer builder type
@builder_type = @options.has_key?(:esxi_url) ? :vmware_iso : :virtualbox_iso
resolve_interp_strings
end
# Generate all relevant files for the project
@@ -44,13 +47,13 @@ class ProjectFilesCreator
if File.exists? "#{@out_dir}/Vagrantfile" or File.exists? "#{@out_dir}/puppet"
dest_dir = "#{@out_dir}/MOVED_#{Time.new.strftime("%Y%m%d_%H%M%S")}"
Print.warn "Project already built to this directory -- moving last build to: #{dest_dir}"
Dir.glob( "#{@out_dir}/**/*" ).select { |f| File.file?( f ) }.each do |f|
Dir.glob("#{@out_dir}/**/*").select {|f| File.file?(f)}.each do |f|
dest = "#{dest_dir}/#{f}"
FileUtils.mkdir_p( File.dirname( dest ) )
FileUtils.mkdir_p(File.dirname(dest))
if f =~ /\.vagrant/
FileUtils.cp( f, dest )
FileUtils.cp(f, dest)
else
FileUtils.mv( f, dest )
FileUtils.mv(f, dest)
end
end
end
@@ -89,7 +92,7 @@ class ProjectFilesCreator
if File.file? packerfile_path
Print.info "Would you like to use the packerfile to create the packerfile from the given url (y/n)"
# TODO: remove user interaction, this should be set via a config option
(Print.info "Exiting as vagrant needs the basebox to continue"; abort) unless ['y','yes'].include?(STDIN.gets.chomp.downcase)
(Print.info "Exiting as vagrant needs the basebox to continue"; abort) unless ['y', 'yes'].include?(STDIN.gets.chomp.downcase)
Print.std "Packerfile #{packerfile_path.split('/').last} found, building basebox #{url.split('/').last} via packer"
template_based_file_write(packerfile_path, packerfile_path.split(/.erb$/).first)
@@ -108,6 +111,82 @@ class ProjectFilesCreator
end
end
end
# Create client side auto-grading config files (auditbeat)
if system.has_module('auditbeat')
auditbeat_rules_file = "#{path}/modules/auditbeat/files/rules/auditbeat_rules_file.conf"
@rules = []
system.module_selections.each do |module_selection|
if module_selection.goals != []
@rules << Rules.generate_auditbeat_rules(module_selection.goals)
end
end
if system.goals != []
@rules << Rules.generate_auditbeat_rules(system.goals)
end
@rules = @rules.flatten.uniq
Print.std "Creating client side auditing rules: #{auditbeat_rules_file}"
if @rules.size > 0
template_based_file_write(AUDITBEAT_RULES_TEMPLATE_FILE, auditbeat_rules_file)
end
end
# Create server-side auto-grading config files (elastalert)
if system.has_module('elastalert')
@systems.each do |sys|
@hostname = sys.get_hostname
if sys.goals != []
sys.goals.each_with_index do |goal, i|
@system_name = sys.name
@goal = goal
@counter = i
rule_name = Rules.get_ea_rulename(@hostname, @system_name, @goal, @counter)
elastalert_rules_file = "#{path}/modules/elastalert/files/rules/#{rule_name}.yaml"
Print.std "Creating server side alerting rules (system): #{elastalert_rules_file}"
template_based_file_write(ELASTALERT_RULES_TEMPLATE_FILE, elastalert_rules_file)
end
end
sys.module_selections.each do |module_selection|
if module_selection.goals != {}
module_selection.goals.each_with_index do |goal, i|
@module_name = module_selection.module_path_end
@goal = goal
@counter = i
rule_name = Rules.get_ea_rulename(@hostname, @module_name, @goal, @counter)
elastalert_rules_file = "#{path}/modules/elastalert/files/rules/#{rule_name}.yaml"
Print.std "Creating server side alerting rules: #{elastalert_rules_file}"
template_based_file_write(ELASTALERT_RULES_TEMPLATE_FILE, elastalert_rules_file)
end
end
end
end
end
# TODO: Refactor to include in the loop above if possible
if system.has_module('analysis_alert_action_server')
Print.info 'AlertActioner: Copying shared libs...'
aa_lib_dir = "#{path}/modules/analysis_alert_action_server/files/alert_actioner/lib"
FileUtils.mkdir_p(aa_lib_dir)
FileUtils.cp_r("#{ROOT_DIR}/lib/helpers/print.rb", "#{aa_lib_dir}/print.rb")
FileUtils.cp_r("#{ROOT_DIR}/lib/readers/xml_reader.rb", "#{aa_lib_dir}/xml_reader.rb")
FileUtils.cp_r("#{ROOT_DIR}/lib/schemas/alertactioner_config_schema.xsd", "#{aa_lib_dir}/alertactioner_config_schema.xsd")
FileUtils.cp_r("#{ROOT_DIR}/lib/helpers/ovirt.rb", "#{aa_lib_dir}/ovirt.rb")
Print.info 'AlertActioner: Generating AA configs...'
aa_conf_dir = "#{path}/modules/analysis_alert_action_server/files/alert_actioner/config/"
FileUtils.mkdir_p(aa_conf_dir)
# Get the config json object from the alert_actioner
aa_confs = JSON.parse(system.get_module('analysis_alert_action_server').received_inputs['aaa_config'][0])['aa_configs']
xml_aa_conf_file = "#{aa_conf_dir}#{@out_dir.split('/')[-1]}.xml"
xml_aa_conf_generator = XmlAlertActionConfigGenerator.new(@systems, @scenario, @time, aa_confs, @options)
xml = xml_aa_conf_generator.output
Print.std "AlertActioner: Creating alert_actioner configuration file: #{xml_aa_conf_file}"
write_data_to_file(xml, xml_aa_conf_file)
end
end
# Create environments/production/environment.conf - Required in Puppet 4+
@@ -125,14 +204,7 @@ class ProjectFilesCreator
xml_report_generator = XmlScenarioGenerator.new(@systems, @scenario, @time)
xml = xml_report_generator.output
Print.std "Creating scenario definition file: #{xfile}"
begin
File.open(xfile, 'w+') do |file|
file.write(xml)
end
rescue StandardError => e
Print.err "Error writing file: #{e.message}"
abort
end
write_data_to_file(xml, xfile)
# Create the marker xml file
x2file = "#{@out_dir}/#{FLAGS_FILENAME}"
@@ -140,14 +212,7 @@ class ProjectFilesCreator
xml_marker_generator = XmlMarkerGenerator.new(@systems, @scenario, @time)
xml = xml_marker_generator.output
Print.std "Creating flags and hints file: #{x2file}"
begin
File.open(x2file, 'w+') do |file|
file.write(xml)
end
rescue StandardError => e
Print.err "Error writing file: #{e.message}"
abort
end
write_data_to_file(xml, x2file)
# Create the CTFd zip file for import
ctfdfile = "#{@out_dir}/CTFd_importable.zip"
@@ -158,10 +223,10 @@ class ProjectFilesCreator
# zip up the CTFd export
begin
Zip::ZipFile.open(ctfdfile, Zip::ZipFile::CREATE) { |zipfile|
Zip::ZipFile.open(ctfdfile, Zip::ZipFile::CREATE) {|zipfile|
zipfile.mkdir("db")
ctfd_files.each do |ctfd_file_name, ctfd_file_content|
zipfile.get_output_stream("db/#{ctfd_file_name}") { |f|
zipfile.get_output_stream("db/#{ctfd_file_name}") {|f|
f.print ctfd_file_content
}
end
@@ -187,6 +252,31 @@ class ProjectFilesCreator
end
def write_data_to_file(data, path)
begin
File.open(path, 'w+') do |file|
file.write(data)
end
rescue StandardError => e
Print.err "Error writing file: #{e.message}"
abort
end
end
# Goal string interpolation for the whole system
# prior to calling the rule generator multiple times
def resolve_interp_strings
@systems.each do |system|
system.module_selections.each do |module_selection|
module_selection.resolve_received_inputs
end
system.module_selections.each do |module_selection|
module_selection.resolve_goals(system.get_hostname)
end
end
end
# @param [Object] template erb path
# @param [Object] filename file to write to
def template_based_file_write(template, filename)
@@ -213,15 +303,15 @@ class ProjectFilesCreator
if current_network.received_inputs.include? 'IP_address'
ip_address = current_network.received_inputs['IP_address'].first
elsif @options.has_key? :ip_ranges
# if we have options[:ip_ranges] we want to use those instead of the ip_range argument.
# Store the mappings of scenario_ip_ranges => @options[:ip_range] in @option_range_map
# if we have options[:ip_ranges] we want to use those instead of the ip_range argument.
# Store the mappings of scenario_ip_ranges => @options[:ip_range] in @option_range_map
# Have we seen this scenario_ip_range before? If so, use the value we've assigned
if @option_range_map.has_key? scenario_ip_range
ip_range = @option_range_map[scenario_ip_range]
else
# Remove options_ips that have already been used
options_ips = @options[:ip_ranges]
options_ips.delete_if { |ip| @option_range_map.has_value? ip }
options_ips.delete_if {|ip| @option_range_map.has_value? ip}
@option_range_map[scenario_ip_range] = options_ips.first
ip_range = options_ips.first
end
@@ -246,13 +336,33 @@ class ProjectFilesCreator
split_ip.join('.')
end
# Replace 'network' with 'snoop' where the system name contains snoop
# Replace 'network' with 'snoop' where the system name contains snoop
def get_ovirt_network_name(system_name, network_name)
split_name = network_name.split('-')
split_name[1] = 'snoop' if system_name.include? 'snoop'
split_name.join('-')
end
# Determine how much memory the system requires for Vagrantfile
def resolve_memory(system)
if @options.has_key? :memory_per_vm
memory = @options[:memory_per_vm]
elsif @options.has_key? :total_memory
memory = @options[:total_memory].to_i / @systems.length.to_i
elsif (@options.has_key? :ovirtuser) && (@options.has_key? :ovirtpass) && (@base_type.include? 'desktop')
memory = '1536'
else
memory = '512'
end
system.module_selections.each do |mod|
if mod.module_path_name.include? "elasticsearch"
memory = '8192'
end
end
memory
end
# Returns binding for erb files (access to variables in this classes scope)
# @return binding
def get_binding

View File

@@ -0,0 +1,155 @@
require 'nokogiri'
require_relative '../helpers/rules'
# Convert systems objects into alertactioner xml configuration
class XmlAlertActionConfigGenerator
# @param [Object] systems the list of systems
# @param [Object] scenario the scenario file used to generate
# @param [Object] time the current time as a string
# @param [Array[Hash]] the alert_actioner configuration settings (list of aa_conf JSON hashes)
def initialize(systems, scenario, time, aa_confs, options)
@systems = systems
@scenario = scenario
@time = time
@aa_confs = aa_confs
@options = options
@alert_actions = []
end
# outputs a XML AlertActioner configuration file
# @return [Object] xml string
def output
create_alert_actions
generate_xml_config
end
def create_alert_actions
Print.info 'AlertActioner: Creating alert actions from aa_conf.'
@aa_confs.each do |aa_conf|
if aa_conf['mapping_type']
case aa_conf['mapping_type']
when 'all_goal_flags_to_hacktivity'
all_goal_flags_to_hacktivity(aa_conf)
when 'all_goal_messages_to_host'
all_goal_message_host(aa_conf)
else
Print.err("AlertActioner Config: Invalid mapping type #{aa_conf['mapping_type']}")
exit(1)
end
elsif aa_conf['mapping']
# TODO: Implement me later
else
Print.err "AlertActioner Config: Either mapping_type or mapping required."
exit(1)
end
end
end
def all_goal_message_host(aa_conf)
@systems.each do |system|
system.module_selections.each do |module_selection|
module_name = module_selection.module_path_end
module_goals = module_selection.goals
if module_goals != []
# Iterate over the goals
module_selection.goals.each_with_index do |goal, i|
@alert_actions << {'alert_name' => Rules.get_ea_rulename(system.hostname, module_name, goal, i),
'action_type' => 'MessageAction',
'host' => aa_conf['host'],
'sender' => aa_conf['sender'],
'password' => aa_conf['password'],
'recipient' => aa_conf['recipient'],
'message_header' => aa_conf['message_header'],
'message_subtext' => aa_conf['message_subtext']
}
end
end
end
end
end
def all_goal_flags_to_hacktivity(aa_conf)
@systems.each do |system|
if system.goals != []
@alert_actions = @alert_actions + get_web_alertactions(aa_conf, system.name, system.goals, $datastore['goal_flags'], system.hostname)
end
system.module_selections.each do |module_selection|
@alert_actions = @alert_actions + get_web_alertactions(aa_conf, module_selection.module_path_end, module_selection.goals, module_selection.received_inputs['goal_flags'], system.hostname)
end
end
end
def get_web_alertactions(aa_conf, name, goals, goal_flags, hostname)
alert_actions = []
# Validate whether there are an equal number of goals and goal_flags + warn / error here if not...
if goals != [] or goal_flags != nil
goals_qty = goals.size
flags_qty = goal_flags.size
unless goals_qty == flags_qty
Print.err "AlertActioner: ERROR for mapping_type: #{aa_conf['mapping_type']}"
Print.err "Unequal number of goals and goal_flags for: #{name}"
Print.err "Goals qty: #{goals_qty} vs Flags qty: #{flags_qty}"
exit(1)
end
if goals != [] and goal_flags != nil
# Iterate over the goals
goals.each_with_index do |goal, i|
alert_actions << {'alert_name' => Rules.get_ea_rulename(hostname, name, goal, i),
'action_type' => 'WebAction',
'target' => aa_conf['target'],
'request_type' => 'POST',
'data' => goal_flags[i]
}
end
end
end
alert_actions
end
def generate_xml_config
Print.info 'Creating AlertActioner xml config...'
ns = {
'xmlns' => "http://www.github/cliffe/SecGen/alertactioner_config",
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
'xsi:schemaLocation' => "http://www.github/cliffe/SecGen/alertactioner_config"
}
builder = Nokogiri::XML::Builder.new do |xml|
xml.alertactioner(ns) {
xml.comment 'This AlertActioner configuration file was generated by SecGen'
xml.comment "#{@time}"
xml.comment "Based on a fulfilment of scenario: #{@scenario}"
@alert_actions.each {|alert_action|
xml.alertaction {
xml.alert_name alert_action['alert_name']
case alert_action['action_type']
when 'WebAction'
xml.WebAction {
xml.target alert_action['target']
xml.request_type alert_action['request_type']
xml.data alert_action['data']
}
when 'MessageAction'
xml.MessageAction {
xml.host alert_action['host']
xml.sender alert_action['sender']
xml.password alert_action['password']
xml.recipient alert_action['recipient']
xml.message_header alert_action['message_header']
xml.message_subtext alert_action['message_subtext']
}
else
# TODO: Add more actions
Print.err "XmlAlertActionConfigGenerator: Invalid alertaction type - #{alert_action['action_type']}"
end
}
}
}
end
builder.to_xml
end
end

View File

@@ -2,9 +2,9 @@ require 'nokogiri'
require_relative '../helpers/constants.rb'
require_relative '../objects/module'
require_relative 'system_reader.rb'
require_relative 'xml_reader.rb'
class ModuleReader
class ModuleReader < XMLReader
def self.get_all_available_modules
Print.info 'Reading available base modules...'
@@ -105,30 +105,9 @@ class ModuleReader
end
Print.verbose "Reading #{module_type}: #{module_path}"
doc, xsd = nil
begin
doc = Nokogiri::XML(File.read(file))
rescue
Print.err "Failed to read #{module_type} metadata file (#{file})"
exit
end
# validate scenario XML against schema
begin
xsd = Nokogiri::XML::Schema(File.read(schema_file))
xsd.validate(doc).each do |error|
Print.err "Error in #{module_type} metadata file (#{file}):"
Print.err ' ' + error.message
exit
end
rescue Exception => e
Print.err "Failed to validate #{module_type} metadata file (#{file}): against schema (#{schema_file})"
Print.err e.message
exit
end
# remove xml namespaces for ease of processing
doc.remove_namespaces!
# Parse and validate the schema
doc = parse_doc(file, schema_file, module_type)
new_module = Module.new(module_type)
# save module path (and as an attribute for filtering)
@@ -159,12 +138,8 @@ class ModuleReader
# for each element in the vulnerability
doc.xpath("/#{module_type}/*").each do |module_doc|
# new_module.attributes[module_doc.name] = module_doc.content
# creates the array if null
(new_module.attributes[module_doc.name] ||= []).push(module_doc.content)
end
# for each conflict in the module
@@ -185,6 +160,22 @@ class ModuleReader
new_module.requires.push(require)
end
# for each goal in the module
doc.xpath("/#{module_type}/goals").each do |goals_doc|
goals = []
goals_doc.elements.each {|node|
goal_type = node.name
goal_hash = {'goal_type' => goal_type,}
node.children.each {|subnode|
unless subnode.text?
goal_hash.merge!({subnode.name => subnode.content.strip})
end
}
goals << goal_hash
}
new_module.goals = goals
end
# for each default input
doc.xpath("/#{module_type}/default_input").each do |inputs_doc|
inputs_doc.xpath('descendant::vulnerability | descendant::service | descendant::utility | descendant::network | descendant::base | descendant::encoder | descendant::generator').each do |module_node|
@@ -220,9 +211,7 @@ class ModuleReader
(new_module.default_inputs_selectors["#{into}"] ||= []).unshift(module_selector)
module_node.xpath('@*').each do |attr|
module_selector.attributes["#{attr.name}"] = [attr.text] unless attr.text.nil? || attr.text == ''
end
module_selector.attributes = read_attributes(module_node)
Print.verbose " #{module_node.name} (#{module_selector.unique_id}), selecting based on:"
module_selector.attributes.each do |attr|
if attr[0] && attr[1] && attr[0].to_s != "module_type"

View File

@@ -3,52 +3,27 @@ require 'digest'
require_relative '../objects/system'
require_relative '../objects/module'
require_relative 'xml_reader.rb'
class SystemReader
class SystemReader < XMLReader
# uses nokogiri to extract all system information from scenario.xml
# This includes module filters, which are module objects that contain filters for selecting
# from the actual modules that are available
# @return [Array] Array containing Systems objects
def self.read_scenario(scenario_file)
def self.read_scenario(scenario_file, options)
systems = []
Print.verbose "Reading scenario file: #{scenario_file}"
doc, xsd = nil
begin
doc = Nokogiri::XML(File.read(scenario_file))
rescue
Print.err "Failed to read scenario configuration file (#{scenario_file})"
exit
end
# validate scenario XML against schema
begin
xsd = Nokogiri::XML::Schema(File.open(SCENARIO_SCHEMA_FILE))
xsd.validate(scenario_file).each do |error|
Print.err "Error in scenario configuration file (#{scenario_file}):"
Print.err " #{error.line}: #{error.message}"
exit
end
rescue Exception => e
Print.err "Failed to validate scenario configuration file (#{scenario_file}): against schema (#{SCENARIO_SCHEMA_FILE})"
Print.err e.message
exit
end
# remove xml namespaces for ease of processing
doc.remove_namespaces!
# Parse and validate the schema
doc = parse_doc(scenario_file, SCENARIO_SCHEMA_FILE, 'scenario')
doc.xpath('/scenario/system').each_with_index do |system_node, system_index|
module_selectors = []
system_attributes = {}
system_name = system_node.at_xpath('system_name').text
Print.verbose "system: #{system_name}"
# system attributes, such as basebox selection
system_node.xpath('@*').each do |attr|
system_attributes["#{attr.name}"] = attr.text unless attr.text.nil? || attr.text == ''
end
system_attributes = read_attributes(system_node)
# literal values to store directly in a datastore
system_node.xpath('*[@into_datastore]/value').each do |value|
@@ -146,9 +121,28 @@ class SystemReader
end
end
systems << System.new(system_name, system_attributes, module_selectors)
# Create new system object before reading goals as we need the hostname
system = System.new(system_name, system_attributes, module_selectors, scenario_file, options)
# Parse goals
system_node.xpath("goals").each do |goals_doc|
goals_doc.elements.each {|node|
goal_type = node.name
goal_hash = {'goal_type' => goal_type, }
node.children.each {|subnode|
unless subnode.text?
goal_hash.merge!({subnode.name => subnode.content.strip})
end
}
goal_hash.merge!({'hostname' => system.get_hostname}) unless goal_hash.has_key? 'hostname'
system.goals << goal_hash
}
end
systems << system
end
return systems
end
end
end

49
lib/readers/xml_reader.rb Normal file
View File

@@ -0,0 +1,49 @@
require 'nokogiri'
require 'digest'
class XMLReader
# uses nokogiri to extract all system information from scenario.xml
# This includes module filters, which are module objects that contain filters for selecting
# from the actual modules that are available
# @return [Array] Array containing Systems objects
def self.parse_doc(file_path, schema, type)
doc = nil
begin
doc = Nokogiri::XML(File.read(file_path))
rescue
Print.err "Failed to read #{type} configuration file (#{file_path})"
exit
end
validate_xml(doc, file_path, schema, type)
# remove xml namespaces for ease of processing
doc.remove_namespaces!
end
def self.validate_xml(doc, file_path, schema, type)
# validate XML against schema
begin
xsd = Nokogiri::XML::Schema(File.open(schema))
xsd.validate(doc).each do |error|
Print.err "Error in #{type} configuration file (#{file_path}):"
Print.err ' ' + error.message
exit
end
rescue Exception => e
Print.err "Failed to validate #{type} xml file (#{file_path}): against schema (#{schema})"
Print.err e.message
exit
end
end
def self.read_attributes(node)
attributes = {}
node.xpath('@*').each do |attr|
attributes["#{attr.name}"] = [attr.text] unless attr.text.nil? || attr.text == ''
end
attributes
end
end

View File

@@ -0,0 +1,114 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.github/cliffe/SecGen/alertactioner_config"
xmlns="http://www.github/cliffe/SecGen/alertactioner_config"
elementFormDefault="qualified">
<xs:element name="alertactioner">
<xs:complexType>
<xs:sequence>
<xs:element name='alertaction' type='AlertActionType' minOccurs='1' maxOccurs='unbounded' />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="AlertActionType">
<xs:sequence>
<xs:element name="alert_name" minOccurs="1" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-Z0-9_\-]+"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="WebAction" type="WebActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="MessageAction" type="MessageActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="VirtAction" type="VirtActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="VirtAddNICAction" type="VirtAddNICActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="VirtRemoveNICAction" type="VirtRemoveNICActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="VirtAddDiskAction" type="VirtAddDiskActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="VirtRemoveDiskAction" type="VirtRemoveDiskActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="VMCommandAction" type="CommandActionType" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="IRCAction" type="xs:string" minOccurs="1" maxOccurs="unbounded" />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="WebActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="target" type="xs:anyURI" minOccurs="1" maxOccurs="1" />
<xs:element name="request_type" minOccurs="1" maxOccurs="1" >
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="GET"/>
<xs:enumeration value="POST"/>
<xs:enumeration value="PUT"/>
<xs:enumeration value="DELETE"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="data" type="xs:string" minOccurs="0" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="CommandActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="host" type="xs:anyURI" minOccurs="1" maxOccurs="1" />
<xs:element name="username" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="password" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="command_string" type="xs:string" minOccurs="1" maxOccurs="unbounded"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="MessageActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="host" type="xs:anyURI" minOccurs="1" maxOccurs="1" />
<xs:element name="sender" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="password" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="recipient" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="message_header" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="message_subtext" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="VirtActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="virt_config" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="vm_name" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="command_string" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="VirtAddNICActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="virt_config" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="vm_name" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="network_name" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="VirtRemoveNICActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="virt_config" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="vm_name" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="network_name" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="VirtAddDiskActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="virt_config" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="vm_name" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="disk_alias" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="VirtRemoveDiskActionType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="virt_config" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="vm_name" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="disk_alias" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
</xs:schema>

View File

@@ -70,6 +70,41 @@
</xs:sequence>
</xs:complexType>
<xs:complexType name="GoalsType">
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name='read_file' minOccurs='0' maxOccurs='unbounded' >
<xs:complexType>
<xs:sequence>
<xs:element name='file_path' type='xs:string' minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='modify_file' minOccurs='0' maxOccurs='unbounded' >
<xs:complexType>
<xs:sequence>
<xs:element name='file_path' type='xs:string' minOccurs="1" maxOccurs="unbounded"/>
<!-- file_contents: either a hash or regexp to match -->
<xs:element name='file_contents' type='xs:string' minOccurs="0" maxOccurs="unbounded"/>
<!-- written_by: include username or uid range (e.g. uid>=1000) -->
<xs:element name='written_by' type='xs:string' minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='access_account' minOccurs='0' maxOccurs='unbounded' >
<xs:complexType>
<xs:sequence>
<xs:element name='account_name' type='xs:string' minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='service_down' type='xs:string' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='system_down' type='xs:string' minOccurs='0' maxOccurs='unbounded' />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="SystemType">
<xs:sequence>
<xs:element name="system_name" minOccurs="1" maxOccurs="1">
@@ -81,6 +116,7 @@
</xs:element>
<xs:element name='base' type='BaseType' minOccurs='1' maxOccurs='1' />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="goals" type="GoalsType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="input" type="InputElements" minOccurs="0" maxOccurs="unbounded" />
<xs:element name='vulnerability' type='VulnerabilityType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='service' type='ServiceUtilityBuildEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
@@ -103,6 +139,7 @@
<xs:attribute name='distro' type='xs:string'/>
<xs:attribute name='url' type='xs:string'/>
<xs:attribute name='vagrantbase' type='xs:string'/>
<xs:attribute name='esxi_url' type='xs:string'/>
<xs:attribute name='ovirt_template' type='xs:string'/>
<xs:attribute name='reference' type='xs:string'/>
<xs:attribute name='software_license' type='xs:string'/>

View File

@@ -93,6 +93,45 @@
<xs:enumeration value="base64"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="GoalsType">
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name='read_file' minOccurs='0' maxOccurs='unbounded' >
<xs:complexType>
<xs:sequence>
<!-- Hostname automatically populated from module goals -->
<xs:element name='hostname' type='xs:string' minOccurs="0" maxOccurs="unbounded"/>
<xs:element name='file_path' type='xs:string' minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='modify_file' minOccurs='0' maxOccurs='unbounded' >
<xs:complexType>
<xs:sequence>
<!-- Hostname automatically populated from module goals -->
<xs:element name='hostname' type='xs:string' minOccurs="0" maxOccurs="unbounded"/>
<xs:element name='file_path' type='xs:string' minOccurs="1" maxOccurs="unbounded"/>
<!-- file_contents: either a hash or regexp to match -->
<xs:element name='file_contents' type='xs:string' minOccurs="0" maxOccurs="unbounded"/>
<!-- written_by: include username or uid range (e.g. uid>=1000) -->
<xs:element name='written_by' type='xs:string' minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='access_account' minOccurs='0' maxOccurs='unbounded' >
<xs:complexType>
<xs:sequence>
<!-- Hostname automatically populated from module goals -->
<xs:element name='hostname' type='xs:string' minOccurs="0" maxOccurs="unbounded"/>
<xs:element name='account_name' type='xs:string' minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name='service_down' type='xs:string' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='system_down' type='xs:string' minOccurs='0' maxOccurs='unbounded' />
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="InputElements">
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
@@ -129,6 +168,7 @@
<xs:attribute name="challenge_type" type="xs:string"/>
<xs:attribute name="challenge_subtype" type="xs:string"/>
<xs:attribute name="difficulty" type="xs:string"/>
<xs:attribute name="goals" type="xs:string"/>
<!--optional vulnerability inputs-->
<xs:attribute name="read_fact" type="xs:string"/>
@@ -205,6 +245,7 @@
<xs:element name="challenge_type" type="challengeTypeOptions" minOccurs="0" maxOccurs="1"/>
<xs:element name="challenge_subtype" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="difficulty" type="difficultyOptions" minOccurs="0" maxOccurs="1"/>
<xs:element name="goals" type="GoalsType" minOccurs="0" maxOccurs="unbounded"/>
<!--optional input values-->
<xs:element name="read_fact" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>

View File

@@ -8,7 +8,9 @@
forge "https://forgeapi.puppetlabs.com"
mod 'puppetlabs-stdlib', '4.24.0' # stdlib enables parsejson() in manifests and other useful functions
mod 'puppetlabs-stdlib', '4.25.1' # stdlib enables parsejson() in manifests and other useful functions
mod 'puppetlabs-concat', '5.2.0'
mod 'puppetlabs-vcsrepo', '2.0.0'
mod 'puppetlabs-apt', '7.4.0' # pin apt to 7.4.0 as current version is incompatible with our base boxes
mod 'SecGen-secgen_functions', :path => '<%= SECGEN_FUNCTIONS_PUPPET_DIR %>'

View File

@@ -6,7 +6,8 @@
# Based on <%= @scenario %>
<% require 'json'
require 'base64'
require 'securerandom' -%>
require 'securerandom'
require_relative './lib/helpers/scenario.rb'-%>
<% scenario_name = @scenario.split('/').last.split('.').first + '-'
prefix = @options[:prefix] ? (@options[:prefix] + '-' + scenario_name) : ('SecGen-' + scenario_name) -%>
@@ -28,7 +29,8 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
end
end
end
end -%>
end
system.memory = resolve_memory(system) -%>
config.vm.define "<%= system.name %>" do |<%= system.name %>|
<% if (@options.has_key? :ovirtuser) && (@options.has_key? :ovirtpass) %>
#oVirt provider begin
@@ -44,13 +46,12 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
end %>
<%=
" ovirt.template = '#{@ovirt_base_template}'" %>
<%= if @options.has_key? :memory_per_vm
" ovirt.memory_size = '#{@options[:memory_per_vm]} MB'\n"
elsif @options.has_key? :total_memory
" ovirt.memory_size = '#{(@options[:total_memory].to_i / @systems.length.to_i)} MB'\n"
else
" ovirt.memory_size = '3000 MB'
ovirt.memory_guaranteed = '512 MB'\n"
<%=
" ovirt.memory_size = '#{system.memory} MB'\n" -%>
<%= if @base_type.include? 'desktop'
" ovirt.memory_guaranteed = '512 MB'\n"
elsif system.memory.to_i >= 4096
" ovirt.memory_guaranteed = '4096 MB'\n"
end -%>
<%= if @options.has_key? :cpu_cores
" ovirt.cpu_cores = #{@options[:cpu_cores]}\n"
@@ -77,9 +78,8 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
<%= if @options.has_key? :esxi_disktype
" esxi.guest_disk_type = '#{@options[:esxi_disktype]}'"
end -%>
<%= if @options.has_key? :memory_per_vm
" esxi.guest_memsize = '#{@options[:memory_per_vm]}'"
end -%>
<%= " esxi.guest_memsize = '#{system.memory} MB'\n"
-%>
<%= if @options.has_key? :cpu_cores
" esxi.guest_numvcpus = #{@options[:cpu_cores]}\n"
end -%>
@@ -87,7 +87,7 @@ end
# End ESXi provider
<%
else %>
config.vm.provider :virtualbox do |vb|
<%= system.name %>.vm.provider :virtualbox do |vb|
<% system.module_selections.each do |selected_module|
if selected_module.module_type == 'base'
@cpu_word_size = selected_module.attributes['cpu_word_size'].first.downcase
@@ -108,11 +108,7 @@ end
end -%>
<%= vtxpid = (@options.has_key? :vtxvpid) ? 'on' : 'off'
" vb.customize ['modifyvm', :id, '--vtxvpid', '#{vtxpid}']\n" -%>
<%= if @options.has_key? :memory_per_vm
" vb.memory = #{@options[:memory_per_vm]}\n"
elsif @options.has_key? :total_memory
" vb.memory = #{@options[:total_memory]}/#{@systems.length}\n"
end -%>
<%= " vb.memory = '#{system.memory}'\n"-%>
<%= if @options.has_key? :cpu_cores
" vb.cpus = #{@options[:cpu_cores]}\n"
end -%>
@@ -144,12 +140,12 @@ end
<% if (@options.has_key? :ovirtuser) && (@options.has_key? :ovirtpass) %> # TODO
<%# if selected_module.attributes['platform'].first.downcase != 'windows' %>
<%# gets stuck setting host name on Windows XP %>
<%= system.name %>.vm.hostname = '<%= "#{prefix}#{system.name}".tr('_', '-') %>'
<%= system.name %>.vm.hostname = '<%= system.get_hostname %>'
<%# end %>
<%= system.name %>.vm.box = 'ovirt4'
<%= system.name %>.vm.box_url = 'https://github.com/myoung34/vagrant-ovirt4/blob/master/example_box/dummy.box?raw=true'
<% elsif (@options.has_key? :esxiuser) && (@options.has_key? :esxipass) %>
<%= system.name %>.vm.hostname = '<%= "#{prefix}#{system.name}".tr('_', '-') %>'
<%= system.name %>.vm.hostname = '<%= system.get_hostname %>'
<%= system.name %>.vm.box = "<%= selected_module.module_path_name %>"
<%= system.name %>.vm.box_url = "<%= selected_module.attributes['esxi_url'].first %>"
<% else %>
@@ -219,7 +215,9 @@ end
<% end -%>
<%=module_name%>.module_path = "<%="puppet/#{system.name}/modules"%>"
<% if selected_module.attributes['platform'].first.downcase != 'windows' %>
<%=module_name%>.options = "--disable_warnings=deprecations"
<%=module_name%>.environment_path = "environments/"
<%=module_name%>.environment_variables = {'RUBYOPT' => '-W0'}
<%=module_name%>.environment = "production"
<%=module_name%>.synced_folder_type = "rsync"
<% end %>

View File

@@ -0,0 +1 @@
<%= @rules.join("\n")%>

View File

@@ -0,0 +1,2 @@
<% require './lib/helpers/rules' -%>
<%= Rules.generate_elastalert_rule(@hostname, @module_name, @goal, @counter) %>

View File

@@ -91,7 +91,7 @@ def generate_scenarios(selected_base)
module_selections << mod
module_selections << get_network_module
system = System.new(system_name, {}, [])
system = System.new(system_name, {}, [], 'testing.xml', {} )
system.module_selections = module_selections
xml_generator = XmlScenarioGenerator.new([system], system_name, Time.new.to_s)

View File

@@ -0,0 +1,227 @@
# Change Log
All notable changes to this project will be documented in this file.
## [v4.0.0](https://github.com/petems/petems-swap_file/tree/v4.0.0) (2017-07-09)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v3.1.4...v4.0.0)
**Closed issues:**
- Push v3.1.3 to Forge [\#72](https://github.com/petems/petems-swap_file/issues/72)
**Merged pull requests:**
- Linting fixes [\#76](https://github.com/petems/petems-swap_file/pull/76) ([petems](https://github.com/petems))
- Update metadata.json [\#75](https://github.com/petems/petems-swap_file/pull/75) ([petems](https://github.com/petems))
- Fixes issue with empty fact [\#74](https://github.com/petems/petems-swap_file/pull/74) ([petems](https://github.com/petems))
- Update to Augeas systcl module [\#73](https://github.com/petems/petems-swap_file/pull/73) ([antyale](https://github.com/antyale))
## [v3.1.4](https://github.com/petems/petems-swap_file/tree/v3.1.4) (2017-02-22)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v3.1.3...v3.1.4)
## [v3.1.3](https://github.com/petems/petems-swap_file/tree/v3.1.3) (2017-02-22)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v3.0.2...v3.1.3)
**Merged pull requests:**
- Fixes Travis [\#71](https://github.com/petems/petems-swap_file/pull/71) ([petems](https://github.com/petems))
- Make json file RFC 4627 valid [\#70](https://github.com/petems/petems-swap_file/pull/70) ([greglint](https://github.com/greglint))
- Change to use vagrant-libvirt over custom [\#69](https://github.com/petems/petems-swap_file/pull/69) ([petems](https://github.com/petems))
- Add testing for custom Vagrantfile [\#68](https://github.com/petems/petems-swap_file/pull/68) ([petems](https://github.com/petems))
- Add basic Jenkinsfile [\#67](https://github.com/petems/petems-swap_file/pull/67) ([petems](https://github.com/petems))
- Set seltype for swapfile [\#66](https://github.com/petems/petems-swap_file/pull/66) ([petems](https://github.com/petems))
## [v3.0.2](https://github.com/petems/petems-swap_file/tree/v3.0.2) (2016-08-07)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v3.0.1...v3.0.2)
**Closed issues:**
- Directory not empty @ dir\_s\_rmdir [\#63](https://github.com/petems/petems-swap_file/issues/63)
**Merged pull requests:**
- Fix error in documentation [\#64](https://github.com/petems/petems-swap_file/pull/64) ([petems](https://github.com/petems))
## [v3.0.1](https://github.com/petems/petems-swap_file/tree/v3.0.1) (2016-05-26)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v3.0.0...v3.0.1)
## [v3.0.0](https://github.com/petems/petems-swap_file/tree/v3.0.0) (2016-05-26)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.5.0...v3.0.0)
**Fixed bugs:**
- Updating the module to latest version will create additional fstab entries for the same swapfile [\#20](https://github.com/petems/petems-swap_file/issues/20)
**Merged pull requests:**
- Type and provider refactor [\#15](https://github.com/petems/petems-swap_file/pull/15) ([petems](https://github.com/petems))
## [v2.5.0](https://github.com/petems/petems-swap_file/tree/v2.5.0) (2016-05-24)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.4.1...v2.5.0)
**Merged pull requests:**
- Adds ability set swappiness with the module [\#62](https://github.com/petems/petems-swap_file/pull/62) ([petems](https://github.com/petems))
## [v2.4.1](https://github.com/petems/petems-swap_file/tree/v2.4.1) (2016-05-11)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.4.0...v2.4.1)
## [v2.4.0](https://github.com/petems/petems-swap_file/tree/v2.4.0) (2016-05-11)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.3.0...v2.4.0)
**Implemented enhancements:**
- Create workaround for stringify\_facts true [\#57](https://github.com/petems/petems-swap_file/issues/57)
**Fixed bugs:**
- Cannot change size of existing swap file [\#13](https://github.com/petems/petems-swap_file/issues/13)
**Merged pull requests:**
- Allows removing existing swap from a CSV fact [\#61](https://github.com/petems/petems-swap_file/pull/61) ([petems](https://github.com/petems))
- Add a swapfile fact as a CSV [\#60](https://github.com/petems/petems-swap_file/pull/60) ([petems](https://github.com/petems))
## [v2.3.0](https://github.com/petems/petems-swap_file/tree/v2.3.0) (2016-05-04)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.2.2...v2.3.0)
**Closed issues:**
- Update CHANGELOG with 2.2.2 changes [\#45](https://github.com/petems/petems-swap_file/issues/45)
**Merged pull requests:**
- Move coverage shim to spec\_helper [\#59](https://github.com/petems/petems-swap_file/pull/59) ([petems](https://github.com/petems))
- Update main class documentation [\#58](https://github.com/petems/petems-swap_file/pull/58) ([petems](https://github.com/petems))
- Add older listen gem for older Ruby versions [\#56](https://github.com/petems/petems-swap_file/pull/56) ([petems](https://github.com/petems))
- New feature: resizing existing swapfiles [\#55](https://github.com/petems/petems-swap_file/pull/55) ([petems](https://github.com/petems))
- Linting fixes in examples [\#54](https://github.com/petems/petems-swap_file/pull/54) ([petems](https://github.com/petems))
- Updates swap file fact to only show swap files [\#53](https://github.com/petems/petems-swap_file/pull/53) ([petems](https://github.com/petems))
- Make things a little less strict [\#52](https://github.com/petems/petems-swap_file/pull/52) ([petems](https://github.com/petems))
- Renaming sizes fact [\#51](https://github.com/petems/petems-swap_file/pull/51) ([petems](https://github.com/petems))
- Add contributing.json \(GitMagic\) [\#49](https://github.com/petems/petems-swap_file/pull/49) ([gitmagic-bot](https://github.com/gitmagic-bot))
- Update stdlib versions [\#48](https://github.com/petems/petems-swap_file/pull/48) ([petems](https://github.com/petems))
- Adding a fact to show you swap file sizes [\#47](https://github.com/petems/petems-swap_file/pull/47) ([petems](https://github.com/petems))
## [v2.2.2](https://github.com/petems/petems-swap_file/tree/v2.2.2) (2016-04-03)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.2.1...v2.2.2)
**Closed issues:**
- Created file size is incorrect [\#43](https://github.com/petems/petems-swap_file/issues/43)
**Merged pull requests:**
- Fixes MB size accuracy [\#44](https://github.com/petems/petems-swap_file/pull/44) ([petems](https://github.com/petems))
## [v2.2.1](https://github.com/petems/petems-swap_file/tree/v2.2.1) (2016-02-16)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.2.0...v2.2.1)
**Merged pull requests:**
- Move to petems-swap\_file [\#42](https://github.com/petems/petems-swap_file/pull/42) ([petems](https://github.com/petems))
- Make testing matrix a little simpler... [\#41](https://github.com/petems/petems-swap_file/pull/41) ([petems](https://github.com/petems))
## [v2.2.0](https://github.com/petems/petems-swap_file/tree/v2.2.0) (2016-02-15)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.1.0...v2.2.0)
**Closed issues:**
- Module should be tested on multiple Ruby and Puppet versions [\#38](https://github.com/petems/petems-swap_file/issues/38)
- Release version 2.1.0 on Puppet Forge [\#36](https://github.com/petems/petems-swap_file/issues/36)
- dd vs fallocate [\#26](https://github.com/petems/petems-swap_file/issues/26)
**Merged pull requests:**
- Wrapper [\#40](https://github.com/petems/petems-swap_file/pull/40) ([Phil-Friderici](https://github.com/Phil-Friderici))
- Modernize Travis setup [\#39](https://github.com/petems/petems-swap_file/pull/39) ([Phil-Friderici](https://github.com/Phil-Friderici))
- Satisfy puppet-lint [\#37](https://github.com/petems/petems-swap_file/pull/37) ([Phil-Friderici](https://github.com/Phil-Friderici))
## [v2.1.0](https://github.com/petems/petems-swap_file/tree/v2.1.0) (2015-12-30)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v2.0.0...v2.1.0)
**Closed issues:**
- Missing 2.0.0 tag [\#24](https://github.com/petems/petems-swap_file/issues/24)
**Merged pull requests:**
- Adds `cmd` parameter. [\#35](https://github.com/petems/petems-swap_file/pull/35) ([petems](https://github.com/petems))
- Updating Beaker acceptance machines [\#34](https://github.com/petems/petems-swap_file/pull/34) ([petems](https://github.com/petems))
- Enable travis docker [\#32](https://github.com/petems/petems-swap_file/pull/32) ([petems](https://github.com/petems))
- Adds spec.opts file [\#31](https://github.com/petems/petems-swap_file/pull/31) ([petems](https://github.com/petems))
- Add cmd param [\#29](https://github.com/petems/petems-swap_file/pull/29) ([petems](https://github.com/petems))
- Added timeout parameter for exec when using dd [\#27](https://github.com/petems/petems-swap_file/pull/27) ([petems](https://github.com/petems))
## [v2.0.0](https://github.com/petems/petems-swap_file/tree/v2.0.0) (2015-07-27)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v1.1.1...v2.0.0)
**Closed issues:**
- swap\_file::files fails when you set the ensure attribute to absent [\#21](https://github.com/petems/petems-swap_file/issues/21)
**Merged pull requests:**
- Remove Class for Swap file [\#23](https://github.com/petems/petems-swap_file/pull/23) ([petems](https://github.com/petems))
- Fix: exec contains swapfile name when absent [\#22](https://github.com/petems/petems-swap_file/pull/22) ([juame](https://github.com/juame))
- Update README.markdown [\#18](https://github.com/petems/petems-swap_file/pull/18) ([yalcinsurkultay](https://github.com/yalcinsurkultay))
## [v1.1.1](https://github.com/petems/petems-swap_file/tree/v1.1.1) (2015-03-17)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v1.1.0...v1.1.1)
**Closed issues:**
- mount resource should be unique [\#14](https://github.com/petems/petems-swap_file/issues/14)
**Merged pull requests:**
- Add defined type for swap and give unique names [\#16](https://github.com/petems/petems-swap_file/pull/16) ([petems](https://github.com/petems))
## [v1.1.0](https://github.com/petems/petems-swap_file/tree/v1.1.0) (2015-03-17)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v1.0.1...v1.1.0)
## [v1.0.1](https://github.com/petems/petems-swap_file/tree/v1.0.1) (2015-01-17)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v1.0.0...v1.0.1)
**Closed issues:**
- Not issue, ask a question [\#11](https://github.com/petems/petems-swap_file/issues/11)
- missed "default" in fstab [\#5](https://github.com/petems/petems-swap_file/issues/5)
- Docker Beaker tests always fail [\#4](https://github.com/petems/petems-swap_file/issues/4)
**Merged pull requests:**
- Fix License code [\#12](https://github.com/petems/petems-swap_file/pull/12) ([petems](https://github.com/petems))
- Add FreeBSD tests [\#10](https://github.com/petems/petems-swap_file/pull/10) ([petems](https://github.com/petems))
- Swap fstab settings [\#8](https://github.com/petems/petems-swap_file/pull/8) ([petems](https://github.com/petems))
- Fixes to swapfile permissions and to implied OS support [\#7](https://github.com/petems/petems-swap_file/pull/7) ([mattock](https://github.com/mattock))
## [v1.0.0](https://github.com/petems/petems-swap_file/tree/v1.0.0) (2014-09-24)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v0.3.0...v1.0.0)
## [v0.3.0](https://github.com/petems/petems-swap_file/tree/v0.3.0) (2014-09-01)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v0.2.0...v0.3.0)
## [v0.2.0](https://github.com/petems/petems-swap_file/tree/v0.2.0) (2014-09-01)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/0.3.0...v0.2.0)
## [0.3.0](https://github.com/petems/petems-swap_file/tree/0.3.0) (2014-09-01)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/0.2.0...0.3.0)
## [0.2.0](https://github.com/petems/petems-swap_file/tree/0.2.0) (2014-08-22)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v0.1.2...0.2.0)
## [v0.1.2](https://github.com/petems/petems-swap_file/tree/v0.1.2) (2014-05-29)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v0.1.1...v0.1.2)
## [v0.1.1](https://github.com/petems/petems-swap_file/tree/v0.1.1) (2014-05-29)
[Full Changelog](https://github.com/petems/petems-swap_file/compare/v0.1.0...v0.1.1)
## [v0.1.0](https://github.com/petems/petems-swap_file/tree/v0.1.0) (2014-02-27)
**Merged pull requests:**
- Removing custom fact for memory size in bytes [\#1](https://github.com/petems/petems-swap_file/pull/1) ([petems](https://github.com/petems))
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@@ -0,0 +1,83 @@
This module has grown over time based on a range of contributions from
people using it. If you follow these contributing guidelines your patch
will likely make it into a release a little quicker.
## Contributing
1. Fork the repo.
2. Run the tests. We only take pull requests with passing tests, and
it's great to know that you have a clean slate
3. Add a test for your change. Only refactoring and documentation
changes require no new tests. If you are adding functionality
or fixing a bug, please add a test.
4. Make the test pass.
5. Push to your fork and submit a pull request.
## Dependencies
The testing and development tools have a bunch of dependencies,
all managed by [bundler](http://bundler.io/) according to the
[Puppet support matrix](http://docs.puppetlabs.com/guides/platforms.html#ruby-versions).
By default the tests use a baseline version of Puppet.
If you have Ruby 2.x or want a specific version of Puppet,
you must set an environment variable such as:
export PUPPET_VERSION="~> 3.2.0"
Install the dependencies like so...
bundle install
## Syntax and style
The test suite will run [Puppet Lint](http://puppet-lint.com/) and
[Puppet Syntax](https://github.com/gds-operations/puppet-syntax) to
check various syntax and style things. You can run these locally with:
bundle exec rake lint
bundle exec rake syntax
## Running the unit tests
The unit test suite covers most of the code, as mentioned above please
add tests if you're adding new functionality. If you've not used
[rspec-puppet](http://rspec-puppet.com/) before then feel free to ask
about how best to test your new feature. Running the test suite is done
with:
bundle exec rake spec
Note also you can run the syntax, style and unit tests in one go with:
bundle exec rake test
## Integration tests
The unit tests just check the code runs, not that it does exactly what
we want on a real machine. For that we're using
[beaker](https://github.com/puppetlabs/beaker).
This fires up a new virtual machine (using vagrant) and runs a series of
simple tests against it after applying the module. You can run this
with:
bundle exec rake acceptance
This will run the tests on an Ubuntu 12.04 virtual machine. You can also
run the integration tests against Centos 6.5 with.
RS_SET=centos-64-x64 bundle exec rake acceptances
If you don't want to have to recreate the virtual machine every time you
can use `RS_DESTROY=no` and `RS_PROVISION=no`. On the first run you will
at least need `RS_PROVISION` set to yes (the default). The Vagrantfile
for the created virtual machines will be in `.vagrant/beaker_vagrant_fies`.

View File

@@ -0,0 +1,2 @@
Peter Souter (@petems)
Matt Dainty (@bodgit)

View File

@@ -0,0 +1,48 @@
source 'http://rubygems.org'
group :test do
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', ENV['PUPPET_VERSION'] || '~> 3.8.0'
end
# rspec must be v2 for ruby 1.8.7
if RUBY_VERSION >= '1.8.7' and RUBY_VERSION < '1.9'
gem 'rspec', '~> 2.0'
end
gem 'json_pure', '<= 2.0.1', :require => false if RUBY_VERSION < '2.0.0'
gem 'safe_yaml', '~> 1.0.4'
gem 'rake'
gem 'puppet-lint'
gem 'rspec-puppet', :git => 'https://github.com/rodjek/rspec-puppet.git'
gem 'puppet-syntax'
gem 'puppetlabs_spec_helper'
gem 'simplecov'
gem 'simplecov-console'
gem 'metadata-json-lint'
end
group :development do
gem 'puppet-blacksmith'
gem 'rubocop' if RUBY_VERSION >= '2.0.0'
gem 'rubocop-rspec', '~> 1.6' if RUBY_VERSION >= '2.3.0'
gem 'github_changelog_generator'
gem 'activesupport', '< 5'
end
group :system_tests do
gem "beaker",
:git => 'https://github.com/puppetlabs/beaker',
:ref => '3d21e843434a2e65152bd352c653511ddea0ce71',
:require => false
gem "beaker-rspec",
:git => 'https://github.com/puppetlabs/beaker-rspec.git',
:ref => 'a617f7bbc3e6ebb6ce49df32749d4ce93cef737d',
:require => false
gem 'serverspec'
gem 'specinfra'
end

View File

@@ -0,0 +1,5 @@
notification :off
guard 'rake', :task => 'test' do
watch(%r{^manifests\/(.+)\.pp$})
end

View File

@@ -0,0 +1,24 @@
node { // The "node" directive tells Jenkins to run commands on the same slave.
checkout scm
stage 'Bundle install'
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'gnome-terminal']) {
sh 'bundle install'
}
stage 'Acceptance Testing'
env.PUPPET_INSTALL_VERSION = "1.5.2"
env.PUPPET_INSTALL_TYPE = "agent"
env.BEAKER_set = "centos-7-x64-vagrant_libvirt"
print "Beaker Settings will be: ${env.PUPPET_INSTALL_VERSION} ${env.PUPPET_INSTALL_TYPE} ${env.BEAKER_set}"
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'gnome-terminal']) {
sh 'bundle exec rake acceptance'
}
}

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,179 @@
# swap_file
[![Puppet
Forge](http://img.shields.io/puppetforge/v/petems/swap_file.svg)](https://forge.puppetlabs.com/petems/swap_file) [![Build Status](https://travis-ci.org/petems/petems-swap_file.svg?branch=master)](https://travis-ci.org/petems/petems-swap_file) [![Puppet Forge
Downloads](http://img.shields.io/puppetforge/dt/petems/swap_file.svg)](https://forge.puppetlabs.com/petems/swap_file) [![Puppet Forge
Endorsement](https://img.shields.io/puppetforge/e/petems/swap_file.svg)](https://forge.puppetlabs.com/petems/swap_file)
#### Table of Contents
1. [Overview](#overview)
2. [Module Description](#module-description)
3. [Setup](#setup)
* [What swap_file affects](#what-swap_file-affects)
4. [Usage](#usage)
5. [Limitations](#limitations)
6. [Upgrading from 1.0.1 Release](#upgrading-from-101-release)
7. [Development](#development)
## Overview
Manage [swap files](http://en.wikipedia.org/wiki/Paging) for your Linux environments. This is based on the gist by @Yggdrasil, with a few changes and added specs.
## Setup
### What swap_file affects
* Creating a swap-file on disk. This uses `dd` by default, but can use `fallocate` optionally for performance reasons. **Note: Using fallocate to create a ZFS file system will fail: https://bugzilla.redhat.com/show_bug.cgi?id=1129205**
* Swapfiles on the system
* Any mounts of swapfiles
## Usage
The simplest use of the module is this:
```puppet
swap_file::files { 'default':
ensure => present,
}
```
By default, the module it will:
* create a file using /bin/dd at `/mnt/swap.1` with the default size taken from the `$::memorysize` fact in megabytes (eg. 8GB RAM will create an 8GB swap file)
* A `mount` for the swapfile created
For a custom setup, you can do something like this:
```puppet
swap_file::files { 'tmp file swap':
ensure => present,
swapfile => '/tmp/swapfile',
add_mount => false,
}
```
To use `fallocate` for swap file creation instead of `dd`:
```puppet
swap_file::files { 'tmp file swap':
ensure => present,
swapfile => '/tmp/swapfile',
cmd => 'fallocate',
}
```
To remove a prexisting swap, you can use ensure absent:
```puppet
swap_file::files { 'tmp file swap':
ensure => absent,
}
```
To choose the size of the swap file instead of defaulting to memory size:
```
swap_file::files { '5GB Swap':
ensure => present,
swapfile => '/mnt/swap.5gb',
swapfilesize => '5GB',
}
```
### hiera
You can also use hiera to call this module and set the configuration.
The simplest use of the module with hiera is this:
```yaml
classes:
- swap_file
swap_file::files:
'default':
ensure: 'present'
```
This hiera setup will create a file using /bin/dd atr `/mnt/swap.1` with the default size taken from the `$::memorysize` fact and add a `mount` resource for it.
You can use all customizations mentioned above in hiera like this:
```yaml
classes:
- swap_file
swap_file::files:
'custom setup':
ensure: 'present'
swapfile: '/tmp/swapfile.custom'
add_mount: false
'use fallocate':
swapfile: '/tmp/swapfile.fallocate'
cmd: 'fallocate'
'remove swap file'
ensure: 'absent'
swapfile: '/tmp/swapfile.old'
```
This hiera config will respectively:
* create a file `/tmp/swapfile.custom` using /bin/dd with the default size taken from the `$::memorysize` fact without creating a `mount` for it.
* create a file `/tmp/swapfile.fallocate` using /usr/bin/fallocate with the default size taken from the `$::memorysize` fact and creating a `mount` for it.
* deactivates the swapfile `/tmp/swapfile.old`, deletes it and removes the `mount`.
Set `$files_hiera_merge` to `true` to merge all found instances of `swap_file::files` in Hiera. This is useful for specifying swap files at different levels of the hierachy and having them all included in the catalog.
##Upgrading from 1.0.1 Release
Previously you would create swapfiles with the `swap_file` class:
```puppet
class { 'swap_file':
ensure => 'present',
}
```
However, this had many problems, such as not being able to declare more than one swap_file because of duplicate class errors.
Since 2.x.x the swapfiles are created by a defined type instead. The `swap_file` class is now a wrapper and can handle multiple swap_files.
You can now use:
```puppet
class { 'swap_file':
files => {
'freetext resource name' => {
ensure => 'present',
},
},
}
```
You can also safely declare mutliple swap file definitions:
```puppet
class { 'swap_file':
files => {
'swapfile' => {
ensure => 'present',
},
'use fallocate' => {
swapfile => '/tmp/swapfile.fallocate',
cmd => 'fallocate',
},
'remove swap file' => {
ensure => 'absent',
swapfile => '/tmp/swapfile.old',
},
},
}
```
## Limitations
Primary support is for Debian and RedHat, but should work on all Linux flavours.
Right now there is no BSD support, but I'm planning on adding it in the future
## Development
Follow the CONTRIBUTING guidelines! :)

View File

@@ -0,0 +1,50 @@
require 'puppetlabs_spec_helper/rake_tasks'
require 'puppet-lint/tasks/puppet-lint'
require 'puppet-syntax/tasks/puppet-syntax'
# These two gems aren't always present, for instance
# on Travis with --without development
begin
require 'puppet_blacksmith/rake_tasks'
rescue LoadError
end
PuppetLint.configuration.fail_on_warnings
PuppetLint.configuration.send('relative')
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.send('disable_class_inherits_from_params_class')
PuppetLint.configuration.send('disable_class_parameter_defaults')
PuppetLint.configuration.send('disable_documentation')
PuppetLint.configuration.send('disable_single_quote_string_with_variables')
PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
exclude_paths = [
"pkg/**/*",
"vendor/**/*",
"spec/**/*",
]
PuppetLint.configuration.ignore_paths = exclude_paths
PuppetSyntax.exclude_paths = exclude_paths
desc "Run acceptance tests"
RSpec::Core::RakeTask.new(:acceptance) do |t|
t.pattern = 'spec/acceptance'
end
desc "Run syntax, lint, and spec tests."
task :test => [
:syntax,
:lint,
:spec,
]
begin
require 'github_changelog_generator/task'
GitHubChangelogGenerator::RakeTask.new :changelog do |config|
version = (Blacksmith::Modulefile.new).version
config.future_release = "v#{version}"
config.header = "# Change Log\n\nAll notable changes to this project will be documented in this file."
config.exclude_labels = %w{duplicate question invalid wontfix modulesync}
end
rescue LoadError
end

View File

@@ -0,0 +1,59 @@
{
"CHANGELOG.md": "af03a4842b5bf7236aa28c65f8b415bd",
"CONTRIBUTING.md": "ec539f7912da25760720eebf9756ae65",
"CONTRIBUTORS": "805eaeecd42f1a06aa564dabc34e8363",
"Gemfile": "6f1069b614dee17b2f6e071a7696fedd",
"Guardfile": "3ae8d9a61b870350cc1c8e0d6a9a36e7",
"Jenkinsfile": "c8514e059beb9914b8797234d3b48f8d",
"LICENSE": "47bbee59c4c1cb14cc3667035a227be0",
"README.markdown": "139ac4a23749f12812ab9e65a489b9d3",
"Rakefile": "cffd8bfb00c0dd7bd704ff87d91febbe",
"contributing.json": "8c4b29954f117223992afe35c1cf6f30",
"examples/default_swap.pp": "670840e180b371de36892f7acca89b99",
"examples/hiera/common.yaml": "9be8ee98915351bc319911ccacb071ae",
"examples/hiera/fqdn/merge_disabled.domain.local.yaml": "c22b93ec67481fb46b8752944aaaa735",
"examples/hiera/fqdn/merge_enabled.domain.local.yaml": "f610049aeae43645c2fdc8aed7717c4a",
"examples/hiera/hiera.yaml": "3b4c8c00535fb4607ea205fc074988ae",
"examples/multiple_swaps.pp": "3b3dd90066b87c6a72d4e9f2669f4eac",
"lib/facter/swapfile_sizes.rb": "8e6d7a67c0c9efc9a5d2883f439abd80",
"lib/facter/swapfile_sizes_csv.rb": "94ada67fdfc1bc5f48a532735b5e74e4",
"lib/puppet/parser/functions/difference_within_margin.rb": "13b0312e33d84bdc604c4e32c3f5d5a1",
"lib/puppet/parser/functions/swap_file_size_from_csv.rb": "363763881ef81a6eac12308d3397e918",
"lib/puppet/provider/swap_file/linux.rb": "f7a238184e72da1d0f95feee21c11252",
"lib/puppet/type/swap_file.rb": "6bc034c8a3cf6770c0b8f8a45d201a2b",
"manifests/files.pp": "6e8f8904c08fcdab0e3d8b845fb52444",
"manifests/init.pp": "b5c294ce0df3a3aa96a47d734e654572",
"manifests/resize.pp": "af12fac873d4ca9220b238a7a377e67e",
"manifests/swappiness.pp": "4efe89c04267fed6bb055b83c7e15605",
"metadata.json": "b7abb8215638788d83a74dd8a8368355",
"spec/acceptance/nodesets/Vagrantfile-vagrant_custom": "099c1dccc5b5ea7a1cc37e967e49c4d7",
"spec/acceptance/nodesets/centos-6-x64.yml": "c9d4c88230670fd2f26bfec522883574",
"spec/acceptance/nodesets/centos-7-x64-vagrant_libvirt.yml": "35389790c6d18c60370b907cee3ef608",
"spec/acceptance/nodesets/centos-7-x64.yml": "31c23abc0148831753e5c2a847ccdd11",
"spec/acceptance/nodesets/default.yml": "b9deaf5e0afc5216331da177f273a0a9",
"spec/acceptance/nodesets/ubuntu-server-12042-x64.yml": "76d8ebcb03ddd64fa7a45909cdee6240",
"spec/acceptance/swap_file_class_spec.rb": "e61e478cb58f1efa87d52edc815c961f",
"spec/acceptance/swap_file_files_fallocate_command.rb": "32e93f89f7eb6e782af4cf3c24f28fba",
"spec/acceptance/swap_file_files_multiple_spec.rb": "b63a30139ede9041dc66f48110faaf38",
"spec/acceptance/swap_file_files_parameters_spec.rb": "802f4b14d8a5bcdbb4ec7606531f7f69",
"spec/acceptance/swap_file_files_spec.rb": "e8b7145c3efff1174b8d3298449fdc87",
"spec/acceptance/swap_file_resizing_spec.rb": "54b43fb9c4f32fcbf1f462ce4f713cf9",
"spec/acceptance/swap_file_resizing_stringify_true_spec.rb": "00a753f91f2ac978a973f7eefed8daf5",
"spec/acceptance/swap_file_swappiness_spec.rb": "f11e3fe2975a02db6656142d513fd29e",
"spec/classes/init_spec.rb": "293d54f4bfa37917e66050816dd9a02e",
"spec/classes/swappiness_spec.rb": "b108f36149300bcf060014ea5e29c10f",
"spec/defines/files_spec.rb": "db3ddc3ff587054bae16efd9951aa33f",
"spec/defines/resize_spec.rb": "ef7b30ea867d0957847f76cf2b576f88",
"spec/fixtures/hiera/fqdn/files.yaml": "c09830704075f3cfe71d7baf2b2f0b9d",
"spec/fixtures/hiera/hiera.yaml": "beeeeaf9ff2a7848f66cadedb749b184",
"spec/fixtures/hiera/parameter_tests/files_hiera_merge.yaml": "cf0173f27f17b20123b9bd514d4549dc",
"spec/functions/difference_within_margin_spec.rb": "4c52adfaee7c34b4c554d4a498c375a1",
"spec/functions/swap_file_size_from_csv_array_spec.rb": "44e66b9ddc93a01040c3dd459987d2d9",
"spec/spec.opts": "841ff248f09ff26ea499e7621d3fef33",
"spec/spec_helper.rb": "9617cb9aa81f0152d7a1cd8820c9b9a9",
"spec/spec_helper_acceptance.rb": "e0ea4032fe2842e584b5b073eb438aa3",
"spec/unit/facter/swapfiles_fact_csv_spec.rb": "d3e855880eb87dffd40013131ab1529e",
"spec/unit/facter/swapfiles_fact_spec.rb": "5cfa5e555c58c23070f533f4a56d8933",
"spec/unit/puppet/provider/swap_file/linux_spec.rb": "c95f5ca88149a398babaa4f50029fca6",
"spec/unit/puppet/type/swap_file/swap_file_spec.rb": "f7696323957041fed2886eb9b24cf255"
}

View File

@@ -0,0 +1,32 @@
{
"commit": {
"subject_cannot_be_empty": true,
"subject_must_be_longer_than": 4,
"subject_must_be_shorter_than": 101,
"subject_lines_must_be_shorter_than": 51,
"subject_must_be_single_line": true,
"subject_must_be_in_tense": "imperative",
"subject_must_start_with_case": "upper",
"subject_must_not_end_with_dot": true,
"body_lines_must_be_shorter_than": 73
},
"pull_request": {
"subject_cannot_be_empty": true,
"subject_must_be_longer_than": 4,
"subject_must_be_shorter_than": 101,
"subject_must_be_in_tense": "imperative",
"subject_must_start_with_case": "upper",
"subject_must_not_end_with_dot": true,
"body_cannot_be_empty": true
},
"issue": {
"subject_cannot_be_empty": true,
"subject_must_be_longer_than": 4,
"subject_must_be_shorter_than": 101,
"subject_must_be_in_tense": "imperative",
"subject_must_start_with_case": "upper",
"subject_must_not_end_with_dot": true,
"body_cannot_be_empty": true,
"body_must_include_reproduction_steps": true
}
}

View File

@@ -0,0 +1,5 @@
node default {
swap_file::files { 'default':
ensure => present,
}
}

View File

@@ -0,0 +1,12 @@
---
classes:
- swap_file
swap_file::files:
'from_common':
ensure: 'present'
swapfile: '/mnt/swap.common'
# This will:
# - call the class swap_file
# - create a file '/mnt/swap.common' using /bin/dd with the default size taken from the $::memorysizeinbytes and create a mount for it.

View File

@@ -0,0 +1,13 @@
---
swap_file::files_hiera_merge: false
swap_file::files:
'from_fqdn':
ensure: 'present'
swapfile: '/mnt/swap.fqdn'
swapfilesize: '2 GB'
cmd: 'fallocate'
# Because files_hiera_merge is set to false, this will create only the swapfiles specified in the most specific hiera level.
# This will:
# - create a file '/mnt/swap.fqdn' using /usr/bin/fallocate with size set to '2 GB' and creates mount for it.

View File

@@ -0,0 +1,16 @@
---
swap_file::files_hiera_merge: true
swap_file::files:
'from_fqdn':
ensure: 'present'
swapfile: '/mnt/swap.fqdn'
swapfilesize: '2 GB'
cmd: 'fallocate'
# Because files_hiera_merge is set to true, this will create all swapfiles specified in different hiera levels.
# This will:
# - create a file '/mnt/swap.common' using /bin/dd with the default size taken from the $::memorysizeinbytes and create a mount for it.
# - create a file '/mnt/swap.fqdn' using /usr/bin/fallocate with size set to '2 GB' and creates mount for it.

View File

@@ -0,0 +1,6 @@
---
:backends:
- yaml
:hierarchy:
- fqdn/%{fqdn}
- common

View File

@@ -0,0 +1,17 @@
node default {
class { '::swap_file':
files => {
'swapfile' => {
ensure => 'present', # lint:ignore:ensure_first_param
},
'use fallocate' => {
swapfile => '/tmp/swapfile.fallocate',
cmd => 'fallocate',
},
'remove swap file' => {
ensure => 'absent', # lint:ignore:ensure_first_param
swapfile => '/tmp/swapfile.old',
},
},
}
}

View File

@@ -0,0 +1,34 @@
if File.exists?('/proc/swaps')
swap_file_hash = {}
swap_file_output = Facter::Util::Resolution.exec('cat /proc/swaps')
# Sample Output
# Filename Type Size Used Priority
# /dev/dm-1 partition 524284 0 -1
# /mnt/swap.1 file 204796 0 -2
# /tmp/swapfile.fallocate file 204796 0 -3
swap_file_output_array = swap_file_output.split("\n")
# Remove the header line
swap_file_output_array.shift
swap_file_output_array.each do |line|
swap_file_line_array = line.gsub(/\s+/m, ' ').strip.split(" ")
# We only want swap-file information, not paritions
if swap_file_line_array[1] == 'file'
swap_file_hash[swap_file_line_array[0]] = swap_file_line_array[2]
end
end
Facter.add('swapfile_sizes') do
confine :kernel => 'Linux'
setcode do
swap_file_hash
end
end
end

View File

@@ -0,0 +1,37 @@
if File.exists?('/proc/swaps')
swap_file_array = []
swap_file_output = Facter::Util::Resolution.exec('cat /proc/swaps')
# Sample Output
# Filename Type Size Used Priority
# /dev/dm-1 partition 524284 0 -1
# /mnt/swap.1 file 204796 0 -2
# /tmp/swapfile.fallocate file 204796 0 -3
swap_file_output_array = swap_file_output.split("\n")
# Remove the header line
swap_file_output_array.shift
swap_file_output_array.each do |line|
swap_file_line_array = line.gsub(/\s+/m, ' ').strip.split(" ")
# We only want swap-file information, not paritions
if swap_file_line_array[1] == 'file'
pipe_seperated_string = "#{swap_file_line_array[0]}||#{swap_file_line_array[2]}"
swap_file_array << pipe_seperated_string
end
end
swapfile_csv = swap_file_array.join(',') unless swap_file_array.empty?
Facter.add('swapfile_sizes_csv') do
confine :kernel => 'Linux'
setcode do
swapfile_csv
end
end
end

View File

@@ -0,0 +1,62 @@
# When given an array of two numbers and a margin, returns true
# if the difference is less than the margin. Basically statistical
# range with a margin.
#
# @example Difference between 150 and 100 is 50, margin is 60. So true
# $within_margin = difference_within_margin([100,150],60)
#
# @example Difference between 150 and 100 is 50, margin is 40. So false
# $within_margin = difference_within_margin([100,150],40)
#
# @return [Boolean] whether the difference between two numbers is within in a margin
#
# @param num_a [Array] array of two numbers to compare
# @param num_b [Float] the margin to compare the two numbers
module Puppet::Parser::Functions
newfunction(:difference_within_margin, :type => :rvalue, :doc => <<-EOS
Get's the difference between two numbers, with a third argument as a margin
*Example:*
compare_with_margin(100,150,60)
Would result in:
true
compare_with_margin(100,150,40)
Would result in:
false
EOS
) do |arguments|
# Check that more than 2 arguments have been given ...
raise(Puppet::ParseError, "compare_with_margin(): Wrong number of arguments " +
"given (#{arguments.size} for 2)") unless arguments.size == 2
# Check that the first parameter is an array
unless arguments[0].is_a?(Array)
raise(Puppet::ParseError, 'difference_within_margin(): Requires array to work with')
end
# Check that the first parameter is an array
if arguments[0].empty?
raise(Puppet::ParseError, 'difference_within_margin(): arg[0] array cannot be empty')
end
arguments[0].collect! { |i| i.to_f }
difference = arguments[0].minmax[1].to_f - arguments[0].minmax[0].to_f
if difference < arguments[1].to_f
return true
else
return false
end
end
end
# vim: set ts=2 sw=2 et :

View File

@@ -0,0 +1,36 @@
#
# swap_file_size_from_csv.rb
#
module Puppet::Parser::Functions
newfunction(:swap_file_size_from_csv, :type => :rvalue, :doc => <<-EOS
Given a csv of swap files and sizes, split by pipe (||), we can determine the size in bytes of the swapfile
Will return false if the swapfile is not found in the csv
*Examples:*
get_swap_file_size_from_csv('/mnt/swap.1','/mnt/swap.1||1019900,/mnt/swap.1||1019900')
Would return: 1019900
get_swap_file_size_from_csv('/mnt/swap.2','/mnt/swap.1||1019900,/mnt/swap.1||1019900')
Would return: false
EOS
) do |arguments|
raise(Puppet::ParseError, "swap_file_size_from_csv(): Wrong number of arguments " +
"given (#{arguments.size} for 2)") if arguments.size < 2
unless arguments[0].is_a? String
raise(Puppet::ParseError, "swap_file_size_from_csv(): swapfile name but be a string (Got #{arguments[0].class}")
end
unless arguments[1].is_a? String
raise(Puppet::ParseError, "swap_file_size_from_csv(): Requires string to work with (Got #{arguments[1].class}")
end
lines = arguments[1].strip.split(',')
swapfile_found = false
lines.each do | swapfile_csv |
swapfile_csv_array = swapfile_csv.split(',')
swapfile_name = swapfile_csv.split('||')[0]
swapfile_size = swapfile_csv.split('||')[1]
swapfile_found = swapfile_size if arguments[0] == swapfile_name
end
swapfile_found
end
end
# vim: set ts=2 sw=2 et :

View File

@@ -0,0 +1,113 @@
Puppet::Type.type(:swap_file).provide(:linux) do
desc "Swap file management via `swapon`, `swapoff` and `mkswap`"
confine :kernel => :linux
commands :swapon => 'swapon'
commands :swapoff => 'swapoff'
commands :mkswap => 'mkswap'
mk_resource_methods
def initialize(value={})
super(value)
@property_flush = {}
end
def self.get_swap_files
swapfiles = swapon(['-s']).split("\n")
swapfiles.shift
swapfiles.sort
end
def self.prefetch(resources)
instances.each do |prov|
if resource = resources[prov.name]
resource.provider = prov
end
end
end
def self.instances
get_swap_files.collect do |swapfile_line|
new(get_swapfile_properties(swapfile_line))
end
end
def self.get_swapfile_properties(swapfile_line)
swapfile_properties = {}
# swapon -s output formats thus:
# Filename Type Size Used Priority
# Split on spaces
output_array = swapfile_line.strip.split(/\s+/)
# Assign properties based on headers
swapfile_properties = {
:ensure => :present,
:name => output_array[0],
:file => output_array[0],
:type => output_array[1],
:size => output_array[2],
:used => output_array[3],
:priority => output_array[4]
}
swapfile_properties[:provider] = :swap_file
Puppet.debug "Swapfile: #{swapfile_properties.inspect}"
swapfile_properties
end
def exists?
@property_hash[:ensure] == :present
end
def create
@property_flush[:ensure] = :present
end
def destroy
@property_flush[:ensure] = :absent
end
def create_swap_file(file_path)
mk_swap(file_path)
swap_on(file_path)
end
def mk_swap(file_path)
Puppet.debug "Running `mkswap #{file_path}`"
output = mkswap([file_path])
Puppet.debug "Returned value: #{output}`"
end
def swap_on(file_path)
Puppet.debug "Running `swapon #{file_path}`"
output = swapon([file_path])
Puppet.debug "Returned value: #{output}"
end
def swap_off(file_path)
Puppet.debug "Running `swapoff #{file_path}`"
output = swapoff([file_path])
Puppet.debug "Returned value: #{output}"
end
def set_swapfile
if @property_flush[:ensure] == :absent
swap_off(resource[:name])
return
end
create_swap_file(resource[:name]) unless exists?
end
def flush
set_swapfile
# Collect the resources again once they've been changed (that way `puppet
# resource` will show the correct values after changes have been made).
@property_hash = self.class.get_swapfile_properties(resource[:name])
end
end

View File

@@ -0,0 +1,40 @@
Puppet::Type.newtype(:swap_file) do
ensurable
desc <<-DOC
Used to configure swap files
=== Examples
swap_file { '/mnt/swap.1':
ensure => 'present',
size => '1068028',
}
DOC
@doc = 'Type representing swap files.'
newparam(:file, :namevar => true) do
desc "The file path of the swapfile."
validate do |value|
fail "file parameter must be a valid absolute path" unless Puppet::Util.absolute_path?(value)
end
end
newproperty(:type) do
desc "The type of the swapfile"
end
newproperty(:size) do
desc "The size of the swapfile in bytes"
end
newproperty(:used) do
desc "The amount of space used"
end
newproperty(:priority) do
desc "The priority of the swapfile"
end
end

View File

@@ -0,0 +1,143 @@
# Define: swap_file::files
#
# This is a defined type to create a swap_file
#
# == Parameters
# [*ensure*]
# Allows creation or removal of swapspace and the corresponding file.
# [*swapfile*]
# Location of swapfile, defaults to /mnt
# [*swapfilesize*]
# Size of the swapfile as a string (eg. 10 MB, 1.2 GB).
# Defaults to $::memorysize fact on the node
# [*add_mount*]
# Add a mount to the swapfile so it persists on boot
# [*options*]
# Mount options for the swapfile
# [*timeout*]
# dd command exec timeout.
# Defaults to 300
# [*cmd*]
# What command is used to create the file, dd or fallocate. dd is better tested and safer but fallocate is significantly faster.
# Defaults to dd
#
# == Examples
#
# swap_file::files { 'default':
# ensure => present,
# swapfile => '/mnt/swap.55',
# }
#
# == Authors
# @petems - Peter Souter
#
define swap_file::files (
$ensure = 'present',
$swapfile = '/mnt/swap.1',
$swapfilesize = $::memorysize,
$add_mount = true,
$options = 'defaults',
$timeout = 300,
$cmd = 'dd',
$resize_existing = false,
$resize_margin = '50MB',
$resize_verbose = false,
)
{
# Parameter validation
validate_legacy(String, 'validate_re', $ensure, ['^absent$', '^present$'])
validate_legacy(String, 'validate_string', $swapfile)
$swapfilesize_mb = to_bytes($swapfilesize) / 1048576
validate_legacy(Boolean, 'validate_bool', $add_mount)
if $ensure == 'present' {
if ($resize_existing and $::swapfile_sizes) {
if (is_hash($::swapfile_sizes)) {
if (has_key($::swapfile_sizes,$swapfile)) {
::swap_file::resize { $swapfile:
swapfile_path => $swapfile,
margin => $resize_margin,
expected_swapfile_size => $swapfilesize,
actual_swapfile_size => $::swapfile_sizes[$swapfile],
verbose => $resize_verbose,
before => Exec["Create swap file ${swapfile}"],
}
}
} else {
$existing_swapfile_size = swap_file_size_from_csv($swapfile,$::swapfile_sizes_csv)
if ($existing_swapfile_size) {
::swap_file::resize { $swapfile:
swapfile_path => $swapfile,
margin => $resize_margin,
expected_swapfile_size => $swapfilesize,
actual_swapfile_size => $existing_swapfile_size,
verbose => $resize_verbose,
before => Exec["Create swap file ${swapfile}"],
}
}
}
}
exec { "Create swap file ${swapfile}":
creates => $swapfile,
timeout => $timeout,
}
case $cmd {
'dd': {
Exec["Create swap file ${swapfile}"] { command => "/bin/dd if=/dev/zero of=${swapfile} bs=1M count=${swapfilesize_mb}" }
}
'fallocate': {
Exec["Create swap file ${swapfile}"] { command => "/usr/bin/fallocate -l ${swapfilesize_mb}M ${swapfile}" }
}
default: {
fail("Invalid cmd: ${cmd} - (Must be 'dd' or 'fallocate')")
}
}
file { $swapfile:
owner => root,
group => root,
mode => '0600',
require => Exec["Create swap file ${swapfile}"],
}
if $::selinux {
File[$swapfile] {
seltype => 'swapfile_t',
}
}
swap_file { $swapfile:
ensure => 'present',
require => File[$swapfile],
}
if $add_mount {
mount { $swapfile:
ensure => present,
fstype => swap,
device => $swapfile,
options => $options,
dump => 0,
pass => 0,
require => Swap_file[$swapfile],
}
}
}
elsif $ensure == 'absent' {
swap_file { $swapfile:
ensure => 'absent',
}
file { $swapfile:
ensure => absent,
backup => false,
require => Swap_file[$swapfile],
}
mount { $swapfile:
ensure => absent,
device => $swapfile,
}
}
}

View File

@@ -0,0 +1,65 @@
# Main class to allow passing required swapfiles as hashes
#
# @example Will create one swapfile in /mnt/swap using the defaults.
# class { '::swap_file':
# 'files' => {
# 'resource_name' => {
# ensure => present,
# swapfile => '/mnt/swap',
# },
# },
# }
#
# @example Will create two swapfile with the given parameters
# class { 'swap_file':
# 'files' => {
# 'swap1' => {
# ensure => present,
# swapfile => '/mnt/swap.1',
# swapfilesize => '1 GB',
# },
# 'swap2' => {
# ensure => present,
# swapfile => '/mnt/swap.2',
# swapfilesize => '2 GB',
# cmd => 'fallocate',
# },
# },
# }
#
# @example Will merge all found instances of swap_file::files found in hiera and create resources for these.
# class { '::swap_file':
# files_hiera_merge: true,
# }
#
# @param [Hash] files Hash of swap files to ensure with swap_file::files
# @param [Boolean] files_hiera_merge Boolean to merge all found instances of swap_file::files in Hiera.
# This can be used to specify swap files at different levels an have
# them all included in the catalog.
#
# @author - Peter Souter
#
class swap_file (
$files = {},
$files_hiera_merge = false,
) {
# variable handling
if $files_hiera_merge =~ Boolean {
$files_hiera_merge_bool = $files_hiera_merge
} else {
$files_hiera_merge_bool = str2bool($files_hiera_merge)
}
validate_legacy(Boolean, 'validate_bool', $files_hiera_merge_bool)
# functionality
if $files_hiera_merge_bool == true {
$files_real = hiera_hash('swap_file::files', {})
} else {
$files_real = $files
}
if $files_real != undef {
validate_legacy(Hash, 'validate_hash', $files_real)
create_resources('swap_file::files', $files_real)
}
}

View File

@@ -0,0 +1,49 @@
# A defined type to resize an existing swapfile
#
# @example
# ::swap_file::resize { '/mnt/swap.1:
# swapfile_path => '/mnt/swap.1',
# margin => '500 MB',
# expected_swapfile_size => '1 GB,
# }
#
# @param [String] swapfile_path Path to the swapfile
# @param [String] expected_swapfile_size Expected size of the swapfile
# @param [String] actual_swapfile_size Actual size of the swapfile
# @param [String] margin Margin that is checked before resizing the swapfile
# @param [Boolean] verbose Adds a notify to explain why the change was made
#
# @author - Peter Souter
#
define swap_file::resize (
$swapfile_path,
$expected_swapfile_size,
$actual_swapfile_size,
$margin = '50MB',
$verbose = false,
)
{
$margin_bytes = to_bytes($margin)
$existing_swapfile_bytes = to_bytes("${actual_swapfile_size}kb")
$expected_swapfile_size_bytes = to_bytes($expected_swapfile_size)
if !($expected_swapfile_size_bytes == $existing_swapfile_bytes) {
if !(difference_within_margin([$existing_swapfile_bytes, $expected_swapfile_size_bytes],$margin_bytes)) {
if ($verbose) {
$alert_message = "Existing : ${existing_swapfile_bytes}B\nExpected: ${expected_swapfile_size_bytes}B\nMargin: ${margin_bytes}B"
notify{"Resizing Swapfile Alert ${swapfile_path}":
name => $alert_message,
}
}
exec { "Detach swap file ${swapfile_path} for resize":
command => "/sbin/swapoff ${swapfile_path}",
onlyif => "/sbin/swapon -s | grep ${swapfile_path}",
} -> exec { "Purge ${swapfile_path} for resize":
command => "/bin/rm -f ${swapfile_path}",
onlyif => "test -f ${swapfile_path}",
path => [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/' ],
}
}
}
}

View File

@@ -0,0 +1,23 @@
# Allows setting the kernel swappiness setting
#
# @example Will set the sysctl setting for swappiness to 75
# class { '::swap_file::swappiness':
# swappiness => 75,
# }
#
# @param [String] swapiness Swapiness level, integer from 0 - 100 inclusive
#
# @author - Peter Souter
#
class swap_file::swappiness (
$swappiness = 60,
) {
validate_legacy(Integer, 'validate_integer', $swappiness, [100, 0])
sysctl { 'vm.swappiness':
ensure => 'present',
value => $swappiness,
}
}

View File

@@ -0,0 +1,55 @@
{
"name": "petems-swap_file",
"version": "4.0.2",
"author": "Peter Souter",
"summary": "Create swap files for Linux systems with Puppet",
"license": "Apache-2.0",
"source": "https://github.com/petems/petems-swap_file",
"project_page": "https://github.com/petems/petems-swap_file",
"issues_url": "https://github.com/petems/petems-swap_file/issues",
"operatingsystem_support": [
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "CentOS",
"operatingsystemrelease": [
"5",
"6",
"7"
]
},
{
"operatingsystem": "Debian",
"operatingsystemrelease": [
"6",
"7"
]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": [
"10.04",
"12.04",
"14.04"
]
}
],
"requirements": [
{
"name": "puppet",
"version_requirement": ">= 4.0.0"
}
],
"dependencies": [
{"name":"puppetlabs/stdlib","version_requirement":">= 4.23.0 < 6.0.0"},
{"name":"puppetlabs/mount_core","version_requirement":">= 1.0.0 < 2.0.0"},
{"name":"herculesteam/augeasproviders_sysctl","version_requirement":">=2.1.0 < 3.0.0"},
{"name":"herculesteam/augeasproviders_core","version_requirement":">=2.1.0 < 3.0.0"}
]
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<build xmlns="http://www.github/cliffe/SecGen/build"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/build">
<name>Swap File Module</name>
<author>Thomas Shaw</author>
<module_license>MIT</module_license>
<description>TODO: </description>
<type>swap</type>
<platform>linux</platform>
<read_fact>size</read_fact>
<default_input into="size">
<value>2 GB</value>
</default_input>
</build>

View File

@@ -0,0 +1,11 @@
Vagrant.configure("2") do |c|
c.ssh.insert_key = false
c.vm.define 'centos-7-x64' do |v|
v.vm.hostname = 'centos-7-x64'
v.vm.box = 'centos/7'
v.vm.box_check_update = 'true'
v.vm.provider :libvirt do |node|
node.memory = 512
end
end
end

View File

@@ -0,0 +1,11 @@
HOSTS:
centos-6-x64:
roles:
- master
platform: el-6-x86_64
box : puppetlabs/centos-6.6-64-nocm
box_url : https://vagrantcloud.com/puppetlabs/boxes/centos-6.6-64-nocm
hypervisor : vagrant
CONFIG:
log_level: debug
type: foss

View File

@@ -0,0 +1,11 @@
HOSTS:
centos-7-x64:
roles:
- default
platform: el-7-x86_64
box : centos/7
hypervisor : vagrant_libvirt
CONFIG:
type: foss
log_level: verbose
network_mac: '5a:65:a9:97:e4:e4'

View File

@@ -0,0 +1,11 @@
HOSTS:
centos-7-x64:
roles:
- master
platform: el-7-x86_64
box: puppetlabs/centos-7.0-64-nocm
box_url: https://vagrantcloud.com/puppetlabs/boxes/centos-7.0-64-nocm
hypervisor: vagrant
CONFIG:
log_level: verbose
type: foss

View File

@@ -0,0 +1,11 @@
HOSTS:
ubuntu-server-1204-x64:
roles:
- master
platform: ubuntu-12.04-amd64
box : puppetlabs/ubuntu-12.04-64-nocm
box_url : https://vagrantcloud.com/puppetlabs/ubuntu-12.04-64-nocm
hypervisor : vagrant
CONFIG:
log_level: debug
type: foss

View File

@@ -0,0 +1,11 @@
HOSTS:
ubuntu-server-1204-x64:
roles:
- master
platform: ubuntu-12.04-amd64
box : puppetlabs/ubuntu-12.04-64-nocm
box_url : https://vagrantcloud.com/puppetlabs/ubuntu-12.04-64-nocm
hypervisor : vagrant
CONFIG:
log_level: debug
type: foss

View File

@@ -0,0 +1,45 @@
require 'spec_helper_acceptance'
describe 'swap_file class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'swap_file' do
context 'ensure => present' do
it 'should work with no errors' do
pp = <<-EOS
class { 'swap_file':
files => {
'swapfile' => {
ensure => 'present',
},
'use fallocate' => {
swapfile => '/tmp/swapfile.fallocate',
cmd => 'fallocate',
},
'remove swap file' => {
ensure => 'absent',
swapfile => '/tmp/swapfile.old',
},
},
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the default swapfile' do
shell('/sbin/swapon -s | grep /mnt/swap.1', :acceptable_exit_codes => [0])
end
it 'should contain the default fstab setting' do
shell('cat /etc/fstab | grep /mnt/swap.1', :acceptable_exit_codes => [0])
shell('cat /etc/fstab | grep defaults', :acceptable_exit_codes => [0])
end
it 'should contain the default swapfile' do
shell('/sbin/swapon -s | grep /tmp/swapfile.fallocate', :acceptable_exit_codes => [0])
end
it 'should contain the default fstab setting' do
shell('cat /etc/fstab | grep /tmp/swapfile.fallocate', :acceptable_exit_codes => [0])
end
end
end
end

View File

@@ -0,0 +1,25 @@
require 'spec_helper_acceptance'
describe 'swap_file::files defined type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'fallocate command', :unless => ['FreeBSD'].include?(fact('osfamily')) do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'default':
ensure => present,
cmd => 'fallocate',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the default swapfile' do
shell('/sbin/swapon -s | grep /mnt/swap.1', :acceptable_exit_codes => [0])
end
it 'should contain the default fstab setting' do
shell('cat /etc/fstab | grep /mnt/swap.1', :acceptable_exit_codes => [0])
shell('cat /etc/fstab | grep defaults', :acceptable_exit_codes => [0])
end
end
end

View File

@@ -0,0 +1,31 @@
require 'spec_helper_acceptance'
describe 'swap_file::files defined type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'multiple swap_file::files', :unless => ['FreeBSD'].include?(fact('osfamily')) do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'tmp file swap 1':
ensure => present,
swapfile => '/tmp/swapfile1',
}
swap_file::files { 'tmp file swap 2':
ensure => present,
swapfile => '/tmp/swapfile2',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfile' do
shell('/sbin/swapon -s | grep /tmp/swapfile1', :acceptable_exit_codes => [0])
shell('/sbin/swapon -s | grep /tmp/swapfile2', :acceptable_exit_codes => [0])
end
it 'should contain the default fstab setting' do
shell('cat /etc/fstab | grep /tmp/swapfile1', :acceptable_exit_codes => [0])
shell('cat /etc/fstab | grep /tmp/swapfile2', :acceptable_exit_codes => [0])
end
end
end

View File

@@ -0,0 +1,35 @@
require 'spec_helper_acceptance'
describe 'swap_file::files defined type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'swap_file' do
context 'custom parameters' do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'tmp file swap':
ensure => present,
swapfile => '/tmp/swapfile',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfile' do
if ["FreeBSD"].include?(fact('osfamily'))
shell('/usr/sbin/swapinfo | grep /dev/md99', :acceptable_exit_codes => [0])
else
shell('/sbin/swapon -s | grep /tmp/swapfile', :acceptable_exit_codes => [0])
end
end
it 'should contain the given fstab setting' do
shell('cat /etc/fstab | grep /tmp/swapfile', :acceptable_exit_codes => [0])
if ["FreeBSD"].include?(fact('osfamily'))
shell('cat /etc/fstab | grep md99', :acceptable_exit_codes => [0])
else
shell('cat /etc/fstab | grep defaults', :acceptable_exit_codes => [0])
end
end
end
end
end

View File

@@ -0,0 +1,73 @@
require 'spec_helper_acceptance'
describe 'swap_file::files defined type', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'swap_file' do
context 'ensure => present' do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'default':
ensure => present,
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the default swapfile' do
shell('/sbin/swapon -s | grep /mnt/swap.1', :acceptable_exit_codes => [0])
end
it 'should contain the default fstab setting' do
shell('cat /etc/fstab | grep /mnt/swap.1', :acceptable_exit_codes => [0])
shell('cat /etc/fstab | grep defaults', :acceptable_exit_codes => [0])
end
end
context 'custom parameters' do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'tmp file swap':
ensure => present,
swapfile => '/tmp/swapfile',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfile' do
shell('/sbin/swapon -s | grep /tmp/swapfile', :acceptable_exit_codes => [0])
end
it 'should contain the default fstab setting' do
shell('cat /etc/fstab | grep /tmp/swapfile', :acceptable_exit_codes => [0])
shell('cat /etc/fstab | grep defaults', :acceptable_exit_codes => [0])
end
end
context 'multiple swap_file::files' do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'tmp file swap 1':
ensure => present,
swapfile => '/tmp/swapfile1',
}
swap_file::files { 'tmp file swap 2':
ensure => present,
swapfile => '/tmp/swapfile2',
}
EOS
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfiles' do
shell('/sbin/swapon -s | grep /tmp/swapfile1', :acceptable_exit_codes => [0])
shell('/sbin/swapon -s | grep /tmp/swapfile2', :acceptable_exit_codes => [0])
end
it 'should contain the default fstab setting' do
shell('cat /etc/fstab | grep /tmp/swapfile1', :acceptable_exit_codes => [0])
shell('cat /etc/fstab | grep /tmp/swapfile2', :acceptable_exit_codes => [0])
end
end
end
end

View File

@@ -0,0 +1,50 @@
require 'spec_helper_acceptance'
describe 'swap_file class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'disable stringify_facts' do
shell('puppet config set stringify_facts false --section=agent', { :acceptable_exit_codes => [0,1] })
shell('puppet config set stringify_facts false', { :acceptable_exit_codes => [0,1] })
end
context 'swap_file' do
context 'swapfilesize => 100' do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'default':
ensure => present,
swapfilesize => '100MB',
resize_existing => true,
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfile with the correct size (102396/100MB)' do
shell('/sbin/swapon -s | grep /mnt/swap.1', :acceptable_exit_codes => [0])
shell('/bin/cat /proc/swaps | grep 102396', :acceptable_exit_codes => [0])
end
end
context 'resize swap file' do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'default':
ensure => present,
swapfilesize => '200MB',
resize_existing => true,
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfile with the resized size (204796kb/200MB)' do
shell('/sbin/swapon -s | grep /mnt/swap.1', :acceptable_exit_codes => [0])
shell('/bin/cat /proc/swaps | grep 204796', :acceptable_exit_codes => [0])
end
end
end
end

View File

@@ -0,0 +1,51 @@
require 'spec_helper_acceptance'
describe 'swap_file class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'disable stringify_facts' do
shell('puppet config set stringify_facts true --section=agent', { :acceptable_exit_codes => [0,1] })
shell('puppet config set stringify_facts true', { :acceptable_exit_codes => [0,1] })
end
context 'swap_file' do
context 'swapfilesize => 100' do
it 'should work with no errors' do
pp = <<-EOS
swap_file::files { 'default':
ensure => present,
swapfilesize => '100MB',
resize_existing => true,
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfile with the correct size (102396/100MB)' do
shell('/sbin/swapon -s | grep /mnt/swap.1', :acceptable_exit_codes => [0])
shell('/bin/cat /proc/swaps | grep 102396', :acceptable_exit_codes => [0])
end
end
context 'resize swap file' do
it 'errors out if stringify_facts is true and resize_existing is true' do
pp = <<-EOS
swap_file::files { 'default':
ensure => present,
swapfilesize => '200MB',
resize_existing => true,
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should contain the given swapfile with the resized size (204796kb/200MB)' do
shell('/sbin/swapon -s | grep /mnt/swap.1', :acceptable_exit_codes => [0])
shell('/bin/cat /proc/swaps | grep 204796', :acceptable_exit_codes => [0])
end
end
end
end

View File

@@ -0,0 +1,24 @@
require 'spec_helper_acceptance'
describe 'swap_file::swappiness class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do
context 'swap_file::swappiness' do
context 'swappiness => 75, permanent => false' do
it 'should work with no errors' do
pp = <<-EOS
class { 'swap_file::swappiness':
swappiness => 75,
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
it 'should set the swappiness to 75 in a seperate sysctl file' do
shell('/bin/cat /proc/sys/vm/swappiness | grep 75', :acceptable_exit_codes => [0])
end
end
end
end

View File

@@ -0,0 +1,156 @@
require 'spec_helper'
describe 'swap_file' do
let(:facts) do
{
:memorysize => '1.00 GB',
selinux: true,
}
end
context 'with defaults for all parameters' do
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('swap_file') }
it { is_expected.to have_resource_count(0) }
end
context 'with files set to valid hash' do
let(:params) do
{
:files => {
'swap' => {
'ensure' => 'present',
},
'test' => {
'swapfile' => '/mnt/test',
},
}
}
end
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('swap_file') }
# subclass swap_file::files adds 4 resources for each given file
it { is_expected.to have_resource_count(10) }
it do
is_expected.to contain_swap_file__files('swap').with({
'ensure' => 'present',
})
end
it do
is_expected.to contain_swap_file__files('test').with({
'swapfile' => '/mnt/test',
})
end
end
describe 'with data for swap_file::files provided in multiple hiera levels' do
let(:facts) do
{
:fqdn => 'files',
:parameter_tests => 'files_hiera_merge',
:memorysize => '1.00 GB',
:selinux => true,
}
end
context 'when files_hiera_merge is set to the default value <false>' do
let(:params) { { :files_hiera_merge => false } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('swap_file') }
it { is_expected.to have_resource_count(5) }
it do
is_expected.to contain_swap_file__files('resource_name').with({
'ensure' => 'present',
'swapfile' => '/mnt/swap',
})
end
end
context 'when files_hiera_merge is set to valid value <true>' do
let(:params) { { :files_hiera_merge => true } }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('swap_file') }
it { is_expected.to have_resource_count(15) }
it do
is_expected.to contain_swap_file__files('resource_name').with({
'ensure' => 'present',
'swapfile' => '/mnt/swap',
})
end
it do
is_expected.to contain_swap_file__files('swap1').with({
'ensure' => 'present',
'swapfile' => '/mnt/swap.1',
'swapfilesize' => '1 GB',
})
end
it do
is_expected.to contain_swap_file__files('swap2').with({
'ensure' => 'present',
'swapfile' => '/mnt/swap.2',
'swapfilesize' => '2 GB',
'cmd' => 'fallocate',
})
end
end
end
describe 'variable type and content validations' do
# set needed custom facts and variables
let(:facts) do
{
:osfamily => 'RedHat',
:memorysize => '1.00 GB',
:selinux => true,
}
end
let(:validation_params) do
{
#:param => 'value',
}
end
validations = {
'bool_stringified' => {
:name => %w(files_hiera_merge),
:valid => [true, false, 'true', 'false'],
:invalid => ['invalid', %w(array), { 'ha' => 'sh' }, 3, 2.42, nil],
:message => '(Unknown type of boolean|str2bool\(\): (Requires either string to work with|Requires string to work with))',
},
'hash' => {
:name => %w(files),
:valid => [{ 'swap' => { 'ensure' => 'present' } }],
:invalid => ['invalid', %w(array), 3, 2.42, true, false, nil],
:message => '(is not a Hash|expects a Hash value, got)',
},
}
validations.sort.each do |type, var|
var[:name].each do |var_name|
var[:valid].each do |valid|
context "with #{var_name} (#{type}) set to valid #{valid} (as #{valid.class})" do
let(:params) { validation_params.merge({ :"#{var_name}" => valid, }) }
it { is_expected.to compile }
end
end
var[:invalid].each do |invalid|
context "with #{var_name} (#{type}) set to invalid #{invalid} (as #{invalid.class})" do
let(:params) { validation_params.merge({ :"#{var_name}" => invalid, }) }
it 'should fail' do
expect do
is_expected.to contain_class(subject)
end.to raise_error(Puppet::Error, /#{var[:message]}/)
end
end
end
end # var[:name].each
end # validations.sort.each
end # describe 'variable type and content validations'
end

View File

@@ -0,0 +1,14 @@
require 'spec_helper'
describe 'swap_file::swappiness' do
let(:params) do
{
:swappiness => 65,
}
end
it do
is_expected.to contain_sysctl('vm.swappiness').
with({"ensure"=>"present",
"value"=>"65"})
end
end

View File

@@ -0,0 +1,360 @@
require 'spec_helper'
describe 'swap_file::files' do
let(:title) { 'default' }
let(:facts) do
{
operatingsystem: 'RedHat',
osfamily: 'RedHat',
operatingsystemrelease: '7',
concat_basedir: '/tmp',
memorysize: '1.00 GB',
selinux: true,
}
end
# Add these two lines in a single test block to enable puppet and hiera debug mode
# Puppet::Util::Log.level = :debug
# Puppet::Util::Log.newdestination(:console)
context 'default parameters' do
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.1')
.with('command' => '/bin/dd if=/dev/zero of=/mnt/swap.1 bs=1M count=1024',
'creates' => '/mnt/swap.1')
end
it do
is_expected.to contain_file('/mnt/swap.1')
.with('owner' => 'root',
'group' => 'root',
'mode' => '0600',
'require' => 'Exec[Create swap file /mnt/swap.1]')
end
it do
is_expected.to contain_swap_file('/mnt/swap.1')
end
it do
is_expected.to contain_mount('/mnt/swap.1')
.with('require' => 'Swap_file[/mnt/swap.1]')
end
end
context 'custom swapfilesize parameter' do
let(:params) do
{
swapfilesize: '4.1 GB'
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.1')
.with('command' => '/bin/dd if=/dev/zero of=/mnt/swap.1 bs=1M count=4198',
'creates' => '/mnt/swap.1')
end
end
context 'custom swapfilesize parameter with timeout' do
let(:params) do
{
swapfile: '/mnt/swap.2',
swapfilesize: '4.1 GB',
timeout: 900
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.2')
.with('command' => '/bin/dd if=/dev/zero of=/mnt/swap.2 bs=1M count=4198',
'timeout' => 900, 'creates' => '/mnt/swap.2')
end
end
context 'custom swapfilesize parameter with timeout' do
let(:params) do
{
swapfile: '/mnt/swap.2',
swapfilesize: '4.1 GB',
timeout: 900
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.2')
.with('command' => '/bin/dd if=/dev/zero of=/mnt/swap.2 bs=1M count=4198',
'timeout' => 900, 'creates' => '/mnt/swap.2')
end
end
context 'custom swapfilesize parameter with fallocate' do
let(:params) do
{
swapfile: '/mnt/swap.3',
swapfilesize: '4.1 GB',
cmd: 'fallocate'
}
it do
is_expected.to compile.with_all_deps
end
is_expected.to contain_exec('Create swap file /mnt/swap.3')
.with(
'command' => '/usr/bin/fallocate -l 4198M /mnt/swap.3',
'creates' => '/mnt/swap.3'
)
end
end
context 'with cmd set to invalid value' do
let(:params) do
{
cmd: 'invalid'
}
end
it 'should fail' do
expect { should contain_class(subject) }.to raise_error(Puppet::Error, /Invalid cmd: invalid - \(Must be \'dd\' or \'fallocate\'\)/)
end
end
context 'resize_existing => true' do
let(:existing_swap_kb) { '204796' } # 200MB
context 'when swapfile_sizes fact exists and matches path' do
let(:params) do
{
swapfile: '/mnt/swap.resizeme',
resize_existing: true
}
end
let(:facts) do
{
operatingsystem: 'RedHat',
osfamily: 'RedHat',
operatingsystemrelease: '7',
concat_basedir: '/tmp',
memorysize: '1.00 GB',
swapfile_sizes: {
'/mnt/swap.resizeme' => existing_swap_kb,
},
swapfile_sizes_csv: "/mnt/swap.resizeme||#{existing_swap_kb}",
selinux: true,
}
end
it do
is_expected.to compile.with_all_deps
end
it do
should contain_swap_file__resize('/mnt/swap.resizeme').with('swapfile_path' => '/mnt/swap.resizeme',
'margin' => '50MB',
'expected_swapfile_size' => '1.00 GB',
'actual_swapfile_size' => existing_swap_kb,
'before' => 'Exec[Create swap file /mnt/swap.resizeme]')
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.resizeme')
.with('command' => '/bin/dd if=/dev/zero of=/mnt/swap.resizeme bs=1M count=1024',
'creates' => '/mnt/swap.resizeme')
end
it do
is_expected.to contain_file('/mnt/swap.resizeme')
.with('owner' => 'root',
'group' => 'root',
'mode' => '0600',
'require' => 'Exec[Create swap file /mnt/swap.resizeme]')
end
it do
is_expected.to contain_swap_file('/mnt/swap.resizeme')
.with('ensure' => 'present')
end
it do
is_expected.to contain_mount('/mnt/swap.resizeme')
.with('require' => 'Swap_file[/mnt/swap.resizeme]')
end
end
context 'when swapfile_sizes fact does not exist' do
let(:params) do
{
swapfile: '/mnt/swap.nofact',
resize_existing: true
}
end
let(:facts) do
{
operatingsystem: 'RedHat',
osfamily: 'RedHat',
operatingsystemrelease: '7',
concat_basedir: '/tmp',
memorysize: '1.00 GB',
swapfile_sizes: nil,
selinux: true,
}
end
it do
is_expected.to compile.with_all_deps
end
it do
should_not contain_swap_file__resize('/mnt/swap.nofact')
end
end
context 'when swapfile_sizes fact exits but file does not match' do
let(:params) do
{
swapfile: '/mnt/swap.factbutnomatch',
resize_existing: true
}
end
let(:facts) do
{
operatingsystem: 'RedHat',
osfamily: 'RedHat',
operatingsystemrelease: '7',
concat_basedir: '/tmp',
memorysize: '1.00 GB',
swapfile_sizes: {
'/mnt/swap.differentname' => '204796', # 200MB
},
swapfile_sizes_csv: "/mnt/swap.differentname||#{existing_swap_kb}",
selinux: true,
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.factbutnomatch')
.with(
'command' => '/bin/dd if=/dev/zero of=/mnt/swap.factbutnomatch bs=1M count=1024',
'creates' => '/mnt/swap.factbutnomatch'
)
end
it do
should_not contain_swap_file__resize('/mnt/swap.factbutnomatch')
end
end
context 'when swapfile_sizes fact exists and matches path, but not hash' do
let(:params) do
{
swapfile: '/mnt/swap.resizeme',
resize_existing: true
}
end
let(:existing_swap_kb) { '204796' } # 200MB
let(:facts) do
{
operatingsystem: 'RedHat',
osfamily: 'RedHat',
operatingsystemrelease: '7',
concat_basedir: '/tmp',
memorysize: '1.00 GB',
swapfile_sizes: "/mnt/swap.resizeme#{existing_swap_kb}",
swapfile_sizes_csv: "/mnt/swap.resizeme||#{existing_swap_kb}",
selinux: true,
}
end
it do
is_expected.to compile.with_all_deps
end
it do
should contain_swap_file__resize('/mnt/swap.resizeme').with('swapfile_path' => '/mnt/swap.resizeme',
'margin' => '50MB',
'expected_swapfile_size' => '1.00 GB',
'actual_swapfile_size' => existing_swap_kb,
'before' => 'Exec[Create swap file /mnt/swap.resizeme]')
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.resizeme')
.with('command' => '/bin/dd if=/dev/zero of=/mnt/swap.resizeme bs=1M count=1024',
'creates' => '/mnt/swap.resizeme')
end
it do
is_expected.to contain_file('/mnt/swap.resizeme')
.with('owner' => 'root',
'group' => 'root',
'mode' => '0600',
'require' => 'Exec[Create swap file /mnt/swap.resizeme]')
end
it do
is_expected.to contain_swap_file('/mnt/swap.resizeme')
.with('ensure' => 'present')
end
it do
is_expected.to contain_mount('/mnt/swap.resizeme')
.with('require' => 'Swap_file[/mnt/swap.resizeme]')
end
end
context 'when swapfile_sizes fact does not exist' do
let(:params) do
{
swapfile: '/mnt/swap.nofact',
resize_existing: true
}
end
let(:facts) do
{
operatingsystem: 'RedHat',
osfamily: 'RedHat',
operatingsystemrelease: '7',
concat_basedir: '/tmp',
memorysize: '1.00 GB',
swapfile_sizes: nil,
swapfile_sizes_csv: nil,
selinux: true,
}
end
it do
is_expected.to compile.with_all_deps
end
it do
should_not contain_swap_file__resize('/mnt/swap.nofact')
end
end
context 'when swapfile_sizes fact exits but file does not match' do
let(:params) do
{
swapfile: '/mnt/swap.factbutnomatch',
resize_existing: true
}
end
let(:facts) do
{
operatingsystem: 'RedHat',
osfamily: 'RedHat',
operatingsystemrelease: '7',
concat_basedir: '/tmp',
memorysize: '1.00 GB',
swapfile_sizes: "/mnt/swap.differentname#{existing_swap_kb}",
swapfile_sizes_csv: "/mnt/swap.differentname||#{existing_swap_kb}",
selinux: true,
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Create swap file /mnt/swap.factbutnomatch')
.with(
'command' => '/bin/dd if=/dev/zero of=/mnt/swap.factbutnomatch bs=1M count=1024',
'creates' => '/mnt/swap.factbutnomatch'
)
end
it do
should_not contain_swap_file__resize('/mnt/swap.factbutnomatch')
end
end
end
end

View File

@@ -0,0 +1,98 @@
require 'spec_helper'
describe 'swap_file::resize' do
let(:title) { 'default' }
let(:default_facts) do
{
:operatingsystem => 'RedHat',
:osfamily => 'RedHat',
:operatingsystemrelease => '7',
:concat_basedir => '/tmp',
:memorysize => '1.00 GB',
}
end
# Add these two lines in a single test block to enable puppet and hiera debug mode
# Puppet::Util::Log.level = :debug
# Puppet::Util::Log.newdestination(:console)
context 'has resize execs if swapfile outside of margin range' do
let(:params) do
{
:swapfile_path => '/mnt/swap.1',
:expected_swapfile_size => '1 GB',
:actual_swapfile_size => '512 GB',
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Detach swap file /mnt/swap.1 for resize').
with(
{
"command"=>"/sbin/swapoff /mnt/swap.1",
"onlyif"=>"/sbin/swapon -s | grep /mnt/swap.1"
}
)
is_expected.to contain_exec('Purge /mnt/swap.1 for resize').
with(
{
"command"=>"/bin/rm -f /mnt/swap.1",
"onlyif"=>"test -f /mnt/swap.1"
}
)
end
end
context 'wont have resize execs if swapfile inside of margin range' do
let(:params) do
{
:swapfile_path => '/mnt/swap.1',
:expected_swapfile_size => '4 GB',
:actual_swapfile_size => '3.9 GB',
:margin => '150MB',
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to_not contain_exec('Detach swap file /mnt/swap.1 for resize')
end
it do
is_expected.to_not contain_exec('Purge /mnt/swap.1 for resize')
end
end
context 'can get verboseness message' do
let(:params) do
{
:swapfile_path => '/mnt/swap.1',
:expected_swapfile_size => '4 GB',
:actual_swapfile_size => '5 GB',
:margin => '5MB',
:verbose => true,
}
end
it do
is_expected.to compile.with_all_deps
end
it do
is_expected.to contain_exec('Detach swap file /mnt/swap.1 for resize')
end
it do
is_expected.to contain_exec('Purge /mnt/swap.1 for resize')
end
it do
is_expected.to contain_notify('Resizing Swapfile Alert /mnt/swap.1').with_name("Existing : 5368709120B\nExpected: 4294967296B\nMargin: 5242880B")
end
end
end

View File

@@ -0,0 +1,5 @@
---
swap_file::files:
'resource_name':
ensure: 'present'
swapfile: '/mnt/swap'

View File

@@ -0,0 +1,8 @@
---
:backends:
- yaml
:yaml:
:datadir: 'spec/fixtures/hiera'
:hierarchy:
- fqdn/%{fqdn}
- parameter_tests/%{parameter_tests}

View File

@@ -0,0 +1,11 @@
---
swap_file::files:
'swap1':
ensure: 'present'
swapfile: '/mnt/swap.1'
swapfilesize: '1 GB'
'swap2':
ensure: 'present'
swapfile: '/mnt/swap.2'
swapfilesize: '2 GB'
cmd: 'fallocate'

View File

@@ -0,0 +1,20 @@
require 'spec_helper'
describe 'difference_within_margin' do
it { is_expected.not_to eq(nil) }
it { is_expected.to run.with_params([]).and_raise_error(Puppet::ParseError, /Wrong number of arguments given \(1 for 2\)/i) }
it { is_expected.to run.with_params(['1','2']).and_raise_error(Puppet::ParseError, /Wrong number of arguments given \(1 for 2\)/i) }
it { is_expected.to run.with_params([],'2').and_raise_error(Puppet::ParseError, /arg\[0\] array cannot be empty/i) }
it { is_expected.to run.with_params([100,150],60).and_return(true) }
it { is_expected.to run.with_params([100,150],40).and_return(false) }
it { is_expected.to run.with_params([213909504, 209711104], 5242880).and_return(true) }
it { is_expected.to run.with_params([104853504,209715200],5242880).and_return(false) }
it { is_expected.to run.with_params(['100','150'],'60').and_return(true) }
it { is_expected.to run.with_params(['100','150'],'40').and_return(false) }
it { is_expected.to run.with_params(['213909504','209711104'],'5242880').and_return(true) }
it { is_expected.to run.with_params(['104853504','209715200'],'5242880').and_return(false) }
end

View File

@@ -0,0 +1,12 @@
require 'spec_helper'
describe 'swap_file_size_from_csv' do
it { is_expected.not_to eq(nil) }
it { is_expected.to run.with_params([]).and_raise_error(Puppet::ParseError, /Wrong number of arguments given \(1 for 2\)/i) }
it { is_expected.to run.with_params(['1','2']).and_raise_error(Puppet::ParseError, /Wrong number of arguments given \(1 for 2\)/i) }
it { is_expected.to run.with_params([],'2').and_raise_error(Puppet::ParseError, /swapfile name but be a string/i) }
it { is_expected.to run.with_params('/mnt/swap.1','/mnt/swap.1||1019900,/mnt/swap.1||1019900').and_return('1019900') }
it { is_expected.to run.with_params('/mnt/swap.2','/mnt/swap.1||1019900,/mnt/swap.1||1019900').and_return(false) }
end

View File

@@ -0,0 +1,6 @@
--format
s
--colour
--loadby
mtime
--backtrace

View File

@@ -0,0 +1,24 @@
require 'puppetlabs_spec_helper/module_spec_helper'
# SimpleCov does not run on Ruby 1.8.7
unless RUBY_VERSION.to_f < 1.9
require 'simplecov'
require 'simplecov-console'
SimpleCov.formatters = [
SimpleCov::Formatter::HTMLFormatter,
SimpleCov::Formatter::Console,
]
SimpleCov.start do
coverage_dir('coverage/')
add_filter('/spec/')
end
end
RSpec.configure do |config|
config.hiera_config = 'spec/fixtures/hiera/hiera.yaml'
config.expect_with :rspec do |c|
c.max_formatted_output_length = 999
end
end
at_exit { RSpec::Puppet::Coverage.report! }

View File

@@ -0,0 +1,33 @@
require 'beaker-rspec'
unless ENV['RS_PROVISION'] == 'no'
hosts.each do |host|
if host.is_pe?
install_pe
else
install_puppet
on host, "mkdir -p #{host['distmoduledir']}"
end
end
end
UNSUPPORTED_PLATFORMS = ['windows']
RSpec.configure do |c|
# Project root
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
# Readable test descriptions
c.formatter = :documentation
# Configure all nodes in nodeset
c.before :suite do
# Install module and dependencies
puppet_module_install(:source => proj_root, :module_name => 'swap_file')
hosts.each do |host|
shell('puppet module install puppetlabs-stdlib --version 4.7.0', { :acceptable_exit_codes => [0] })
shell('puppet module install herculesteam/augeasproviders_core --version 2.1.0', { :acceptable_exit_codes => [0] })
shell('puppet module install herculesteam/augeasproviders_sysctl --version 2.1.0', { :acceptable_exit_codes => [0] })
end
end
end

View File

@@ -0,0 +1,46 @@
require "spec_helper"
describe Facter::Util::Fact do
before {
Facter.clear
}
describe 'swapfile_sizes_csv' do
context 'returns swapfile_sizes when present' do
before do
Facter.fact(:kernel).stubs(:value).returns("Linux")
File.stubs(:exists?)
File.expects(:exists?).with('/proc/swaps').returns(true)
Facter::Util::Resolution.stubs(:exec)
end
it do
proc_swap_output = <<-EOS
Filename Type Size Used Priority
/dev/dm-1 partition 524284 0 -1
/mnt/swap.1 file 204796 0 -2
/tmp/swapfile.fallocate file 204796 0 -3
EOS
Facter::Util::Resolution.expects(:exec).with('cat /proc/swaps').returns(proc_swap_output)
expect(Facter.value(:swapfile_sizes_csv)).to eq('/mnt/swap.1||204796,/tmp/swapfile.fallocate||204796')
end
end
context 'returns nil when no swapfiles' do
before do
Facter.fact(:kernel).stubs(:value).returns("Linux")
File.stubs(:exists?)
File.expects(:exists?).with('/proc/swaps').returns(true)
Facter::Util::Resolution.stubs(:exec)
end
it do
proc_swap_output = <<-EOS
Filename Type Size Used Priority
/dev/dm-2 partition 16612860 0 -1
EOS
Facter::Util::Resolution.expects(:exec).with('cat /proc/swaps').returns(proc_swap_output)
expect(Facter.value(:swapfile_sizes_csv)).to eq(nil)
end
end
end
end

View File

@@ -0,0 +1,34 @@
require "spec_helper"
describe Facter::Util::Fact do
before {
Facter.clear
}
describe 'swapfile_sizes' do
context 'returns swapfile_sizes when present' do
before do
Facter.fact(:kernel).stubs(:value).returns("Linux")
File.stubs(:exists?)
File.expects(:exists?).with('/proc/swaps').returns(true)
Facter::Util::Resolution.stubs(:exec)
end
it do
proc_swap_output = <<-EOS
Filename Type Size Used Priority
/dev/dm-1 partition 524284 0 -1
/mnt/swap.1 file 204796 0 -2
/tmp/swapfile.fallocate file 204796 0 -3
EOS
Facter::Util::Resolution.expects(:exec).with('cat /proc/swaps').returns(proc_swap_output)
expect(Facter.value(:swapfile_sizes)).to eq(
{
"/mnt/swap.1"=>"204796",
"/tmp/swapfile.fallocate"=>"204796"
}
)
end
end
end
end

View File

@@ -0,0 +1,93 @@
require 'spec_helper'
describe Puppet::Type.type(:swap_file).provider(:linux) do
let(:resource) { Puppet::Type.type(:swap_file).new(
{
:name => '/tmp/swap',
:size => '1024',
:provider => described_class.name
}
)}
let(:provider) { resource.provider }
let(:instance) { provider.class.instances.first }
swapon_s_output = <<-EOS
Filename Type Size Used Priority
/dev/sda2 partition 4192956 0 -1
/dev/sda1 partition 4454542 0 -2
EOS
swapon_line = <<-EOS
/dev/sda2 partition 4192956 0 -1
EOS
mkswap_return = <<-EOS
Setting up swapspace version 1, size = 524284 KiB
no label, UUID=0e5e7c60-bbba-4089-a76c-2bb29c0f0839
EOS
swapon_line_to_hash = {
:ensure => :present,
:file => "/dev/sda2",
:name => "/dev/sda2",
:priority => "-1",
:provider => :swap_file,
:size => "4192956",
:type => "partition",
:used => "0",
}
before :each do
Facter.stubs(:value).with(:kernel).returns('Linux')
provider.class.stubs(:swapon).with(['-s']).returns(swapon_s_output)
end
describe 'self.prefetch' do
it 'exists' do
provider.class.instances
provider.class.prefetch({})
end
end
describe 'exists?' do
it 'checks if swap file exists' do
expect(instance.exists?).to be_truthy
end
end
describe 'self.instances' do
it 'returns an array of swapfiles' do
swapfiles = provider.class.instances.collect {|x| x.name }
swapfile_sizes = provider.class.instances.collect {|x| x.size }
expect(swapfiles).to include('/dev/sda1','/dev/sda2')
expect(swapfile_sizes).to include('4192956','4454542')
end
end
describe 'self.get_swapfile_properties' do
it 'turns results from swapon -s line to hash' do
swapon_line_to_hash_provider = provider.class.get_swapfile_properties(swapon_line)
expect(swapon_line_to_hash_provider).to eql swapon_line_to_hash
end
end
describe 'create_swap_file' do
it 'runs mkswap and swapon' do
provider.stubs(:mkswap).returns(mkswap_return)
provider.stubs(:swapon).returns('')
provider.create_swap_file('/tmp/swap')
end
end
describe 'swap_off' do
it 'runs swapoff and returns the log of the command' do
provider.stubs(:swapoff).returns('')
provider.swap_off('/tmp/swap')
end
end
end

View File

@@ -0,0 +1,68 @@
#!/usr/bin/env ruby
require 'spec_helper'
describe Puppet::Type.type(:swap_file) do
before do
@class = described_class
@provider_class = @class.provide(:fake) { mk_resource_methods }
@provider = @provider_class.new
@resource = stub 'resource', :resource => nil, :provider => @provider
@class.stubs(:defaultprovider).returns @provider_class
@class.any_instance.stubs(:provider).returns @provider
end
it "should have :name as its keyattribute" do
expect(@class.key_attributes).to eq([:file])
end
describe "when validating attributes" do
params = [
:file,
]
properties = [
:type,
:size,
:used,
:priority,
]
params.each do |param|
it "should have a #{param} parameter" do
expect(@class.attrtype(param)).to eq(:param)
end
end
properties.each do |prop|
it "should have a #{prop} property" do
expect(@class.attrtype(prop)).to eq(:property)
end
end
%w[. ./foo \foo C:/foo \\Server\Foo\Bar \\?\C:\foo\bar \/?/foo\bar \/Server/foo foo//bar/baz].each do |invalid_path|
context "path => #{invalid_path}" do
it 'should require a valid path for file' do
expect {
@class.new({:file => invalid_path})
}.to raise_error(Puppet::ResourceError, /file parameter must be a valid absolute path/)
end
end
end
%w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\Server/Foo /foo//bar/baz].each do |valid_path|
context "path => #{valid_path}" do
it 'should allow a valid path for file' do
expect {
@class.new({:file => valid_path})
}.not_to raise_error
end
end
end
end
end

View File

@@ -0,0 +1,11 @@
$secgen_params = secgen_functions::get_parameters($::base64_inputs_file)
$swapfile_size = $secgen_params['size'][0]
class { 'swap_file':
files => {
'swap1' => {
ensure => present,
swapfile => '/mnt/swap.1',
swapfilesize => $swapfile_size,
}
},
}

View File

@@ -31,7 +31,7 @@ class FilenameGenerator < StringEncoder
extension = ''
end
15.times { leaked_filenames << Faker::File.file_name('', file_name, extension, '').chomp('.') }
15.times { leaked_filenames << Faker::File.file_name(dir:'', name:file_name, ext:extension, directory_separator: '').chomp('.') }
output = leaked_filenames.sample

View File

@@ -9,7 +9,7 @@ class RandomDifficulty < StringGenerator
end
def generate
outputs << %w(easy medium high).sample.chomp
outputs << %w(easy medium hard).sample.chomp
end
end

View File

@@ -1,5 +1,6 @@
#!/usr/bin/ruby
require_relative '../../../../../lib/objects/local_string_encoder.rb'
class AccountGenerator < StringEncoder
attr_accessor :username
attr_accessor :password

View File

@@ -0,0 +1,42 @@
#!/usr/bin/ruby
require 'json'
require_relative '../../../../../../lib/objects/local_string_generator.rb'
class GoalFlagHacktivity < StringGenerator
attr_accessor :target
attr_accessor :mapping
attr_accessor :mapping_type
def initialize
super
self.module_name = 'Goal-Flag to Hacktivity AlertActioner Config Generator'
self.target = '' # Address for Hacktivity / external web application
self.mapping = [] # TODO: Implement granular mappings
self.mapping_type = ''
end
def generate
# TODO: Create an enum-like hash/class to validate the mapping_types
self.outputs << {:target => self.target, :mapping => self.mapping, :mapping_type => self.mapping_type}.to_json
end
def get_options_array
super + [['--target', GetoptLong::REQUIRED_ARGUMENT],
['--mapping', GetoptLong::OPTIONAL_ARGUMENT],
['--mapping_type', GetoptLong::OPTIONAL_ARGUMENT]]
end
def process_options(opt, arg)
super
case opt
when '--target'
self.target = arg
when '--mapping'
self.mapping << arg
when '--mapping_type'
self.mapping_type << arg
end
end
end
GoalFlagHacktivity.new.run

View File

@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<generator xmlns="http://www.github/cliffe/SecGen/generator"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/generator">
<name>AlertActioner goal-flag-hacktivity config generator</name>
<author>Thomas Shaw</author>
<module_license>GPLv3</module_license>
<description>TODO</description>
<type>alert_actioner_config</type>
<platform>linux</platform>
<read_fact>target</read_fact>
<!-- Either provide a mapping (i.e. [system1vuln1goal1 to system1vuln1goal_flag1, ... ] OR a broader mapping_type -->
<read_fact>mapping</read_fact>
<read_fact>mapping_type</read_fact>
<default_input into="target">
<value>https://hacktivity.aet.leedsbeckett.ac.uk/submit_flag</value>
</default_input>
<!-- Mapping types tell the project_file_creator what to do.-->
<default_input into="mapping_type">
<value>all_goal_flags_to_hacktivity</value>
</default_input>
<output_type>json</output_type>
</generator>

View File

@@ -0,0 +1,67 @@
#!/usr/bin/ruby
require 'json'
require_relative '../../../../../../lib/objects/local_string_generator.rb'
# Generate a config hash for the XmlAlertActionConfigGenerator
class GoalMessageHost < StringGenerator
attr_accessor :host
attr_accessor :sender
attr_accessor :password
attr_accessor :recipient
attr_accessor :message_header
attr_accessor :message_subtext
attr_accessor :mapping
attr_accessor :mapping_type
def initialize
super
self.module_name = 'Goal-Message-Host AlertActioner Config Generator'
self.host = '' # Host IP
self.sender = '' # Host username
self.password = '' # Host password
self.recipient = '' # Host password
self.message_header = '' # Message to send to host
self.message_subtext = '' # Message to send to host
self.mapping = [] # TODO: Implement granular mappings
self.mapping_type = ''
end
def generate
self.outputs << {:host => self.host, :sender => self.sender, :password => self.password, :recipient => self.recipient, :message_header => self.message_header, :message_subtext => self.message_subtext, :mapping => self.mapping, :mapping_type => self.mapping_type}.to_json
end
def get_options_array
super + [['--host', GetoptLong::REQUIRED_ARGUMENT],
['--sender', GetoptLong::REQUIRED_ARGUMENT],
['--password', GetoptLong::REQUIRED_ARGUMENT],
['--recipient', GetoptLong::REQUIRED_ARGUMENT],
['--message_header', GetoptLong::REQUIRED_ARGUMENT],
['--message_subtext', GetoptLong::OPTIONAL_ARGUMENT],
['--mapping', GetoptLong::OPTIONAL_ARGUMENT],
['--mapping_type', GetoptLong::OPTIONAL_ARGUMENT]]
end
def process_options(opt, arg)
super
case opt
when '--host'
self.host = arg
when '--sender'
self.sender = arg
when '--password'
self.password = arg
when '--recipient'
self.recipient = arg
when '--message_header'
self.message_header = arg
when '--message_subtext'
self.message_subtext = arg
when '--mapping'
self.mapping << arg
when '--mapping_type'
self.mapping_type = arg
end
end
end
GoalMessageHost.new.run

View File

@@ -0,0 +1,46 @@
<?xml version="1.0"?>
<generator xmlns="http://www.github/cliffe/SecGen/generator"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/generator">
<name>AlertActioner goal-message-host config generator</name>
<author>Thomas Shaw</author>
<module_license>GPLv3</module_license>
<description>Generates a json config string for use by the XmlAlertActionConfigGenerator.</description>
<type>alert_actioner_config</type>
<platform>linux</platform>
<read_fact>host</read_fact>
<read_fact>sender</read_fact>
<read_fact>password</read_fact>
<read_fact>recipient</read_fact>
<read_fact>message_header</read_fact>
<read_fact>message_subtext</read_fact>
<!-- Either provide a mapping (i.e. [system1vuln1goal1 to system1vuln1goal_flag1, ... ] OR a broader mapping_type -->
<read_fact>mapping</read_fact>
<read_fact>mapping_type</read_fact>
<default_input into="message_header">
<generator type="message_generator"/>
<value>Message header from the module</value>
</default_input>
<default_input into="message_subtext">
<!--<generator type="message_generator"/>-->
<value>Subtext from the module</value>
</default_input>
<default_input into="host">
<value>127.0.0.1</value>
</default_input>
<!-- Mapping types tell the project_file_creator what to do.-->
<default_input into="mapping_type">
<value>all_goal_messages_to_host</value>
</default_input>
<output_type>json</output_type>
</generator>

View File

@@ -0,0 +1,61 @@
#!/usr/bin/ruby
require 'json'
require_relative '../../../../../lib/objects/local_string_generator.rb'
class AAAConfigGenerator < StringGenerator
attr_accessor :server_ip
attr_accessor :client_ips
attr_accessor :elasticsearch_port
attr_accessor :logstash_port
attr_accessor :kibana_port
attr_accessor :aa_configs
def initialize
super
self.module_name = 'Analysis Alert Action Config Generator'
self.client_ips = []
self.aa_configs = []
end
def generate
# Validate the inputs + crash out if we don't receive all inputs.
self.outputs << {
:server_ip => self.server_ip,
:client_ips => self.client_ips,
:elasticsearch_port => self.elasticsearch_port,
:logstash_port => self.logstash_port,
:kibana_port => self.kibana_port,
:aa_configs => self.aa_configs
}.to_json
end
def get_options_array
super + [['--server_ip', GetoptLong::REQUIRED_ARGUMENT],
['--client_ips', GetoptLong::REQUIRED_ARGUMENT],
['--elasticsearch_port', GetoptLong::REQUIRED_ARGUMENT],
['--logstash_port', GetoptLong::REQUIRED_ARGUMENT],
['--kibana_port', GetoptLong::REQUIRED_ARGUMENT],
['--aa_configs', GetoptLong::REQUIRED_ARGUMENT]]
end
def process_options(opt, arg)
super
case opt
when '--server_ip'
self.server_ip = arg
when '--client_ips'
self.client_ips << arg
when '--elasticsearch_port'
self.elasticsearch_port = arg
when '--logstash_port'
self.logstash_port = arg
when '--kibana_port'
self.kibana_port = arg
when '--aa_configs'
self.aa_configs << JSON.parse(arg)
end
end
end
AAAConfigGenerator.new.run

View File

@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<generator xmlns="http://www.github/cliffe/SecGen/generator"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/generator">
<name>AAA config generator</name>
<author>Thomas Shaw</author>
<module_license>GPLv3</module_license>
<description>TODO</description>
<type>aaa_config</type>
<platform>linux</platform>
<read_fact>server_ip</read_fact>
<read_fact>client_ips</read_fact>
<read_fact>elasticsearch_port</read_fact>
<read_fact>logstash_port</read_fact>
<read_fact>kibana_port</read_fact>
<read_fact>aa_configs</read_fact>
<output_type>json</output_type>
</generator>

View File

@@ -0,0 +1,27 @@
$secgen_parameters = secgen_functions::get_parameters($::base64_inputs_file)
$aaa_config = parsejson($secgen_parameters['aaa_config'][0])
$elasticsearch_ip = $aaa_config['server_ip']
$elasticsearch_port = 0 + $aaa_config['elasticsearch_port']
$logstash_port = 0 + $aaa_config['logstash_port']
$kibana_ip = $aaa_config['server_ip']
$kibana_port = 0 + $aaa_config['kibana_port']
class { 'elasticsearch_7':
api_host => $elasticsearch_ip,
api_port => $elasticsearch_port,
}~>
class { 'logstash_7':
elasticsearch_ip => $elasticsearch_ip,
elasticsearch_port => $elasticsearch_port,
logstash_port => $logstash_port
}
class { 'kibana_7':
elasticsearch_ip => $elasticsearch_ip,
elasticsearch_port => $elasticsearch_port,
kibana_port => $kibana_port
}~>
class { 'elastalert':
elasticsearch_ip => $elasticsearch_ip,
elasticsearch_port => $elasticsearch_port,
}~>
class { 'analysis_alert_action_server': }

View File

@@ -0,0 +1,46 @@
require 'fileutils'
require 'erb'
require_relative '../lib/logging'
require_relative '../lib/print'
require_relative '../lib/aa_constants'
class AlertActioner
include Logging
attr_accessor :alertactioner_name # AlertActioner name - ID for this particular action
attr_accessor :alert_name # Alert / Rule name - ID for elastalert rule that was triggered
def initialize(config_filename, alertaction_index, alert_name)
self.alertactioner_name = config_filename[0..-5] + '-' + alertaction_index.to_s + '-' + alertaction_index.to_s # Remove .xml extension
self.alert_name = alert_name
end
def perform_action
# override me
end
def action_alert
Print.info("Running #{self.class}: #{self.alertactioner_name}", logger)
Print.info("Actioning alert: #{self.alert_name}", logger)
perform_action
end
def template_based_file_write(template, filename)
template_out = ERB.new(File.read(template), 0, '<>-')
begin
File.open(filename, 'wb+') do |file|
file.write(template_out.result(self.get_binding))
end
rescue StandardError => e
Print.err "Error writing file: #{e.message}"
Print.err e.backtrace.inspect
end
end
def get_binding
binding
end
end

View File

@@ -0,0 +1,57 @@
require 'net/http'
require 'uri'
require 'open3'
require_relative 'alert_actioner'
class CommandActioner < AlertActioner
attr_accessor :host
attr_accessor :username
attr_accessor :password
attr_accessor :commands
def initialize(config_filename, alertaction_index, alert_name, host, username, password, commands=[])
super(config_filename, alertaction_index, alert_name)
self.host = host
self.username = username
self.password = password
self.commands = commands
end
def perform_action
# Create config/commands directory
FileUtils.mkdir_p COMMANDS_DIRECTORY
commands_sh_path = COMMANDS_DIRECTORY + "#{self.alertactioner_name}.sh"
template_path = TEMPLATES_DIRECTORY + 'command.sh.erb'
# We need to populate an array of commands + their parameters
@shell_commands = command_strings
template_based_file_write(template_path, commands_sh_path)
ssh_command = "sshpass -p #{self.password} ssh -oStrictHostKeyChecking=no #{self.username}@#{self.host} /bin/bash -s < #{commands_sh_path}"
Print.info " Command strings:\n #{@shell_commands.join("\n ")}"
stdout, stderr, status = Open3.capture3(ssh_command)
Print.info " stdout: #{stdout}", logger
Print.info " stderr: #{stderr}", logger if stderr != ''
Print.info " STATUS: #{status}", logger
unless status.exitstatus == 0
Print.info " ERROR: non-zero exit code.", logger
exit(1)
end
end
def command_strings
self.commands
# For more specific command-actioners, override me.
end
# TODO: Override me in superclass to print actioner type + all parameters??
def to_s
"#{self.class}:\n Host: #{self.host}\n Command: #{self.command}\n Parameters: #{self.parameters.join(',')}"
end
end

Some files were not shown because too many files have changed in this diff Show More