593 private links
With Python, you can just open a shell and quickly try out some code in a so-called REPL console. Guess what, you can do the same with Ansible. Browse your inventory, and even remote file systems in an interactive shell with
ansible-console
.
If you have installed Ansible via pip you already have this nifty tool installed.
I have invested some effort into Keycloak, but still not learnt enough about it to manage more than a single-user scenario.
The Authentik licenses look a bit complicated, though?
Via https://old.reddit.com/r/selfhosted/comments/1eohsds/best_self_hosted_authentication_solution
Thanks to git log -1
it is trivial to view the latest commit in a repo, and combined with find ... -exec ...
we can easily do that for multiple repos at once (for example see my commits
bash function).
Turns out doing the same for the earliest commit in each repo is not at all trivial. Here is my approach (it should be said up-front that it's not pretty). But I wanted to get a handle on when I started working on some repos, and this pulled out the data I needed without having to do it manually for each individual repo.
taha@asks2:/media/bay/taha/projects/ansible/pub/roles
$ find . -maxdepth 2 -name ".git" -exec sh -c "git -C {} --no-pager log --all --pretty=format:\"%cs %h%d %s [%cn]\" | tail -n1" \; -exec printf " %s\n" {} \; | sort -n
2021-03-14 02506d4 Initial commit [taha] ./texlive/.git
2021-03-14 14ddd9a Initial commit [taha] ./desktop-tools/.git
2021-03-14 2bfe5bc Initial commit [taha] ./R/.git
2021-03-15 0c2deda Initial commit [taha] ./calibre-web/.git
2021-04-02 7a2bdb2 Initial commit [taha] ./iriun-webcam/.git
2021-04-13 167ef2d First commit [taha@asks2] ./shaarli/.git
2021-05-28 ca70e06 Initial commit [taha] ./wallabag/.git
2021-06-09 3d4891e Initial commit [taha] ./lxd-server/.git
The directory roles
contains a bunch of git repos (and possibly also a few folders that are not repos, which we exclude from find with -name ".git"
. Then for each repo, we list all commits (across all branches) using a custom format but then, importantly, only showing the last one which is the earliest commit.
To be able to tail this git command (and not the entire find command) we need to put it inside sh -c ...
.
Then we use a second exec statement to append the current directory name (so we can orient ourselves in the output) and then sort the entirety on the output (which thanks to our custom git format lists the commit date in the first column). Note that piping strips the colour from the git output, so no need to set colours in our pretty-format
.
- https://stackoverflow.com/questions/51376361/getting-commit-history-from-multiple-repositories-for-some-period
- https://stackoverflow.com/questions/5188914/how-to-show-the-first-commit-by-git-log (this approach with
git rev-list --max-parents
did not work in my case, not sure why) - https://andrewrea.co.uk/posts/git-log-over-multiple-repos
- https://stackoverflow.com/questions/50882822/view-logs-of-multiple-git-repositories
- https://stackoverflow.com/questions/6712423/how-do-i-count-the-number-of-commits-made-in-to-all-repos-hosted-by-gitosis-on-m
- https://stackoverflow.com/questions/49388069/what-does-git-log-1-do
- https://stackoverflow.com/questions/307015/how-do-i-include-a-pipe-in-my-linux-find-exec-command
- https://stackoverflow.com/questions/1371261/get-current-directory-or-folder-name-without-the-full-path
Let's say you are looking for old Ansible code dealing with multiple distros (meaning multiple files in the ./vars/
directory) to draw inspiration from. How can we quickly identify the roles with vars
directories containing more than a single file?
$ cd /media/bay/taha/projects/ansible && find . -not -path "*/archived/*" -not -path "*/testing/*" -path "*/vars/*" -printf "%h\n" | sort | uniq -d && cd $OLDPWD
./pub/roles/apache/vars
./pub/roles/common-systools/vars
./pub/roles/desktop-environment/vars
./pub/roles/digikam/vars
./pub/roles/graphics-driver-nvidia/vars
./pub/roles/php/vars
./pub/roles/php-versions/vars
./pub/roles/python2/vars
./pub/roles/python3/vars
./pub/roles/R/vars
./pub/roles/sioyek-pdf/vars
./pub/roles/wallabag/vars
All of the returned paths indeed contain at least two files.
Note how we limited find
to only directories named vars
.
This is a one-liner that I find useful when deciding a free IPv4 address to assign a new container:
$ lxc network list-leases lxdbr0 | grep STATIC | sort -t "." -n -k4
| taipei | 00:16:3e:9c:dd:28 | 10.252.116.3 | STATIC |
| hunan | 00:16:3e:f7:4f:b2 | 10.252.116.4 | STATIC |
| xian | 00:16:3e:b0:54:12 | 10.252.116.5 | STATIC |
| samarkand | 00:16:3e:e8:79:60 | 10.252.116.6 | STATIC |
| karachi | 00:16:3e:e5:4c:47 | 10.252.116.7 | STATIC |
| lahore | 00:16:3e:f1:73:ab | 10.252.116.8 | STATIC |
| muscat | 00:16:3e:9a:89:05 | 10.252.116.9 | STATIC |
| antioch | 00:16:3e:92:79:ca | 10.252.116.11 | STATIC |
| delhi | 00:16:3e:22:9c:d0 | 10.252.116.13 | STATIC |
| bahrain | 00:16:3e:4b:b6:ee | 10.252.116.14 | STATIC |
| lusail | 00:16:3e:fd:1f:49 | 10.252.116.15 | STATIC |
| pergamon | 00:16:3e:0e:9c:82 | 10.252.116.16 | STATIC |
The one-liner is simplified by the fact that only the IP address field contains dots, so we can use them as delimiters for sort
without first having to sort
on the column delimiters.
WireHole is a combination of WireGuard, Pi-hole, and Unbound in a docker-compose project with the intent of enabling users to quickly and easily create a personally managed full or split-tunnel WireGuard VPN with ad blocking capabilities thanks to Pi-hole, and DNS caching, additional privacy options, and upstream providers via Unbound.
Interesting... Via Awesome Wireguard.
With frequent changes to my Ansible roles it often becomes tricky to keep track of which version of a particular role was executed for a particular play.
This is not thoroughly tested yet, but my approach is simple: each role contains a task that writes its git repo state (a git log formatted one-liner containing last commit hash, date, author, etc.).
These tasks are set to write to a log-file per inventory host, which is for two reasons: to avoid drowning in the default log file defined by log_path
in ansible.cfg
, and because log_path
cannot be overridden by a play or role.
This results in a log-file (one per inventory host) looking something like this:
Started playbook execution at 2024-02-22 12:34:32.327042
Role 'locales' last commit caa6355 2024-02-21 23:38:31 +0100 by solarchemist
Role 'digikam' last commit 16f0643 2024-02-21 22:50:49 +0100 by solarchemist
Playbook 'workstation' last commit 4775cf7 2024-02-20 22:53:56 +0100 by solarchemist
Ended playbook execution at 2024-02-22 12:34:55.913856
My implementation illustrated in code below.
In a role:
- name: Log the last commit and git repo status to playbook log-file
local_action: >
shell git -C {{ role_path }} log
--pretty="Role '{{ role_name }}' last commit %h %ci by %cn" -1 >>
logbook-{{ inventory_hostname }}.log
args: { chdir: "{{ playbook_dir }}" }
become: true
become_user: "{{ local_user }}"
In the playbook:
vars:
local_user: "{{ lookup('env', 'USER') }}"
pre_tasks:
- name: Write a start message to the playbook log
ansible.builtin.shell: >
printf "\nStarted playbook execution at {{ now() }}\n" >> logbook-{{ inventory_hostname }}.log
run_once: true
delegate_to: localhost
args: { chdir: "{{ playbook_dir }}" }
become: true
become_user: "{{ local_user }}"
changed_when: true
tags: always
tasks: [...]
post_tasks:
- name: Log the current commit of this playbook
local_action: >
shell git log
--pretty="Playbook '{{ playbook_dir | basename }}' last commit %h %ci by %cn" -1
>> logbook-{{ inventory_hostname }}.log
args: { chdir: "{{ playbook_dir }}" }
become: true
become_user: "{{ local_user }}"
tags: always
- name: Write an end message to the playbook log
ansible.builtin.shell: >
echo "Ended playbook execution at {{ now() }}" >> logbook-{{ inventory_hostname }}.log
run_once: true
delegate_to: localhost
args: { chdir: "{{ playbook_dir }}" }
become: true
become_user: "{{ local_user }}"
changed_when: true
tags: always
This is an extension of the very common find ... -exec grep ... {} \;
construct I use almost daily to find which files contain a particular text string.
Now, let's say you're looking for files in all your Ansible roles containing the string ppa:
, because you want to create a new role using a suitable existing role as a template. In this case, I think most recently modified is an excellent proxy for suitability.
Thus, the challenge: can we tack on something to the find ... grep
construct such that the output shows matching files in order of most recently modified?
taha@asks2:~
$ cd /media/bay/taha/projects/ansible && find . -not -path '*/legacy/*' -type f -name "*.yml" -exec grep -il "ppa:" {} \; -printf "%T+ %p\n" | grep -v "^\\./.*" | sort && cd $OLDPWD
2021-06-09+22:27:20.6889730070 ./roles/public/php-versions/tasks/setup-Debian.yml
2022-01-09+07:39:51.1836426130 ./roles/public/java-openjdk/tasks/ppa.yml
2022-02-23+15:33:13.2318546100 ./roles/dev/deluge/tasks/install.yml
2022-03-31+04:42:16.5644336650 ./roles/dev/editor-notepadqq/tasks/main.yml
2022-05-09+15:21:01.7974094830 ./roles/dev/qownnotes/tasks/main.yml
2022-06-25+02:46:13.0580097480 ./playbooks/workstation/roles/boot-grub/tasks/main.yml
2022-06-25+02:46:18.2940273650 ./roles/dev/libreoffice/tasks/main.yml
2022-07-03+00:16:44.5563131800 ./roles/dev/shutter/tasks/main.yml
2022-07-04+20:35:15.6128270470 ./roles/dev/magnus/tasks/main.yml
2022-07-14+21:09:01.6502524570 ./roles/dev/flatpak-remote/tasks/main.yml
2022-07-14+22:05:43.3325667290 ./roles/public/firejail/tasks/install.yml
2022-07-16+13:42:04.3756138100 ./roles/public/variety/tasks/main.yml
2022-07-16+22:05:52.0552035060 ./roles/public/browser-chromium/tasks/install.yml
2022-07-21+00:14:51.1137716920 ./roles/public/foliate-ebookreader/tasks/ppa.yml
2022-07-21+03:07:42.2876610030 ./roles/public/graphics-driver-nvidia/tasks/install.yml
2022-07-21+06:05:27.4514643180 ./roles/public/foliate-ebookreader/tasks/flatpak.yml
2022-07-23+18:32:46.8466638700 ./roles/dev/handbrake/tasks/main.yml
2022-08-12+00:09:51.6575729520 ./roles/dev/x2goclient/tasks/main.yml
2022-08-19+15:28:49.5605481840 ./roles/dev/x2goserver/tasks/main.yml
2022-11-04+11:35:14.6208169990 ./roles/public/python3/tasks/python-ppa.yml
2022-11-19+03:16:16.7832183030 ./roles/public/browser-firefox/tasks/main.yml
2022-12-24+23:04:01.2033026010 ./roles/public/R/tasks/dependencies.yml
2022-12-31+19:38:32.9105553030 ./roles/public/digikam/tasks/install-ppa.yml
2023-01-01+01:39:08.2045090970 ./roles/public/digikam/tasks/install-appimage.yml
2023-01-14+00:44:34.8526187360 ./roles/public/java-openjdk/defaults/main.yml
2023-01-26+14:10:18.9247087870 ./roles/public/ansible/tasks/main.yml
2023-01-26+16:14:59.9903243110 ./roles/public/sioyek-pdf/defaults/main.yml
2023-05-12+12:01:30.4705549280 ./roles/public/mpv/tasks/install.yml
2023-05-13+00:47:41.1561557100 ./roles/public/nextcloud-desktop/tasks/main.yml
2023-08-27+18:19:44.8291334420 ./roles/public/digikam/defaults/main.yml
Eureka!
Explainer
- the initial
cd <path-parent>
ensure that the resulting paths displayed byfind
don't contain the<path-parent>
part (to avoid cluttering the output), and the finalcd $OLDPWD
just make sure that the bash prompt is not changed to<path-parent>
. - unless you want to exclude some path from the search, there is obviously no need for
-not -path '*/<some-path>/*'
. grep -i
for case insensitive matching, and-l
(that's the letterl
for list) makesgrep
print only the filename and not each matching line (this is crucial for this hack to work, we wantgrep
to produce as little output as possible, in fact, if I could figure out a way to silencegrep
altogether I would have, but I couldn't).-printf "%T+ %p\n"
adds the filemtime
to the output (on a new line). Thanks angus@Unix.SE.
At this point, an example of the unfinished product is order. Before sorting, and before the final grep -v
, the output looks like this (excerpt):
taha@asks2:~
$ cd /media/bay/taha/projects/ansible && find . -not -path '*/legacy/*' -type f -name "*.yml" -exec grep -il "ppa:" {} \; -printf "%T+ %p\n" && cd $OLDPWD
./roles/public/java-openjdk/defaults/main.yml
2023-01-14+00:44:34.8526187360 ./roles/public/java-openjdk/defaults/main.yml
./roles/public/java-openjdk/tasks/ppa.yml
2022-01-09+07:39:51.1836426130 ./roles/public/java-openjdk/tasks/ppa.yml
./roles/public/browser-firefox/tasks/main.yml
2022-11-19+03:16:16.7832183030 ./roles/public/browser-firefox/tasks/main.yml
./roles/public/R/tasks/dependencies.yml
2022-12-24+23:04:01.2033026010 ./roles/public/R/tasks/dependencies.yml
./roles/public/browser-chromium/tasks/install.yml
2022-07-16+22:05:52.0552035060 ./roles/public/browser-chromium/tasks/install.yml
./roles/dev/x2goclient/tasks/main.yml
2022-08-12+00:09:51.6575729520 ./roles/dev/x2goclient/tasks/main.yml
with the grep
output on its own line, followed by the time-stamped output of printf
. Like I said, it would have been better if we could somehow silence the grep
output at this point. If you know a way, feel free to let me know!
As expected, sorting resulted in the non-time-stamped lines dangling about like some unwanted appendage:
taha@asks2:~
$ cd /media/bay/taha/projects/ansible && find . -not -path '*/legacy/*' -type f -name "*.yml" -exec grep -il "ppa:" {} \; -printf "%T+ %p\n" && cd $OLDPWD
2022-01-09+07:39:51.1836426130 ./roles/public/java-openjdk/tasks/ppa.yml
2022-07-16+22:05:52.0552035060 ./roles/public/browser-chromium/tasks/install.yml
2022-08-12+00:09:51.6575729520 ./roles/dev/x2goclient/tasks/main.yml
2022-11-19+03:16:16.7832183030 ./roles/public/browser-firefox/tasks/main.yml
2022-12-24+23:04:01.2033026010 ./roles/public/R/tasks/dependencies.yml
2023-01-14+00:44:34.8526187360 ./roles/public/java-openjdk/defaults/main.yml
./roles/public/java-openjdk/tasks/ppa.yml
./roles/public/browser-chromium/tasks/install.yml
./roles/dev/x2goclient/tasks/main.yml
./roles/public/browser-firefox/tasks/main.yml
./roles/public/R/tasks/dependencies.yml
./roles/public/java-openjdk/defaults/main.yml
At this point I was out of ideas, so grep -v ...
it was, and we end up with the one-liner shown above. It's an ugly hack, but hey, it works :-)
Bash 5.1 on Ubuntu 22.04.3, with GNU find 4.8.0, GNU grep 3.7, and GNU sort 8.32.
Passwordstore is a great password manager, and I rely on it also in my Ansible playbooks, where it works by causing the gpg-agent to prompt me for the passphrase of my gpg key.
This prompt is a GUI prompt, which is very suitable when sitting at the computer in question. But a small annoyance is that it does not work at all when working on a remote computer via ssh (the prompt shows up on the remote computer's desktop, and the Ansible playbook in the terminal just freezes until it eventually fails).
It would be so much nicer if those ssh terminal sessions would instead get the gpg-agent prompt in the terminal. So far I have not found a method that achieves this without also sacrificing the GUI desktop prompt for non-remote work.
Desktop and laptop running Ubuntu 22.04 with i3wm desktop.
The relevant parts of my config can be seen in https://codeberg.org/ansible/dotfiles.
I considered the following related Q:s&A:s but did not achieve the desired outcome.
- https://unix.stackexchange.com/questions/554153/what-is-the-proper-configuration-for-gpg-ssh-and-gpg-agent-to-use-gpg-auth-sub
- https://stackoverflow.com/questions/17769831/how-to-make-gpg-prompt-for-passphrase-on-cli
- https://superuser.com/questions/1189602/how-to-configure-gpg2-to-ask-for-passphrase-on-the-console-instead-of-in-a-popup
- https://unix.stackexchange.com/questions/217737/pinentry-fails-with-gpg-agent-and-ssh
Some more tests
In the SSH session (no effect, unfortunately):
gpg-connect-agent updatestartuptty /bye
Learned that the gpg-agent is running in --supervised
mode, and its ENV variables include DISPLAY=:0
:
solarchemist@desktop:~
$ sudo cat /proc/2652288/environ
HOME=/home/solarchemist LANG=en_US.UTF-8 LC_TIME=sv_SE.UTF-8 LOGNAME=solarchemist
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin
SHELL=/bin/bash SYSTEMD_EXEC_PID=2652288 USER=solarchemist
XDG_DATA_DIRS=/home/solarchemist/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop
XDG_RUNTIME_DIR=/run/user/1000 QT_ACCESSIBILITY=1
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus DISPLAY=:0 XAUTHORITY=/home/solarchemist/.Xauthority
MANAGERPID=1532 LISTEN_PID=2652288 LISTEN_FDS=4
LISTEN_FDNAMES=browser:extra:std:ssh INVOCATION_ID=<stuff> JOURNAL_STREAM=<stuff>
(the PID of the gpg-agent process is easily identified with ps aux | grep [g]pg
).
Manual work-around
Manual work-around is to set pinentry-program /usr/bin/pinentry-tty
in ~/.gnupg/gpg-agent.conf
and reload the agent gpg-connect-agent reloadagent /bye
.
To revert to the default (GUI) pinentry behaviour, just remove the line and reload the agent again.
Will add more as I learn about them. I only care about FOSS projects.
LXD
See my Ansible role that installs and configures LXD and provisions LXC containers.
Docker
I have some experience with Docker containers. Prefer LXC if I have the choice.
I have written an Ansible playbook to setup containers with docker-compose as part of a as-yet not-public project for InvenioRDM.
Podman
No hands-on experience yet.
Singularity
Hm, interesting. Could it be better than Docker for sharing something like a thesis with "all batteries included", I wonder?
Singularity can convert Docker containers to Singularity, or can run containers directly from Docker Hub
https://blogs.oregonstate.edu/learningbydoing/2022/01/04/docker-and-singularity-containers-which-one-is-better/
Learn the skills required to sysadmin a remote Linux server from the commandline.
Course restarts every first Monday of the month
What an interesting idea. A Python script that given an existing system creates an Ansible playbook that will duplicate it.
I wanted to create an ASCII art "bismillah" for use in the terminal.
Found this beautiful calligraphy by Nuria Garcia Masip. After cropping it, rotating it slightly and making the background monochromatic, I tried the ascii-image-converter by Zoraiz Hassan, and it worked great with the --braille
flag:
This converter tool was really easy to install (just run the binary), and the output was automatically resized to fit the terminal window size, which was a convenient feature.
I discovered some other ASCII art CLI tools (never tested them though):
- https://dns-lookup.jvns.ca/.
- https://zone.vision (queries the authoritative nameservers for a domain directly)
- https://mxtoolbox.com (oriented towards MX/SPF queries)
- https://toolbox.googleapps.com/apps/dig/#A/
Via Julia Evans.
Guides and how-tos
- Introduction to Ansible playbooks
- Playbooks vs roles, and explain the difference between task, role, play and playbook
- How to install and configure Ansible on Ubuntu 18.04 - DigitalOcean
- Linux hardening using idempotency with Ansible
Ansible playbooks and roles
- I haven't published all roles that I've written, but all the roles that I do publish are collected at codeberg.org/ansible
- Ansible module (Python code) to install R packages, by yutannihilation
- Ansible role to install TeXLive
- https://ansible.jeffgeerling.com/
- https://robertdebock.nl/ansible.html
Related
This could perhaps be a much more stable alternative to my reverse SSH tunnels for keeping connections with various servers, especially desktops inside FM-NET and other machines inside other LANs.
The main downside is that Tailscale is not FOSS. So perhaps we should look for other solutions built on WireGuard, or perhaps learn to configure WireGuard directly.
The command and its typical output (highlights not shown due to technical limitations in Markdown):
me@host:~/ansible/playbooks
$ find . -type f -name "playbook.log" -exec sh -c 'tac {} | grep -m 1 -A1 "^Playbook last committed by"' \; | grep --color -E "^|git/ansible/[A-Za-z]+?/[A-Za-z-]+?.yml|(19|20)[0-9][0-9]-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}"
Playbook last committed by me@host on Fri Apr 17 22:02:15 2020 +0200 (afd13a3b3b3f43d3f84bb16b1c91a6b5bec2cfe1)
2020-04-19 00:40:30,925 p=32526 u=me n=ansible | task path: /home/me/ansible/playbooks/luxor/playbook-host.yml:99
Playbook last committed by me@host on Wed Jan 29 14:34:38 2020 +0100 (5157cd051e276abfe99e93c37a8ad0c79dd4d3dc)
2020-03-29 01:39:34,874 p=14553 u=me n=ansible | task path: /home/me/ansible/playbooks/damietta/playbook-heliopolis.yml:31
Playbook last committed by me@host on Tue Feb 18 17:30:19 2020 +0100 (ae6c02965f4471d8089c5e4d2a427cb0cbfbc6b8)
2020-02-23 19:58:30,188 p=1050 u=me n=ansible | task path: /home/me/ansible/playbooks/abydos/playbook-webserver.yml:35
Playbook last committed by me@host on Sun Jan 5 09:44:27 2020 +0100 (26392ab778deaf86430f36bc7aed942ae04a938c)
2020-01-08 13:26:35,647 p=me u=27195 | changed: [hunan.domain.se -> localhost] => {"changed": true, "cmd": "git log --pretty=\"Playbook last committed by %cn on %cd (%H)\" -1 >> playbook.log", "delta": "0:00:00.003510", "end": "2020-01-08 13:26:35.628135", "rc": 0, "start": "2020-01-08 13:26:35.624625", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
Playbook last committed by me@host on Thu Mar 26 14:57:00 2020 +0100 (2c9aa2030192c2942c5dfb0bcf5976f46fefd774)
2020-04-01 15:44:19,057 p=10821 u=me n=ansible | task path: /home/me/ansible/playbooks/alexandria/playbook.yml:137
The first find
command lists all playbook.log
files below the current directory (recursing into child directories). tac
is the opposite of cat
and lists each file backwards (from last line to first). We use grep
to look for a string ("Playbook last committed") that my Ansible playbooks always insert into the log-file at the end of a run. Note the use of the -A1
flag that gets the matched line and one line after (but because we used tac
, we actually get the line before, which is what we want). The final grep
uses extended regular expressions (-E
) to color highlight several parts of the output (while displaying all of the output, that's what the initial caret does - it effectively matches all lines).
Pretty neat, if I may say so myself.
Some of the refs I consulted to figure out this one-liner:
https://serverfault.com/questions/197123/getting-the-last-match-in-a-file-using-grep
https://unix.stackexchange.com/questions/112159/grep-from-the-end-of-a-file-to-the-beginning
https://stackoverflow.com/questions/307015/how-do-i-include-a-pipe-in-my-linux-find-exec-command
https://superuser.com/questions/914856/grep-display-all-output-but-highlight-search-matches
https://unix.stackexchange.com/questions/366/convince-grep-to-output-all-lines-not-just-those-with-matches
https://unix.stackexchange.com/questions/37313/how-do-i-grep-for-multiple-patterns-with-pattern-having-a-pipe-character
https://en.wikipedia.org/wiki/Regular_expression#POSIX_extended
Discusses entropy as a measure of password strength, and includes two useful tables.
For example, a 30-character password using alphanumeric characters (mixing both small- and upper-case letters) achieves around 160 bits of entropy.
The article also includes guidelines for strong passwords, reproduced below:
- Use a minimum password length of 10 or more characters if permitted.
- Include lowercase and uppercase alphabetic characters, numbers and symbols if permitted.
- Generate passwords randomly where feasible.
- Avoid using the same password twice (e.g., across multiple user accounts and/or software systems).
- Avoid character repetition, keyboard patterns, dictionary words, letter or number sequences.
- Avoid using information that is or might become publicly associated with the user or the account, such as username, ancestors' names or dates.
- Avoid using information that the user's colleagues and/or acquaintances might know to be associated with the user, such as relative or pet names, romantic links (current or past) and biographical information (e.g., ID numbers, ancestors' names or dates)..
- Do not use passwords which consist wholly of any simple combination of the aforementioned weak components.
Setup your Pi-hole:
- Connect the Raspberry Pi to your router
- Open the Ubiquiti router's dashboard in your browser, and open the Services tab
- Click on the Actions button, select View Leases
- Identify your Raspberry Pi in the list (look for the hostname of your Raspberry or its MAC address)
- Click the Map Static IP button, and give your Raspberry Pi a static IP address
- Click on the Details tab inside the open dialog, and enter the IP address you just chose into the "DNS 1" field. Feel free to add another DNS provider in the "DNS 2" field (this is used in case your Pi-Hole stops working).
- Restart the Raspberry Pi
You should now be able to login to your Pi-Hole web dashboard from your browser by going to http://<ip-address>/admin
(click on login and enter your password to see everything).