wmdeit-cf-wmdelib/lib.cf

686 lines
17 KiB
CFEngine3

#
#
#
body perms uperm(user,group,mode)
{
mode => "$(mode)";
rxdirs => "false";
groups => { "$(group)" };
owners => { "$(user)" };
}
#
# wmdelib.cf
#
bundle agent wmde_install_packages(pkgs,name)
{
packages:
freebsd::
"$(pkgs)"
policy => "present",
package_module => pkg,
handle => "$(name)_pkgs_installed",
classes => results("namespace","$(name)");
debian::
"$(pkgs)"
policy => "present",
package_module => apt_get,
handle => "$(name)_pkgs_installed",
classes => results("namespace","$(name)");
fedora|centos::
"$(pkgs)"
policy => "present",
package_module => yum,
handle => "$(name)_pkgs_installed",
classes => results("namespace","$(name)");
}
body perms wmde_perms(user,group,mode)
{
owners => { "$(user)" };
groups => { "$(group)" };
mode => "$(mode)";
rxdirs=>"false";
}
bundle agent wmde_srv(service_name,cmd)
{
classes:
"start" expression => strcmp("start","$(cmd)");
"restart" expression => strcmp("restart",cmd);
commands:
freebsd::
"/bin/sh"
args => "-c '/usr/sbin/service $(service_name) onestatus > /dev/null && echo +$(service_name)_running || echo -$(service_name)_running'",
inform => "false",
module => "true",
handle => "$(service_name)_status_tested";
"!$(service_name)_running&start"::
"/bin/sh"
args => "-c '/usr/sbin/service $(service_name) onestart 2> /dev/null > /dev/null && echo +$(service_name)_started || echo -$(service_name)_started'",
module => "true",
depends_on => {"$(service_name)_status_tested"};
"!$(service_name)_running&restart"::
"/bin/sh"
args => "-c '/usr/sbin/service $(service_name) onerestart 2> /dev/null > /dev/null && echo +$(service_name)_started || echo -$(service_name)_started'",
module => "true",
depends_on => {"$(service_name)_status_tested"};
reports:
start::
# "MUST START";
!start::
# "MUST NOT START";
# running::
# "Server $(service_name) - running";
# !running::
# "Server $(service_name) - not running";
}
body service_method wmde
{
service_type => "generic";
service_bundle => wmde_srv ($(this.promiser), $(this.service_policy));
}
bundle agent wmde_enable_service(bundlename)
{
vars:
freebsd::
# "cha" string => "$(bundlename).service_cfg_name";
"filename" string => "/etc/rc.conf.d/$($(bundlename).service_cfg_name)";
files:
freebsd::
"$(filename)"
create => "true",
perms => m("644"),
content => "$($(bundlename).service_cfg_name)_enable=YES";
reports:
# "FREEBSD: $(filename) $(cha)";
}
bundle agent wmde_service(service_name,start_cond, restart_cond)
{
classes:
freebsd::
"service_running" expression => returnszero("/usr/sbin/service $(service_name) onestatus >/dev/null 2>&1", "useshell");
commands:
"freebsd&(!service_running)&($(start_cond))"::
"/usr/sbin/service"
args => "$(service_name) onestart >/dev/null 2>&1",
contain => wmde_cmd_useshell,
handle => "$(handle)_service_started";
"freebsd&(service_running)&($(start_cond))"::
"/usr/bin/true"
inform => "false",
handle => "$(handle)_service_started";
"freebsd&($(restart_cond))"::
"/usr/sbin/service"
args => "$(service_name) onerestart >/dev/null 2>&1",
contain => wmde_cmd_useshell,
handle => "$(handle)_service_restarted";
services:
"(!freebsd)&($(start_cond))"::
"$(service_name)"
service_policy => "start",
handle => "$(handle)_service_started";
"(!freebsd)&($(restart_cond))"::
"$(service_name)"
service_policy => "restart",
handle => "$(handle)_service_restarted";
reports:
}
bundle agent wmde_restart_service(service_name, id)
{
commands:
debian|centos|fedora::
"/bin/sh -c "
args => "'/bin/echo $(id) > /dev/null && /usr/bin/systemctl restart $(service_name)'";
freebsd::
"/bin/sh -c "
args => "'/bin/echo $(id) > /dev/null && /usr/sbin/service $(service_name) onerestart'";
}
body contain wmde_cmd_useshell
{
useshell=>"useshell";
}
bundle agent download_and_untar(
name,
sync_src,
sync_dst,
install_dir,
test_file
)
# @brief Download and untar a tar archive at specified location
# @param name String used for unique handles and classes
# @param sync_src Path to tar archive on the policy hub
# @param sync_dst Path to destination of download archive
# @param install_dir Destination directory for untarred archive
# @param test_file Conditional file for existence of untarred archive
{
classes:
"$(name)_untar" expression => not(fileexists("$(test_file)"));
files:
"$(sync_dst)"
copy_from => sync_cp("$(sync_src)","$(sys.policy_hub)"),
handle => "$(name)_tgz_copied",
classes => if_repaired ("$(name)_untar"),
perms => m(644);
commands:
"$(name)_untar"::
"/usr/bin/tar"
args => "xzvf $(sync_dst) -C $(install_dir)",
depends_on => {"$(name)_tgz_copied"},
handle => "$(name)_untarred";
reports:
# "TESTFILE: $(test_file)";
}
bundle agent install_yum_repo(name,content)
{
files:
"/etc/yum.repos.d/$(name).repo"
create => "true",
content => $(content),
classes => if_repaired("yum_repo_$(name)_repaired"),
handle => "yum_repo_$(name)_installed";
commands:
"yum_repo_$(name)_repaired"::
"dnf"
args => "makecache",
contain => wmde_cmd_useshell;
}
bundle agent install_apt_repo(name,repo_src,key_src,key_name)
{
classes:
debian|ubuntu::
"do_install" expression => not(fileexists("/etc/apt/sources.list.d/$(name).list"));
vars:
do_install::
"pkgs" slist => {
"curl",
"ca-certificates",
"lsb-release"
};
"add_repo_cmd" string => "/usr/bin/add-apt-repository";
methods:
do_install::
"any" usebundle => install_wget;
"any" usebundle => wmde_install_packages(@(pkgs),"apt_repo");
commands:
do_install::
"/bin/sh"
args => "$(sys.workdir)/inputs/$(def.wmde_libdir)/scripts/install-apt-repo.sh $(name) $(repo_src) $(key_src) $(key_name)",
depends_on => {
"wget_pkgs_installed",
"apt_repo_pkgs_installed"
};
}
bundle agent install_server_tools
{
vars:
debian|fedora|centos::
"pkgs" slist => {
"net-tools",
"telnet",
"tcpdump",
"nmap"
};
methods:
debian|fedora|centos::
"any" usebundle => wmde_install_packages(@(pkgs),"server_tools");
}
bundle agent install_system_repos
{
classes:
centos::
"centos_9_and_later" expression => isgreaterthan("$(sys.os_version_major)", "8") ;
commands:
vars:
# centos::
# "pkgs" slist => {
# "epel-release"
# };
# !centos::
# "pkgs" slist => {},
# handle => "system_repos_pkgs_installed";
commands:
centos::
"/usr/bin/yum"
args => "install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(sys.os_version_major).noarch.rpm",
if => not(returnszero("rpm -q epel-release > /dev/null","useshell")),
handle=>"system_repos_pkgs_installed";
centos_8::
"/usr/bin/dnf"
inform => "false",
args => "config-manager --set-enabled powertools";
centos_9_and_later::
"/usr/bin/dnf"
inform => "false",
args => "config-manager --set-enabled crb";
"/usr/bin/update-crypto-policies"
inform => "false",
contain => wmde_cmd_useshell,
args => "--set LEGACY > /dev/null";
methods:
# "any" usebundle => wmde_install_packages(@(pkgs),"system_repos");
reports:
}
bundle agent dearmor_gpg_key( key_file, dearmored_key_path )
# @brief Dearmor a GPG key
# @param key_file Path of the key file to dearmor
# @todo Still needs work
{
vars:
"gpg_pkg"
slist => {"gpg"};
methods:
"Ensure presence of gpg"
usebundle => wmde_install_packages( @(gpg_pkg) , "gpg"),
handle => "gpg_installed";
files:
"$(keyrings_dir)/$(key_name)"
perms => mog( "0644", "root", "root" ),
depends_on => { "dearmored_key" };
commands:
"/usr/bin/gpg"
arglist => { "--dearmor", "--yes", "--output", "$(keyrings_dir)/$(key_name)"},
depends_on => { "gpg_installed" },
handle => "dearmored_key";
}
bundle agent _install_apt_repo( name, url, key_url, _key_name )
# @brief Configure an apt repository
# @param name Unique name of the repository, used for filenames
# @param url URL of the repository
# @param key_url URL for downloading the signing key
# @param _key_name Name of the signing key, only for compatibility
#
# Drop-in for Tobias' install_apt_repo.
{
methods:
"Install component main of repository"
usebundle => _install_apt_repository( "$(name)", "$(url)", "$(key_url)", "$(sys.os_release[VERSION_CODENAME])", "main" );
}
bundle agent _install_apt_repository( name, url, key_url, suite, components )
# @brief Configure an apt repository
# @param name Unique name of the repository, used for filenames
# @param url URL of the repository
# @param key_url URL for downloading the signing key
# @param suite Name of the suite to use, e.g. "stable", "bookworm", "nimble"
# @param components Whitespace separated string of names of components to enable, e.g. "main" or "main universe multiverse"
#
# Replacement for install_apt_repo from Tobias. Does not need a shell script.
# What are suites and components? See
# https://gist.github.com/CodingKoopa/3b30afe8c91e3950f6b124cd2abe3b6b
{
vars:
"keyrings_dir"
string => "/etc/apt/keyrings";
"sources_dir"
string => "/etc/apt/sources.list.d";
"key_name"
string => lastnode( "$(key_url)", "/" ),
handle => "key_name_defined";
"key_extension"
string => lastnode( "key_name", "\."),
depends_on => { "key_name_defined" };
"sources_data"
data => '{ "URL": "$(url)", "Suite": "main" }';
"pkgs" slist => {
"curl",
"ca-certificates",
"lsb-release"
};
dearmor::
"final_key_name"
string => string_replace( "$(key_name)", ".asc", ".gpg"),
handle => "final_key_name_defined";
!dearmor::
"final_key_name"
string => "$(key_name)",
handle => "final_key_name_defined";
key_url_has_content::
"source_template"
string => "deb [signed-by=$(keyrings_dir)/$(final_key_name)] $(url) $(suite) $(components)",
depends_on => { "final_key_name_defined" },
handle => "source_template_defined";
!key_url_has_content::
"source_template"
string => "deb $(url) $(suite) $(components)",
handle => "source_template_defined";
classes:
"name_exists" expression => isvariable( "name" );
"url_exists" expression => isvariable( "url" );
"key_url_exists" expression => isvariable( "key_url" );
name_exists::
"name_is_string" expression => strcmp( "string", type( "name", "false" ) );
name_is_string::
"name_has_content" expression => isgreaterthan( string_length( "$(name)" ), "0" );
url_exists::
"url_is_string" expression => strcmp( "string", type( "url", "false" ) );
url_is_string::
"url_has_content" expression => isgreaterthan( string_length( "$(url)" ), "7" ); # "https?://.+"
key_url_exists::
"key_url_is_string" expression => strcmp( "string", type( "key_url", "false" ) );
key_url_is_string::
"key_url_has_content" expression => isgreaterthan( string_length( "$(key_url)" ), "7" );
key_url_has_content::
"dearmor"
expression => strcmp( "asc", $(key_extension) ),
scope => "bundle";
debian|ubuntu::
"do_install" expression => not( or( fileexists( "$(sources_dir)/$(name).list" ), fileexists( "$(sources_dir)/$(name).sources" ) ) );
files:
do_install::
"$(keyrings_dir)/."
create => "true",
perms => mog( "755", "root", "root" ),
handle => "keyrings_dir";
"$(sources_dir)/$(name).list"
content => "$(source_template)",
perms => mog( "0644", "root", "root" ),
depends_on => { "source_template_defined" },
handle => "$(name)_list_created",
classes => if_repaired( "repo_repaired" );
"$(keyrings_dir)/$(final_key_name)"
perms => mog( "0644", "root", "root"),
depends_on => { "final_key_name_defined", "$(name)_signing_key" },
handle => "set_perms";
methods:
do_install::
"Ensure necessary packages"
usebundle => wmde_install_packages( @(pkgs), "apt_repo" ),
handle => "apt_repo_pkgs_installed";
do_install & key_url_has_content::
"Ensure presence of signing key"
usebundle => curl_file( "$(key_url)", "$(keyrings_dir)/" ),
depends_on => { "keyrings_dir" },
handle => "$(name)_signing_key";
do_install & dearmor::
"Dearmor the GPG key"
usebundle => dearmor_gpg_key( "$(keyrings_dir)/$(key_name)", "$(keyrings_dir)/$(final_key_name)" ),
depends_on => { "$(name)_signing_key", "final_key_name_defined" },
handle => "key_dearmored";
commands:
repo_repaired::
"/usr/bin/apt-get"
args => "update",
depends_on => { "$(name)_list_created" };
}
bundle agent enable_ppa( ppa )
# @brief Enable a PPA
# @param ppa URL to the PPA to enable
{
vars:
"pkg" slist => { "software-properties-common" };
methods:
"Ensure add-apt-repository is present"
usebundle => wmde_install_packages( @(pkg), "software_properties_common"),
handle => "add_apt_repository";
commands:
"/usr/bin/add-apt-repository"
args => "--yes --ppa $(ppa)",
depends_on => {"add_apt_repository"};
}
bundle agent curl_file( url, destination )
# @depends paths
# @brief Download a file from a network location
#
# Use `curl` to download a file from a network location. See the curl(1) man page for
# a list of protocols supported by curl.
#
# Source: https://gist.github.com/bahamat/9aeaf8964f01c368a6c5
#
# @param url The full URL to the requested resource
# @param destination The full local path to store the downloaded resource data
#
# Example:
#
# ```cf3
# vars:
# "url" string => "http://s3.amazonaws.com/cfengine.package-repos/tarballs/cfengine-3.6.5.tar.gz";
# "file" string => "/tmp/cfengine-3.6.5.tar.gz";
# methods:
# "download" usebundle => curl_file("$(url)","$(file)");
# ```
{
vars:
"file_date"
string => "-z $(file)",
ifvarclass => fileexists("$(file)");
"curlflags" string => "--silent --location --remote-name --continue-at -";
defaults:
"file_date" string => " ";
commands:
"/usr/bin/curl $(file_date) $(curlflags) --output-dir $(destination) $(url)";
}
bundle agent download_file(method,src,dst,cls,prms_arg)
# @brief Download a file to specific location and set permissions.
# @param method string Either `wget`, `wget_simple` or anything else (not implemented?)
# @param src string Source URL of the file to download
# @param dst string Destination for the downloaded file
# @param cls string Prefix for classes used in the bundle
# @param prms_arg data JSON for { m(string): mode, o(string): uid, g(string): gid }
#
# method wget will download to a specific file while method wget_simple
# will accept the remote filename and puts the file at a specified
# destination.
{
vars:
"prms_default" data => '{
"m":"600",
"o":"$(sys.user_data[uid])",
"g":"$(sys.user_data[gid])"
}';
"prms" data => mergedata(@(prms_default),parsejson($(prms_arg)));
wget::
"wget_args" string => "-q --timestamping -O $(dst) $(src) || (rm -f $(dst) && /usr/bin/false) ";
wget_simple::
"wget_args" string => "-q --timestamping -P $(dst) $(src)";
classes:
"$(method)";
wget | wget_simple::
"run_wget" expression => not(fileexists($(dst)));
files:
policyhub::
"$(dst)"
copy_from => remote_dcp("$(src)","$(sys.policy_hub)"),
classes => if_repaired("$(cls)_repaired"),
perms => mog ("$(prms[m])","$(prms[o])","$(prms[g])");
methods:
wget::
"any" usebundle => "install_wget"; #, handle=>"wget_installed";
commands:
run_wget::
"$(wget.exe)"
args => "$(wget_args)",
contain => wmde_cmd_useshell,
handle => "$(cls)_downloaded",
classes => results("namespace","$(cls)"),
depends_on => {"wget_installed"},
inform => "true";
"/usr/bin/true"
inform => "false",
depends_on => {"$(cls)_downloaded"},
classes => if_repaired("$(cls)_kept");
(wget|wget_simple) & !run_wget::
"/usr/bin/true"
inform => "false",
classes => if_repaired("$(cls)_kept");
files:
!wget_simple::
"$(dst)"
perms => mog ("$(prms[m])","$(prms[o])","$(prms[g])"),
depends_on => {"$(cls)_downloaded"};
reports:
}
bundle edit_line bind_mount(src,dst)
{
insert_lines:
freebsd::
"$(src) $(dst) nullfs rw,late 0 0";
centos::
"$(src) $(dst) none defaults,bind 0 0";
}
bundle agent bind_mount(src,dst)
{
files:
"/etc/fstab"
edit_line => bind_mount("$(src)","$(dst)"),
classes => if_repaired(bind_mount_fstab_changed);
commands:
bind_mount_fstab_changed::
"echo '$(src)$(dst)' > /dev/null && mount"
contain=>wmde_cmd_useshell,
args => "-a";
}
bundle agent etc_hosts(hosts)
{
vars:
"idx" slist => getindices(@(hosts));
"settings[$(idx)]" string => "$(hosts[$(idx)])";
files:
"/etc/hosts"
create => "true",
perms => m("644"),
edit_line => set_config_values("$(this.bundle).settings"),
classes => results("namespace","etc_hosts");
}
bundle agent cron
{
vars:
"cron_d" string => "/etc/cron.d";
freebsd::
"cron_d" string => "/usr/local/etc/cron.d";
files:
"$(cron_d)/."
create => "true",
handle => "cron_d_created";
}
bundle agent create_cron_job( name, time, command )
# @brief Create a cron job
# @param name filename of the cronjob
# @param time cron time pattern
# @param command command to execute prepended by the user account under which to run the command
#
# Usage example
# ```cf3
# bundle agent main {
# methods:
# "Ensure Nextcloud's cronjob is setup"
# usebundle => create_cron_job( "my_cronjob", "*/5 * * * *", "www-data /usr/bin/php -f /var/www/nextcloud/cron.php ");
# }
# ```
{
methods:
"any" usebundle => cron;
files:
"$(cron.cron_d)/$(name)"
create => "true",
content => "#
# Managed by CFEngine
#
$(time) $(command)
",
depends_on => {"cron_d_created"};
}