refs #8025 #7892 roles debian-once & debian-host, sysctl, README, environment

This commit is contained in:
Juan Ferrer 2024-10-01 14:14:51 +02:00
parent 71dfa53a8c
commit 0a73bc63b3
37 changed files with 482 additions and 56 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
.vscode/
.vaultpass
.pyenv
venv
context/_build

View File

@ -16,8 +16,9 @@ ansible-galaxy collection install -r collections/requirements.yml
Create Python virtual environment.
```
python3 -m venv .pyenv
source .pyenv/bin/activate
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip ansible==10.1.0 ansible-builder==3.1.0
pip install -r requirements.txt
```
@ -45,6 +46,13 @@ ansible-vault {view,edit} --vault-pass-file .vaultpass vault.yml
When running playbooks that use the vault the *vault-playbook.sh* script can
be used, it is ovelay over the original *ansible-playbook* command.
## Create execution environment
Create an image with *ansible-builder* and upload it to registry.
```
ansible-builder build --tag ansible-runner:vn1
```
## Common playbooks
* **facts.yml**: Collect and display facts from a host
@ -59,3 +67,4 @@ be used, it is ovelay over the original *ansible-playbook* command.
* https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_vars_facts.html
* https://www.passbolt.com/blog/managing-secrets-in-ansible-using-passbolt
* https://galaxy.ansible.com/ui/repo/published/anatomicjc/passbolt/
* https://www.ansible.com/blog/introduction-to-ansible-builder/

View File

@ -2,7 +2,7 @@
remote_user = root
host_key_checking = False
roles_path = ./roles
inventory = ./inventories/servers
inventory = ./inventories/lab
gathering = smart
interpreter_python = auto_silent

View File

@ -1,10 +1,4 @@
collections:
- name: community.general
version: '>=9.0.0'
type: galaxy
- name: ansible.posix
version: '>=1.5.4'
type: galaxy
- name: ansible.utils
version: '>=4.1.0'
type: galaxy

83
context/Dockerfile Normal file
View File

@ -0,0 +1,83 @@
ARG EE_BASE_IMAGE="quay.io/ansible/ansible-runner:latest"
ARG PYCMD="/usr/bin/python3"
ARG PKGMGR_PRESERVE_CACHE=""
ARG ANSIBLE_GALAXY_CLI_COLLECTION_OPTS=""
ARG ANSIBLE_GALAXY_CLI_ROLE_OPTS=""
ARG PKGMGR="/usr/bin/dnf"
# Base build stage
FROM $EE_BASE_IMAGE as base
USER root
ENV PIP_BREAK_SYSTEM_PACKAGES=1
ARG EE_BASE_IMAGE
ARG PYCMD
ARG PKGMGR_PRESERVE_CACHE
ARG ANSIBLE_GALAXY_CLI_COLLECTION_OPTS
ARG ANSIBLE_GALAXY_CLI_ROLE_OPTS
ARG PKGMGR
COPY _build/scripts/ /output/scripts/
COPY _build/scripts/entrypoint /opt/builder/bin/entrypoint
RUN /output/scripts/pip_install $PYCMD
# Galaxy build stage
FROM base as galaxy
ARG EE_BASE_IMAGE
ARG PYCMD
ARG PKGMGR_PRESERVE_CACHE
ARG ANSIBLE_GALAXY_CLI_COLLECTION_OPTS
ARG ANSIBLE_GALAXY_CLI_ROLE_OPTS
ARG PKGMGR
RUN /output/scripts/check_galaxy
COPY _build /build
WORKDIR /build
RUN mkdir -p /usr/share/ansible
RUN ansible-galaxy role install $ANSIBLE_GALAXY_CLI_ROLE_OPTS -r requirements.yml --roles-path "/usr/share/ansible/roles"
RUN ANSIBLE_GALAXY_DISABLE_GPG_VERIFY=1 ansible-galaxy collection install $ANSIBLE_GALAXY_CLI_COLLECTION_OPTS -r requirements.yml --collections-path "/usr/share/ansible/collections"
# Builder build stage
FROM base as builder
ENV PIP_BREAK_SYSTEM_PACKAGES=1
WORKDIR /build
ARG EE_BASE_IMAGE
ARG PYCMD
ARG PKGMGR_PRESERVE_CACHE
ARG ANSIBLE_GALAXY_CLI_COLLECTION_OPTS
ARG ANSIBLE_GALAXY_CLI_ROLE_OPTS
ARG PKGMGR
RUN $PYCMD -m pip install --no-cache-dir bindep pyyaml packaging
COPY --from=galaxy /usr/share/ansible /usr/share/ansible
COPY _build/requirements.txt requirements.txt
RUN $PYCMD /output/scripts/introspect.py introspect --user-pip=requirements.txt --write-bindep=/tmp/src/bindep.txt --write-pip=/tmp/src/requirements.txt
RUN /output/scripts/assemble
# Final build stage
FROM base as final
ENV PIP_BREAK_SYSTEM_PACKAGES=1
ARG EE_BASE_IMAGE
ARG PYCMD
ARG PKGMGR_PRESERVE_CACHE
ARG ANSIBLE_GALAXY_CLI_COLLECTION_OPTS
ARG ANSIBLE_GALAXY_CLI_ROLE_OPTS
ARG PKGMGR
RUN /output/scripts/check_ansible $PYCMD
COPY --from=galaxy /usr/share/ansible /usr/share/ansible
COPY --from=builder /output/ /output/
RUN /output/scripts/install-from-bindep && rm -rf /output/wheels
RUN chmod ug+rw /etc/passwd
RUN mkdir -p /runner && chgrp 0 /runner && chmod -R ug+rwx /runner
WORKDIR /runner
RUN $PYCMD -m pip install --no-cache-dir 'dumb-init==1.2.5'
RUN rm -rf /output
LABEL ansible-execution-environment=true
USER 1000
ENTRYPOINT ["/opt/builder/bin/entrypoint", "dumb-init"]
CMD ["bash"]

View File

@ -0,0 +1,4 @@
version: 3
dependencies:
galaxy: collections/requirements.yml
python: requirements.txt

View File

@ -9,9 +9,7 @@ main_dns_server: ns1.verdnatura.es
ldap_uri: ldap://ldap.verdnatura.es
ldap_base: dc=verdnatura,dc=es
dc_net: "10.0.0.0/16"
resolv:
domain: verdnatura.es
search: verdnatura.es
resolv_domain: verdnatura.es
resolvers:
- '10.0.0.4'
- '10.0.0.5'

View File

@ -0,0 +1,5 @@
- name: First time host configuration
hosts: all
tasks:
- import_role:
name: debian-once

View File

@ -13,3 +13,7 @@
import_role:
name: debian-qemu
when: ansible_virtualization_role == 'guest' and ansible_virtualization_type == 'kvm'
- name: Configure virtual machine or host
import_role:
name: debian-host
when: ansible_virtualization_role == 'host' or ansible_virtualization_type == 'kvm'

View File

@ -1,10 +1,10 @@
- name: Fetch passbolt password
hosts: all
gather_facts: no
vars:
passbolt: 'anatomicjc.passbolt.passbolt'
passbolt_inventory: 'anatomicjc.passbolt.passbolt_inventory'
tasks:
- name: Print password
debug:
msg: "Variable: {{ lookup(passbolt, 'test') }}"
vars:
passbolt: 'anatomicjc.passbolt.passbolt'
passbolt_inventory: 'anatomicjc.passbolt.passbolt_inventory'

View File

@ -1,3 +1,2 @@
py-passbolt==0.0.18
cryptography==3.3.2
ansible==2.1.0

View File

@ -0,0 +1,5 @@
- name: Delete default user
user:
name: "{{ default_user }}"
state: absent
remove: yes

View File

@ -1,3 +1,5 @@
- import_tasks: defuser.yml
tags: defuser
- import_tasks: install.yml
tags: install
- import_tasks: locale.yml

View File

@ -1,9 +0,0 @@
- name: Delete default user
user:
name: "{{ default_user }}"
state: absent
remove: yes
- name: Change root password
user:
name: root
password: "{{ root_password | password_hash('sha512') }}"

View File

@ -0,0 +1,4 @@
vm.swappiness=10
vm.dirty_ratio=30
vm.dirty_background_ratio=5
net.core.somaxconn=65536

View File

@ -0,0 +1,7 @@
net.core.rmem_max=134217728
net.core.wmem_max=134217728
net.core.netdev_max_backlog=250000
net.ipv4.tcp_rmem=4096 87380 67108864
net.ipv4.tcp_wmem=4096 65536 67108864
net.ipv4.tcp_congestion_control=htcp
net.ipv4.tcp_mtu_probing=1

View File

@ -0,0 +1,3 @@
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6=1

View File

@ -0,0 +1,4 @@
- name: restart-sysctl
service:
name: systemd-sysctl
state: restarted

View File

@ -0,0 +1,12 @@
- name: Set the hostname in /etc/hostname
hostname:
name: "{{ inventory_hostname_short }}"
use: debian
- name: Configure hostname in hosts
blockinfile:
path: /etc/hosts
marker_begin: '--- BEGIN VN ---'
marker_end: '--- END VN ---'
marker: "# {mark}"
block: |
{{ ansible_default_ipv4.address }} {{ ansible_host }} {{ inventory_hostname_short }}

View File

@ -0,0 +1,4 @@
- import_tasks: hostname.yml
tags: hostname
- import_tasks: sysctl.yml
tags: sysctl

View File

@ -0,0 +1,9 @@
- name: Replace /etc/resolv.conf
template:
src: resolv.conf
dest: /etc/
owner: root
group: root
mode: '0644'
backup: true
when: resolv_enabled

View File

@ -0,0 +1,8 @@
- name: Set systctl configuration
copy:
src: sysctl/
dest: /etc/sysctl.d/
owner: root
group: root
mode: u=rw,g=r,o=r
notify: restart-sysctl

View File

@ -1,5 +1,5 @@
domain {{ resolv.domain }}
search {{ resolv.search }}
domain {{ resolv_domain }}
search {{ resolv_domain }}
{% if resolvers is defined %}
{% for resolver in resolvers %}
nameserver {{resolver}}

View File

@ -0,0 +1 @@
root_password: Pa$$w0rd

View File

@ -0,0 +1,4 @@
- import_tasks: ssh.yml
tags: ssh
- import_tasks: root.yml
tags: root

View File

@ -0,0 +1,14 @@
- name: Generate a random root password
set_fact:
root_password: "{{ lookup('password', '/dev/null length=18 chars=ascii_letters,digits') }}"
- name: Save the root password to a file
copy:
content: "{{ root_password }}\n"
dest: /root/root_password.txt
owner: root
group: root
mode: '0600'
- name: Change root password
user:
name: root
password: "{{ root_password | password_hash('sha512') }}"

View File

@ -0,0 +1,10 @@
- name: Delete old host SSH keys
file:
path: "{{ item }}"
state: absent
with_items:
- /etc/ssh/ssh_host_ecdsa_key
- /etc/ssh/ssh_host_ed25519_key
- /etc/ssh/ssh_host_rsa_key
- name: Regenerate host SSH keys
command: dpkg-reconfigure openssh-server

View File

@ -1,23 +0,0 @@
# https://docs.ansible.com/ansible/latest/collections/ansible/builtin/hostname_module.html#ansible-collections-ansible-builtin-hostname-module
- name: Set the hostname in /etc/hostname
ansible.builtin.hostname:
name: "{{ hostname }}"
use: debian
- name: Replace /etc/hosts
template:
src: hosts.j2
dest: /etc/hosts
owner: root
group: root
mode: '0644'
backup: true
- name: Replace /etc/resolv.conf
template:
src: resolv.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: '0644'
backup: true
when: resolv_enabled

View File

@ -1,5 +0,0 @@
{% if hosts is defined %}
{% for host in hosts %}
{{host.ip}} {{hostname}}
{% endfor %}
{% endif %}

127
roles/pve/files/nrpe/check_chrony Executable file
View File

@ -0,0 +1,127 @@
#!/usr/bin/env perl
#===============================================================================
# DESCRIPTION: Icinga2 / Nagios Check for chrony time sync status and offset
#
# OPTIONS: -h : Help
# -w [warning threshold in seconds]
# -c [critical threshold in seconds]
#
# REQUIREMENTS: Chrony, perl version 5.10.1+
#
# AUTHOR: Dennis Ullrich (request@decstasy.de)
#
# BUGS ETC: https://github.com/Decstasy/check_chrony
#
# LICENSE: GPL v3 (GNU General Public License, Version 3)
# see https://www.gnu.org/licenses/gpl-3.0.txt
#===============================================================================
use 5.10.1;
use strict;
use warnings;
use utf8;
use Getopt::Std;
#
# Variables
#
my $chronyDaemonName = "chronyd";
my $leapOk = "Normal";
my $rc = 3;
my $msg= "";
my $perfdata = "";
#
# Subroutines
#
sub help {
print "check_chrony [options]
-w [warning threshold in seconds]
-c [critical threshold in seconds]
e.g.: check_chrony -w 0.6 -c 2\n";
exit(3);
}
# Script exit with Nagios / Icinga typical output
sub _exit {
my ( $return, $line ) = @_;
my @state = ( "OK", "WARNING", "CRITICAL", "UNKNOWN" );
print "$state[$return]: $line\n";
exit( $return );
}
# Checks if a process with $_[0] as name exists
sub proc_exists {
my $PID = `ps -C $_[0] -o pid=`;
if ( ${^CHILD_ERROR_NATIVE} == 0 ){
return 1;
}
return 0;
}
#
# Options
#
my %options=();
getopts( "hw:c:", \%options );
# Check input
if ( keys %options == 0 || defined $options{h} ){
&help;
}
for my $key ( keys %options ){
if ( $options{$key} !~ /^[\d\.]+$/ ){
&_exit( 3, "Value of option -$key is not a valid number!" );
}
}
#
# Check chrony process
#
&_exit( 2, "$chronyDaemonName is not running!" ) if not &proc_exists( $chronyDaemonName );
#
# Get tracking data
#
my $chronyOutput = `chronyc tracking`;
&_exit( 3, "Chronyc tracking command failed!" ) if ${^CHILD_ERROR_NATIVE} != 0;
my ( $offset, $dir ) = $chronyOutput =~ /(?:System\stime)[^\d]+([\d\.]+)(?:.*?)(fast|slow)/;
my ( $leap ) = $chronyOutput =~ /(?:Leap)[^\:]+(?::\s+)([\w\h]+)/;
#
# Check stuff
#
# Check offset
if ( $offset >= $options{"c"} ){
$rc = 2; # Critical
}
elsif ( $offset >= $options{"w"} ){
$rc = 1; # Warning
}
else {
$rc = 0; # Ok
}
# Prepare offset performace data
$offset = $dir =~ "slow" ? "-$offset" : "+$offset";
$msg = sprintf( "Time offset of %+.9f seconds to reference.", $offset);
$perfdata = sprintf( "|offset=%.9fs;%.9f;%.9f", ${offset}, $options{'w'}, $options{'c'});
# Check leap
if( $leap !~ $leapOk ){
&_exit( 2, "Chrony leap status \"$leap\" is not equal to \"$leapOk\"! $msg $perfdata" );
}
#
# Return stuff
#
&_exit($rc, "$msg $perfdata");

View File

@ -0,0 +1,22 @@
#!/bin/bash
# Checks status of disks SMART
STATUS_LABEL="SMART Health Status:"
STATUS_OK="$STATUS_LABEL OK"
if [[ "$#" == "0" ]]; then
echo "Usage: $0 <disk1> [<disk2> ... <diskX>]"
exit
fi
for DISK in "$@"
do
STATUS=$(sudo /usr/sbin/smartctl -H -d scsi "$DISK" | grep "$STATUS_LABEL")
if [ "$STATUS" != "$STATUS_OK" ]; then
echo "CRITICAL: $DISK: $STATUS"
exit 2
fi
done
echo "OK: $STATUS_OK"

120
roles/pve/files/nrpe/check_zfs.pl Executable file
View File

@ -0,0 +1,120 @@
#!/usr/bin/perl
use strict;
use warnings;
use English;
$ENV{'PATH'} = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
use constant N_OK => 0;
use constant N_WARNING => 1;
use constant N_CRITICAL => 2;
use constant N_MSG => [ "OK", "WARNING", "CRITICAL" ];
my @zpool = ();
sub get_pools() {
local *P;
my $zpool_cmd = $EUID == 0 ? "zpool" : "sudo zpool";
open(P, $zpool_cmd . " list -H 2>&1 |") or &nagios_response("Could not find zpool command", N_CRITICAL);
while (<P>) {
chomp;
my @ret = split(/\s+/, $_);
push(@zpool, {
'name' => $ret[0],
'health' => $ret[-2],
'size' => $ret[1],
'alloc' => $ret[2],
'free' => $ret[3]
});
}
close(P);
my $rc = $?;
if ($rc != 0) {
&nagios_response("zpool list command failed (rc=$rc)", N_CRITICAL);
}
}
sub get_status()
{
my $storage = shift || "unknown";
my $cat = 0;
my $res = {};
local *P;
my $zpool_cmd = $EUID == 0 ? "zpool" : "sudo zpool";
open(P, $zpool_cmd . " status $storage 2>&1 |") or &nagios_response("Could not find zpool command", N_CRITICAL);
while (<P>) {
chomp;
if ($_ =~ /^\s*([^\s]+):\s*(.*)$/) {
$cat = $1;
$res->{"$cat"} = ();
if ($2) {
push(@{$res->{"$cat"}}, $2);
}
} elsif ($cat && $_ =~ /^\s+(.+)$/) {
push(@{$res->{"$cat"}}, $1);
}
}
close(P);
my $rc = $?;
if ($rc != 0) {
&nagios_response("zpool status command failed (rc=$rc)", N_CRITICAL);
}
return $res;
}
sub nagios_response()
{
my $msg = shift || "Unknown";
my $exit_status = shift;
if (!defined($exit_status)) {
$exit_status = N_CRITICAL;
}
printf("%s %s\n", N_MSG->[$exit_status], $msg);
exit($exit_status);
}
sub main() {
&get_pools();
my $exit_status = N_OK;
my @out = ();
foreach my $pool (@zpool) {
if ($pool->{'health'} eq 'DEGRADED') {
$exit_status = N_WARNING;
my $extinfo = &get_status($pool->{'name'});
my $scanned = 0;
my $total = 0;
my $speed = 0;
my $left = 0;
my $percent = 0;
my $resilvered = 0;
if (defined($extinfo->{'scan'})) {
foreach my $line (@{$extinfo->{'scan'}}) {
if ($line =~ /^\s*([^\s]+)\s+scanned out of\s+([^\s]+)\s+at\s+([^\s]+),\s*([^\s]+)\s+to go/) {
$scanned = $1;
$total = $2;
$speed = $3;
$left = $4;
} elsif ($line =~ /^\s*([^\s]+)\s+resilvered,\s*([^\s]+)\s+done/) {
$resilvered = $1;
$percent = $2;
}
}
}
if ($scanned && length($scanned) > 2) {
push(@out, sprintf("%s(RESILVER %s,%s,%s)", $pool->{'name'}, $percent, $speed, $left));
} else {
push(@out, sprintf("%s(%s %s/%s)", $pool->{'name'}, $pool->{'health'}, $pool->{'alloc'}, $pool->{'size'}));
}
} elsif ($pool->{'health'} ne 'ONLINE') {
$exit_status = N_WARNING;
push(@out, sprintf("%s(%s %s/%s)", $pool->{'name'}, $pool->{'health'}, $pool->{'alloc'}, $pool->{'size'}));
} else {
push(@out, sprintf("%s(%s %s/%s)", $pool->{'name'}, $pool->{'health'}, $pool->{'alloc'}, $pool->{'size'}));
}
}
&nagios_response(join(",", @out), $exit_status);
}
&main();

View File

@ -6,6 +6,14 @@
group: root
mode: u=rw,g=r,o=r
notify: restart-nrpe
- name: Copy PVE NRPE plugins
copy:
src: nrpe/
dest: /etc/nagios/plugins/
owner: root
group: root
mode: u=rwx,g=rx,o=rx
notify: restart-nrpe
- name: Add nagios to sudoers
copy:
src: sudoers
@ -13,6 +21,7 @@
mode: u=rw,g=r,o=
owner: root
group: root
notify: restart-nrpe
- name: Configure memory regions
copy:
src: vhost.conf

View File

@ -1,2 +1,3 @@
#!/bin/bash
export PYTHONPATH=./venv/lib/python3.12/site-packages/
ansible-playbook --vault-password-file .vaultpass $@