15 Commits
1.0.0 ... 2.1.1

17 changed files with 114 additions and 196 deletions

View File

@ -2,6 +2,7 @@
exclude_paths:
- molecule/
- .pre-commit-config.yaml
- .github/
parseable: true

View File

@ -10,6 +10,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: galaxy
uses: robertdebock/galaxy-action@1.1.1
uses: robertdebock/galaxy-action@1.2.1
with:
galaxy_api_key: ${{ secrets.galaxy_api_key }}
git_branch: main

View File

@ -8,43 +8,75 @@ on: # yamllint disable-line rule:truthy
pull_request:
jobs:
setup:
name: Setup scenarios matrix
runs-on: ubuntu-22.04
outputs:
scenarios: ${{ steps.matrix.outputs.scenarios }}
steps:
- uses: actions/checkout@v3
- id: matrix
run: |
JSON="["
for s in $(find molecule -mindepth 1 -maxdepth 1 -type d -exec basename "{}" \;); do
JSON="${JSON}\"${s}\","
done
JSON="${JSON%?}"
JSON="$JSON]"
echo "::set-output name=scenarios::$(echo $JSON)"
lint:
runs-on: ubuntu-20.04
name: Lint
needs:
- setup
runs-on: ubuntu-22.04
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
path: "${{ github.repository }}"
- name: molecule
uses: robertdebock/molecule-action@2.6.17
uses: robertdebock/molecule-action@4.0.9
with:
command: lint
scenario: ${{ fromJson(needs.setup.outputs.scenarios)[0] }}
test:
name: Scenario "${{ matrix.scenario }}" on ${{ matrix.config.image }}:${{ matrix.config.tag }}
needs:
- lint
- setup
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
scenario: ${{ fromJson(needs.setup.outputs.scenarios) }}
config:
- image: "amazonlinux"
tag: "latest"
- image: "centos"
tag: "8"
- name: "redhat"
image: "registry.access.redhat.com/ubi8/ubi"
tag: "latest"
- image: "debian"
tag: "11"
- image: "debian"
tag: "10"
- image: "ubuntu"
tag: "20.04"
tag: "22.04"
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
path: "${{ github.repository }}"
- name: molecule
uses: robertdebock/molecule-action@2.6.17
uses: robertdebock/molecule-action@4.0.9
with:
image: ${{ matrix.config.image }}
tag: ${{ matrix.config.tag }}
scenario: ${{ matrix.scenario }}
env:
name: ${{ matrix.config.name }}

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
###VSCode###
.vscode
###MacOS###
.DS_Store

View File

@ -9,14 +9,15 @@
> :star: Star us on GitHub — it motivates us a lot!
Install and configure dynamic MOTD and SSH banner
Install and configure dynamic MOTD
This role uses [https://github.com/claranet/motd](https://github.com/claranet/motd) by default to get the MOTD
```
System info:
Hostname·········: claranet_motd_ubuntu-20.04
Distro···········: Ubuntu 20.04.3 LTS
Kernel···········: Linux 5.10.47-linuxkit
Updates available: 6 (2 security)
Uptime···········: up 2 days, 23 hours, 18 minutes
Load·············: 1.33 (1m), 0.43 (5m), 0.20 (15m)
Processes········: 13 (root), 3 (user), 16 (total)
@ -34,7 +35,7 @@ Ansible:
## :warning: Requirements
Ansible >= 2.9
Ansible >= 2.10
## :zap: Installation
@ -44,11 +45,14 @@ ansible-galaxy install claranet.motd
## :gear: Role variables
Variable | Default value | Description
--------------------------|-----------------------|----------------------------------------
motd_disable_default_motd | true | Disable system default MOTD (/etc/motd)
motd_banner_template | etc/banner | SSH banner template
motd_template | usr/local/bin/dynmotd | Dynmaic MOTD template
Variable | Default value | Description
-----------------------------|------------------------------------------------------------------------|----------------------------------------------------------------
motd_disable_default_motd | true | Disable system default MOTD (/etc/motd)
motd_template | https://raw.githubusercontent.com/claranet/motd/master/scripts/00-basic| Dynmaic MOTD template<br>Can be a URL or a local template
motd_template_prepend | "" | Prepend raw content to `motd_template`
motd_template_append | See [defaults/main.yml](defaults/main.yml) | Append raw content to `motd_template`
motd_template_username | "" | Used when `motd_template` is an URL
motd_template_password | "" | Used when `motd_template` is an URL
## :pencil2: Example Playbook
@ -56,7 +60,8 @@ motd_template | usr/local/bin/dynmotd | Dynmaic MOTD template
---
- hosts: all
roles:
- claranet.motd
- role: claranet.motd
motd_template: "{{ playbook_dir }}/templates/dynmotd.j2"
```
## :closed_lock_with_key: [Hardening](HARDENING.md)

View File

@ -1,4 +1,12 @@
---
motd_disable_default_motd: true
motd_banner_template: etc/banner
motd_template: usr/local/bin/dynmotd
motd_template: https://raw.githubusercontent.com/claranet/motd/master/scripts/00-basic
motd_template_prepend: ""
motd_template_append: |
echo -e "\e[0;39mAnsible:
Last deployment\e[2m···\e[0m: {{ ansible_date_time.iso8601_micro }}
"
motd_template_username: ""
motd_template_password: ""

View File

@ -1,4 +1,4 @@
- name: Restart sshd
service:
ansible.builtin.service:
name: sshd
state: restarted

View File

@ -7,7 +7,7 @@ galaxy_info:
license: "MPL2"
min_ansible_version: 2.9
min_ansible_version: "2.10"
galaxy_tags: ['claranet', 'motd', 'system', 'dynmotd', 'dynamic']

View File

@ -14,8 +14,11 @@ ENV {{ var }} {{ value }}
{% endfor %}
{% endif %}
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python3 sudo bash ca-certificates iproute2 systemd && apt-get clean; \
elif [ $(command -v yum) ]; then yum install -y python3 sudo bash iproute systemd firewalld initscripts; \
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 && zypper clean -a; \
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 && xbps-remove -O; fi
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python3 sudo bash ca-certificates iproute2 systemd wget && apt-get clean; \
elif [ $(command -v yum) ]; then yum install -y python3 sudo bash iproute systemd initscripts wget; \
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml iproute2 wget && zypper clean -a; \
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates wget; \
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates iproute2 wget && xbps-remove -O; fi
RUN wget -O /usr/bin/systemctl https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl3.py && \
chmod +x /usr/bin/systemctl

View File

@ -3,7 +3,6 @@
hosts: all
roles:
- role: claranet.motd
motd_banner_template: null
pre_tasks:
- name: "Update APT cache"

View File

@ -6,7 +6,9 @@ driver:
name: docker
platforms:
- name: claranet_motd_${image:-debian}-${tag:-latest}
# On "${name:-${image:-debian}", the last brace does not miss, it works like that...
# The env var "name" should be used when the image variable refers to a full registry path like registry.access.redhat.com/ubi8/ubi
- name: claranet_motd_${name:-${image:-debian}-${tag:-latest}-${scenario:-default}
image: ${image:-debian}:${tag:-latest}
privileged: true
tty: true

View File

@ -12,11 +12,6 @@ motd_file_path = "/usr/local/bin/dynmotd"
pam_line = f"session optional pam_exec.so type=open_session stdout {motd_file_path}"
def test_banner_file(host):
file = host.file("/etc/banner")
assert not file.exists
def test_motd_file(host):
file = host.file(motd_file_path)
assert file.exists

View File

@ -0,0 +1,22 @@
---
- name: disable_default_motd | Get stats of {{ _motd_sshd_config_file_path }}
ansible.builtin.stat:
path: "{{ _motd_sshd_config_file_path }}"
register: _motd_sshd_config_file_stat
- name: disable_default_motd | Ensure PrintMotd is set to "no" in {{ _motd_sshd_config_file_path }}
ansible.builtin.lineinfile:
path: "{{ _motd_sshd_config_file_path }}"
regexp: "^PrintMotd "
line: PrintMotd no
when: _motd_sshd_config_file_stat.stat.exists
- name: disable_default_motd | Comment out pam_motd in pam
ansible.builtin.replace:
path: "{{ item }}"
regexp: '^(session\s+optional\s+pam_motd.so\s+.*)'
replace: '# \1'
loop:
- "{{ _motd_pam_login_file_path }}"
- "{{ _motd_pam_sshd_file_path }}"
...

View File

@ -1,47 +1,11 @@
---
- block:
- name: Get stats of {{ _motd_sshd_config_file_path }}
stat:
path: "{{ _motd_sshd_config_file_path }}"
register: _motd_sshd_config_file_stat
- name: Ensure PrintMotd is set to "no" in {{ _motd_sshd_config_file_path }}
lineinfile:
path: "{{ _motd_sshd_config_file_path }}"
regexp: "^PrintMotd "
line: PrintMotd no
when: _motd_sshd_config_file_stat.stat.exists
- name: Comment out pam_motd in pam
replace:
path: "{{ item }}"
regexp: '^(session\s+optional\s+pam_motd.so\s+.*)'
replace: '# \1'
loop:
- "{{ _motd_pam_login_file_path }}"
- "{{ _motd_pam_sshd_file_path }}"
- name: Include disable_default_motd.yml if motd_disable_default_motd is True
ansible.builtin.include_tasks: disable_default_motd.yml
when: motd_disable_default_motd|bool
- name: Apply template {{ motd_banner_template }} on {{ _motd_banner_file_path }}
template:
src: "{{ motd_banner_template }}"
dest: "{{ _motd_banner_file_path }}"
owner: root
group: root
mode: '0644'
when: motd_banner_template
- name: Ensure line "Banner {{ _motd_banner_file_path }}" is {{ _motd_banner_state }} in {{ _motd_sshd_config_file_path }}
lineinfile:
path: "{{ _motd_sshd_config_file_path }}"
regexp: "^(#?)Banner "
line: Banner {{ _motd_banner_file_path }}
state: "{{ _motd_banner_state }}"
notify: Restart sshd
- name: Apply template {{ motd_template }} on {{ _motd_file_path }}
template:
src: "{{ motd_template }}"
- name: configure_motd | Apply template {{ motd_template }} on {{ _motd_file_path }}
ansible.builtin.copy:
content: "{{ motd_template_prepend + _motd_template_content + motd_template_append }}"
dest: "{{ _motd_file_path }}"
owner: root
group: root
@ -49,10 +13,11 @@
tags:
- molecule-idempotence-notest
- name: Add pam_exec {{ _motd_file_path }} in pam
lineinfile:
- name: configure_motd | Add pam_exec {{ _motd_file_path }} in pam
ansible.builtin.lineinfile:
path: "{{ item }}"
line: session optional pam_exec.so type=open_session stdout {{ _motd_file_path }}
loop:
- "{{ _motd_pam_login_file_path }}"
- "{{ _motd_pam_sshd_file_path }}"
...

View File

@ -1,32 +0,0 @@
,╓╗╖æææææ╖╖,
,╗╣╬╬╣╝╜╙╙"╙╙╜╝╣Ñ╬æ╖
╓╣╬╝╜ "╙╣╬╣µ
╓╬╬M "╢╬╣,
╣╬╝ ╣Ñæ ╙╬╬╖╖╖╖╖╓,
,,╓╣╬M ,╗ ╬╬╬ ╓╖, ╝╜╜╜╜╝╣╬╬╣╖
╓æ╣Ñ╬╣╝╝╝ ╓╣╬╝ ╬╬╬ ²╣╬╣µ `╙╣╬æ,
╓╣╬╝^ ╣╬╝ ╬╬╬ `╣╬æ ╙╬╬φ
╣╬M ║╬╬ ╢╣M ╬╬U ╢╬φ
╬╬M ║╬╬ ╬╬M ╬╬∩
║╬╬ "╬ѵ ╓Ñ╬ ╟╬┼
╟╬┼ `╣╬╖, ,╢╬╣ ╟╬┼
║╬╬ ╙╢╬╬╣ææ╬╣╬╬╝` ╬╬M
╬╬µ `"""` ╣╬M
`╬╬φ ╓╬╬M
╚╬╬╖, ,╗╣╬╝
╙╣Ñ╬╣æ╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖╖æ╣╬╬╝╜
`"╙╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╜╙" ®
╖╖┬ ╓╖╖
╬╬M ╓╬╬
,╗╣╬╬╣æ, ╬╬M ,æ╣╬╬╬æ╖ ß╣ß╓æ╣ ╓æ╣╬╬╣æ, -╣╣╓æ╣╬╣╖ ,╖╣╬╬╣╖, ╢╣╬╬╣╣
║╬╬" `╝╜* ╬╬M ╝╝┘ ╟╬φ ╬╬╬M"` ª╝╝ ╙╬Ñ "╬╬M ║╬╬ ╢╬M ╢╬φ ╓╬╬
-╬╬H ╬╬M ╓æ╣╬╬╣╬╬M ╬╬╬ ╗╢╣╬╣╣╬╬⌐ "╬╬ ╓╬╬ ╬╬╬╬╬╬╬╬╬╬ ╓╬╬
╢╬φ ╓,, ╬╬M ╢╬M ╟╬M ╬╬╪ ╬╬M ,╬╬⌐ "╬╬ ╓╬╬ ╬╬φ ╓ ╓╬╬
╚╬╬╣╣╣╬╝ ╬╬M ╙╣╬╣æ╣╬╬╬M ╬╬╪ ║╬╬ææ╣╣╬╬⌐ "╬╬ ╓╬╬ ╚╬╬╣╣╣╬╣╜ '╬╬╣æ
`` `` ``` ``` ```
WARNING : Unauthorized access to this system is forbidden and will be
prosecuted by law. By accessing this system, you agree that your actions
may be monitored if unauthorized usage is suspected.

View File

@ -1,85 +0,0 @@
#!/bin/bash
# get load averages
IFS=" " read LOAD1 LOAD5 LOAD15 <<<$(awk '{ print $1,$2,$3 }' /proc/loadavg)
# get free memory
IFS=" " read USED AVAIL TOTAL <<<$(free -htm | grep "Mem" | awk {'print $3,$7,$2'})
# get processes
PROCESS=$(ps -eo user=|sort|uniq -c | awk '{ print $2 " " $1 }')
PROCESS_ALL=$(echo "$PROCESS"| awk {'print $2'} | awk '{ SUM += $1} END { print SUM }')
PROCESS_ROOT=$(echo "$PROCESS"| grep root | awk {'print $2'})
PROCESS_USER=$(echo "$PROCESS"| grep -v root | awk {'print $2'} | awk '{ SUM += $1} END { print SUM }')
# get processors
PROCESSOR_NAME=$(grep "model name" /proc/cpuinfo | cut -d ' ' -f3- | awk {'print $0'} | head -1)
PROCESSOR_COUNT=$(grep -ioP 'processor\t:' /proc/cpuinfo | wc -l)
LOCAL_IP_ADDRESSES=$(hostname -I | sed 's/ /\n\ /g' | sed '/^[[:space:]]*$/d')
# Processing available updates with yum is too long
{% if ansible_pkg_mgr == "apt" %}
UPDATES_AVAILABLE=$(apt list --upgradable 2>/dev/null)
UPDATES_AVAILABLE_NB=$(echo -e "$UPDATES_AVAILABLE" | wc -l)
SECURITY_UPDATES_AVAILABLE_NB=$(echo -e "$UPDATES_AVAILABLE" | grep "\-security" | wc -l)
{% endif %}
# colors
W="\e[0;39m"
G="\e[1;32m"
R="\e[1;31m"
dim="\e[2m"
undim="\e[0m"
echo -e "${W}System info:
$W Hostname$dim·········$undim: $W${HOSTNAME}
$W Distro$dim···········$undim: $W$(grep "PRETTY_NAME" /etc/*release | cut -d "=" -f 2- | sed 's/"//g')
$W Kernel$dim···········$undim: $W$(uname -sr)
{% if ansible_pkg_mgr == "apt" %}
$W Updates available: $W$UPDATES_AVAILABLE_NB ($SECURITY_UPDATES_AVAILABLE_NB security)
{% endif %}
$W Uptime$dim···········$undim: $W$(uptime -p)
$W Load$dim·············$undim: $G$LOAD1$W (1m), $G$LOAD5$W (5m), $G$LOAD15$W (15m)
$W Processes$dim········$undim: $G$PROCESS_ROOT$W (root), $G$PROCESS_USER$W (user), $G$PROCESS_ALL$W (total)
$W CPU$dim··············$undim: $W$PROCESSOR_NAME ($G$PROCESSOR_COUNT$W vCPU)
$W Memory$dim···········$undim: $G$USED$W used, $G$AVAIL$W avail, $G$TOTAL$W total
$W Local IPs$dim········$undim: $LOCAL_IP_ADDRESSES$W"
# config
max_usage=90
bar_width=50
# disk usage: ignore zfs, squashfs & tmpfs
mapfile -t dfs < <(df -H -x zfs -x squashfs -x tmpfs -x devtmpfs -x overlay --output=target,pcent,size | tail -n+2)
printf "\nDisk usage:\n"
for line in "${dfs[@]}"; do
# get disk usage
usage=$(echo "$line" | awk '{print $2}' | sed 's/%//')
used_width=$((($usage*$bar_width)/100))
# color is green if usage < max_usage, else red
if [ "${usage}" -ge "${max_usage}" ]; then
color=$R
else
color=$G
fi
# print green/red bar until used_width
bar="[${color}"
for ((i=0; i<$used_width; i++)); do
bar+="="
done
# print dimmmed bar until end
bar+="${W}${dim}"
for ((i=$used_width; i<$bar_width; i++)); do
bar+="·"
done
bar+="${undim}]"
# print usage line & bar
echo "${line}" | awk '{ printf("%-31s%+3s used out of %+4s\n", $1, $2, $3); }' | sed -e 's/^/ /'
echo -e "${bar}" | sed -e 's/^/ /'
done
printf "\n"
echo -e "${W}Ansible:
${W} Last deployment$dim···$undim: {{ ansible_date_time.iso8601_micro }}
"

View File

@ -2,6 +2,6 @@
_motd_sshd_config_file_path: /etc/ssh/sshd_config
_motd_pam_login_file_path: /etc/pam.d/login
_motd_pam_sshd_file_path: /etc/pam.d/sshd
_motd_banner_file_path: /etc/banner
_motd_banner_state: "{{ 'present' if motd_banner_template else 'absent' }}"
_motd_file_path: /usr/local/bin/dynmotd
_motd_lookup_method: "{{ 'url' if motd_template is match('http(s)?:\/\/') else 'template' }}"
_motd_template_content: "{{ lookup(_motd_lookup_method, motd_template, split_lines=False, username=motd_template_username, password=motd_template_password) }}"