Tools

Postgresql

Testing HA

Source

Bitlbee

Inter Process Communication, Remote Procedure Calls

gRPC - message interchange format and interface definition language. Protocol Buffers - platform-neutral extensible mechanisms for serializing structured data, default for gRPC.

Identity Access Management

Keycloak

Network security

Tcpdump, Wireshark

Intrusion Detection, Prevention System (IDS, IPS)

Suricata

Snort

SysAdmin, Audit, Network, Security tools list

Containers and Docker

OCI (Open Container Initiative) low level, runtime specification and high level, image specification have standardized what was initially a mess.

Containerd, Docker, CRI-O, podman are high level since they implement at least parts of the image specification. Runc, Crun, gVisor, Firecracker implement the low level runtime specification that runs processes in the container.

Containerd architecture schema.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Docker CLI] [   Docker API   ] [    Build     ]
                                                 Integrated Lifecycle
[  Compose ] [Content Trust     [Authentication] Management
              and verification]

[ Security ] [    Network     ] [   Volumes    ] Docker

[ Containerd                  [RUNC]           ] Container runtime

------------------------------------------------ OCI

[       Linux         ][        Windows        ] OS

[       Hardware      ][        Cloud          ] Infrastructure

Runc is written in Go. Crun is an alternative that needs fewer resources and is favored by Red Hat, written in C.

Firecracker by AWS (Rust) has hardware enforced isolation via KVM. Google's gVisor has better Kubernetes and Docker integration, less overhead but less strict isolation (sandbox).

containerd offers a fully namespaced API so multiple consumers can all use a single containerd instance without conflicting with one another in a single daemon. To inspect container

1
2
3
ctr namespaces
ctr leases --help
ctr -n docker tasks

Command examples

Create a default OCI configuration.

1
runc spec && less config.json

Configure runc (enter namespace)

1
nsenter --help

Docker CLI reference

1
2
3
4
5
6
7
docker run --help
docker run hello-world
docker pull rockylinux/rockylinux:10-ubi-micro
docker images
docker history rockylinux/rockylinux:10-ubi-micro
docker inspect minikube
docker exec --tty minikube sh -c 'uname -a'

Run uv using docker

1
docker run --rm -it ghcr.io/astral-sh/uv:debian uv --help

Docker CLI comparable tools

Also compatible with docker-compose

alias docker=podman

1
podman

nerdctl, CLI for containerd.

Docker helper tools

Turn docker-compose files into flowcharts with docker-compose-viz-mermaid

1
docker run --rm -v $(PWD):/data derlin/docker-compose-viz-mermaid /data/docker-compose.yml -f png

Vulnerabilities, container escape

TeamsSix container escape check script. Deepce Docker Enumeration, Escalation of Privileges and Container Escapes

Genuine vulnerabilities exist but misconfiguration is a more likely way to escape a container.

First, figure out if you're in a container.

1
2
3
4
ls -la / | grep dockerenv
cat /proc/1/cgroup
env | grep -i kube
env | grep -i docker
--privileged container detection in the container

Non-privileged containers can't create network interfaces.

1
ip link add dummy0 type dummy

List disks, mount the host root file system and other nested ones as needed, chroot into the host.

1
2
3
4
5
6
7
8
fdisk -l

mount /dev/sda4 /mnt/hostroot
... snip ...

chroot /mnt/hostroot bash

id
1
uid=0(root) gid=0(root) groups=0(root)
Inspecting capabilities
1
2
capsh --print
cat /proc/self/status | grep Cap

Ansible

Configuration

Generate config defaults, all commented.

1
2
ansible-config init --disabled > ansible.cfg
ansible-config init --disabled -t all > ansible_full.cfg

Specify and install requirements.

1
2
ansible-galaxy install -r requirements.yml # roles
ansible-galaxy collection install -r requirements.yml # collections
1
2
3
4
5
6
7
8
9
10
11
#requirements.yml
---
roles:
  - src: https://my.scm.com/my-ansible-roles/role1.git
    scm: git
    version: master
    name: role1

collections:
# simple notation
  - community.libvirt

Specify requirements for roles in role1/meta/main.yml using the same notation as in requirements.yml

Miscellaneous uses

1
2
3
4
5
6
7
8
9
10
11
ansible-galaxy collection list
ansible-galaxy list
ansible-galaxy search
ansible-pull --only-if-changed --verify-commit site.yml
ansible-inventory -i inventory/ --list
# count changes
ansible-playbook site.yml | grep -oE "changed=*[0-9]" | cut -d '=' -f 2
# get guest vm status using community.libvirt.virt module
ansible localhost -m virt -a "name=vm_name command=status"
# quick fact overview
ansible --inventory inventory/ srv-web -m ansible.builtin.setup

Loops have the default loop_var item but that can be renamed in case of a conflict. loop_control has other uses and does not affect until.

1
2
3
4
5
6
7
community.digitalocean.digital_ocean:
  name: "{{ server }}"
  state: present
loop: "{{ servers }}"
loop_control:
  loop_var: server
  pause: 3

Lookup current loop_var:

1
"{{ lookup('vars', ansible_loop_var) }}"

Inspect:

1
{{ servers | type_debug }}
Test jinja2 template
1
2
ansible-playbook ansible_test_jinja2_template.yml --diff \
--extra-vars="@kvmlab/roles/web_server/defaults/main.yml"
1
2
3
4
5
6
7
8
# playbook to test jinja2 template
---
- hosts: 127.0.0.1
  tasks:
  - name: Test jinja2template
    template:
      src: "kvmlab/roles/web_server/templates/nginx.conf.j2"
      dest: "test.conf"

Playbooks

1
2
3
4
5
ansible-playbook --syntax-check kvm_provision.yml
yamllint -d relaxed kvm_provision.yml
ansible-lint kvm_provision.yml # recursive check descending to roles
ansible-playbook --ask-become-pass kvm_provision.yml --extra-vars vm=web01
ansible-playbook -K kvm_provision.yml -e vm=web01 -e net=br0

Ansible-playbook error YAML parsing failed: Colons in unquoted values must be followed by a non-space character.

is likely caused by an indentation error.

Playbooks contain plays as top level elements, for using roles, tasks keywords.

1
2
3
4
5
6
7
8
9
10
11
12
# site.yml
---
- name: Prepare KVM host
  hosts: kvm # ansible group name from inventory
  gather_facts: yes
  become: yes
  roles:
    - kvm_host
  post_tasks:
    - name: Print ansible_hostname
      ansible.builtin.debug:
        var: ansible_hostname

Ansible loads inventory sources in the order you supply them. It defines hosts, groups, and variables as it encounters them in the source files, adding the all and ungrouped groups at the end if needed.

1
2
3
4
# inventory/test.yml
# Keep the inventory ordering!
kvm:
  hosts: localhost
1
ansible-playbook -K -i inventory/ site.yml

Modules

List of modules that only run when source changed:

List of modules that ensure state only when parameter state is used:

Roles

Create your new role:

1
ansible-galaxy role init kvm_provision

Variable precedence

Include role conditionally:
In a task
1
2
3
4
5
6
# roles/webapp/tasks/main.yml
---
- name: Include monitoring role conditionally
  ansible.builtin.include_role:
    name: monitoring
  when: webapp_enable_monitoring | default(true) | bool
In meta/main.yml using a feature flag
1
2
3
4
5
# roles/webapp/meta/main.yml
dependencies:
  - role: monitoring
    vars:
      monitoring_enabled: "{{ webapp_enable_monitoring | default(true) }}"
1
2
3
4
5
6
7
8
# roles/monitoring/tasks/main.yml
# Skip all tasks if monitoring is disabled
---
- name: Install monitoring agent
  ansible.builtin.dnf:
    name: monitoring-agent
    state: present
  when: monitoring_enabled | bool

Documentation using docsible

1
docsible --role roles/kvm_provision/ --playbook kvm_provision.yml --no-backup --graph

Secrets

Debug output can also include secret information despite no_log settings being enabled. Put the encrypt_string result in a vars file like vault.yml containing secrets to see clearly which secrets are which. Add vault.yml to .gitignore

1
2
3
ansible-vault create secrets_file.enc # no secret name recorded
ansible-vault encrypt_string 'supersecret1' --name 'vault_root_pass' >> vault.yml
ansible-playbook -i inventory.ini -e @vault.yml --vault-password-file password_file kvm_provision.yml

Example lookup from Hashicorp Vault

1
2
3
4
- name: Ensure API key is present in config file
      ansible.builtin.lineinfile:
        path: /etc/app/configuration.ini
        line: "API_KEY={{ lookup('hashi_vault', 'secret=config-secrets/data/app/api-key:data token=s.FOmpGEHjzSdxGixLNi0AkdA7 url=http://localhost:8201')['key'] }}"

Adding tags

Special reserved tags are always, never, tagged, untagged and all. Both always and never are used for tagging, others for selecting which tags to run or skip.

1
2
ansible-playbook example.yml --list-tags
ansible-playbook example.yml --tags "configuration,packages" --list-tasks
Tag inheritance

Define the tags at the level of your play or block, or when you add a role or import a file. Ansible applies the tags down the dependency chain to all child tasks. Fact gathering is an implicit task tagged with always so that runs in addition to tagged tasks by default.