Bug in StackScript Bash Library configure_basic_firewall on Ubuntu 23.10
Not sure if this is the place to report this but there seems to be a bug in the StackScript Bash Library
I have the following StackScript
#!/bin/bash
# <UDF name="admin_password" label="The default user password">
# <UDF name="admin_username" label="The default user name">
# <UDF name="admin_pubkey" label="The default user account's public key">
# <UDF name="hostname" label="The hostname for the new instance">
source <ssinclude StackScriptID="1">
get_started "" "$HOSTNAME" $(system_primary_ip)
system_update
secure_server "$ADMIN_USERNAME" "$ADMIN_PASSWORD" "$ADMIN_PUBKEY"
I spin up a new Linode using Ubuntu 23.10 but after it finishes I can't connect.
If I use LISH to get in and run iptables --list
I see that port 22 hasn't been allowed
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT all -- anywhere anywhere /* Allow loopback connections */
ACCEPT icmp -- anywhere anywhere /* Allow Ping to work as expected */
Chain FORWARD (policy DROP)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
But the secure_server
function calls configure_basic_firewall
which calls
add_port 'ipv4' 22 'tcp'
add_port 'ipv6' 22 'tcp'
save_firewall
So it looks like it should work
If I change my StackScript to add these 3 lines, or the following, after secure_server
and spin up a new instance I can get in
add_ports 22
save_firewall
So I changed the StackScript to exit immediately and add set -x
to the beginning then spin up a new instance.
When it was up I got into the instance and removed the exit from the script and then ran it (setting appropriate ENV variables)
root@localhost:~# ./StackScript
++ system_primary_ip
+++ awk '/inet / {print $2}'
+++ ip a
++ local 'ip_address=127.0.0.1/8
88.80.184.76/24'
++ cut -d/ -f 1
++ cut '-d ' -f 2
++ echo 127.0.0.1/8 88.80.184.76/24
+ get_started '' rebelinblue.com 88.80.184.76
+ local -r subdomain= domain=rebelinblue.com ip=88.80.184.76
+ '[' rebelinblue.com ']'
+ '[' '' ']'
+ local -r fqdn=rebelinblue.com
+ local -r hostname=rebelinblue.com
+ printf 'Setting IP Address (%s), fqdn (%s), and hostname (%s) in /etc/hosts...\n' 88.80.184.76 rebelinblue.com rebelinblue.com
Setting IP Address (88.80.184.76), fqdn (rebelinblue.com), and hostname (rebelinblue.com) in /etc/hosts...
+ system_add_host_entry 88.80.184.76 rebelinblue.com rebelinblue.com
+ local -r ip_address=88.80.184.76 fqdn=rebelinblue.com hostname=rebelinblue.com
+ '[' -z 88.80.184.76 -o -z rebelinblue.com ']'
+ echo '88.80.184.76 rebelinblue.com rebelinblue.com'
+ system_update
+ case "${detected_distro[family]}" in
+ echo 'Acquire::ForceIPv4 "true";'
+ printf 'Checking for initial updates...\n'
Checking for initial updates...
+ DEBIAN_FRONTEND=noninteractive
+ apt-get update -qq
+ system_set_hostname rebelinblue.com
+ local -r hostname=rebelinblue.com
+ '[' '!' -n rebelinblue.com ']'
+ hostnamectl set-hostname rebelinblue.com
+ system_update
+ case "${detected_distro[family]}" in
+ echo 'Acquire::ForceIPv4 "true";'
+ printf 'Checking for initial updates...\n'
Checking for initial updates...
+ DEBIAN_FRONTEND=noninteractive
+ apt-get update -qq
+ secure_server stephen blahblahblbha 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgnlCD5hNitroeqHKun4svSkQwkt6OcWkTyA0g66Wj5'
+ '[' '!' -n stephen ']'
+ '[' '!' -n blahblahblbha ']'
+ '[' '!' -n 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgnlCD5hNitroeqHKun4svSkQwkt6OcWkTyA0g66Wj5' ']'
+ local -r user=stephen password=blahblahblbha 'pubkey=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgnlCD5hNitroeqHKun4svSkQwkt6OcWkTyA0g66Wj5'
+ user_add_sudo stephen blahblahblbha
+ '[' '!' -n stephen -o '!' -n blahblahblbha ']'
+ local -r username=stephen userpass=blahblahblbha
+ '[' '!' -x /usr/bin/sudo ']'
+ case "${detected_distro[family]}" in
+ adduser stephen --disabled-password --gecos ''
info: Adding user `stephen' ...
info: Selecting UID/GID from range 1000 to 59999 ...
info: Adding new group `stephen' (1000) ...
info: Adding new user `stephen' (1000) with group `stephen (1000)' ...
info: Creating home directory `/home/stephen' ...
info: Copying files from `/etc/skel' ...
info: Adding new user `stephen' to supplemental / extra groups `users' ...
info: Adding user `stephen' to group `users' ...
+ chpasswd
+ echo stephen:blahblahblbha
+ adduser stephen sudo
+ user_add_pubkey stephen 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgnlCD5hNitroeqHKun4svSkQwkt6OcWkTyA0g66Wj5'
+ '[' '!' -n stephen -o '!' -n 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgnlCD5hNitroeqHKun4svSkQwkt6OcWkTyA0g66Wj5' ']'
+ local -r username=stephen 'userpubkey=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgnlCD5hNitroeqHKun4svSkQwkt6OcWkTyA0g66Wj5'
+ case "$username" in
+ mkdir -p /home/stephen/.ssh
+ chmod -R 700 /home/stephen/.ssh/
+ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgnlCD5hNitroeqHKun4svSkQwkt6OcWkTyA0g66Wj5'
+ chown -R stephen:stephen /home/stephen/.ssh
+ chmod 600 /home/stephen/.ssh/authorized_keys
+ ssh_disable_root
+ sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
+ sed -i -e 's/#PermitRootLogin no/PermitRootLogin no/' /etc/ssh/sshd_config
+ sed -i -e 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
+ sed -i -e 's/#PasswordAuthentication no/PasswordAuthentication no/' /etc/ssh/sshd_config
+ '[' debian == debian ']'
+ systemctl restart ssh
+ '[' debian == redhat ']'
+ configure_basic_firewall
+ case "${detected_distro[family]}" in
+ iptables --policy INPUT DROP
+ iptables --policy OUTPUT ACCEPT
+ iptables --policy FORWARD DROP
+ iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+ iptables -A INPUT -i lo -m comment --comment 'Allow loopback connections' -j ACCEPT
+ iptables -A INPUT -p icmp -m comment --comment 'Allow Ping to work as expected' -j ACCEPT
+ ip6tables --policy INPUT DROP
+ ip6tables --policy OUTPUT ACCEPT
+ ip6tables --policy FORWARD DROP
+ ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+ ip6tables -A INPUT -i lo -m comment --comment 'Allow loopback connections' -j ACCEPT
+ ip6tables -A INPUT -p icmpv6 -m comment --comment 'Allow Ping to work as expected' -j ACCEPT
+ add_port ipv4 22 tcp
+ '[' '!' -n ipv4 ']'
+ '[' '!' -n 22 ']'
+ '[' '!' -n tcp ']'
+ local -r standard=ipv4 port=22 protocol=tcp
+ case "${detected_distro[family]}" in
+ '[' -x /usr/sbin/ufw ']'
+ ufw allow 22/tcp
Rules updated
Rules updated (v6)
+ add_port ipv6 22 tcp
+ '[' '!' -n ipv6 ']'
+ '[' '!' -n 22 ']'
+ '[' '!' -n tcp ']'
+ local -r standard=ipv6 port=22 protocol=tcp
+ case "${detected_distro[family]}" in
+ '[' -x /usr/sbin/ufw ']'
+ ufw allow 22/tcp
Skipping adding existing rule
Skipping adding existing rule (v6)
+ save_firewall
+ case "${detected_distro[family]}" in
+ printf 'Saving firewall rules for IPv4 and IPv6...\n'
Saving firewall rules for IPv4 and IPv6...
+ sudo debconf-set-selections
+ echo iptables-persistent iptables-persistent/autosave_v4 boolean true
+ sudo debconf-set-selections
+ echo iptables-persistent iptables-persistent/autosave_v6 boolean true
+ system_install_package iptables-persistent
+ '[' '!' -n iptables-persistent ']'
+ packages=('iptables-persistent')
+ local packages
+ case "${detected_distro['family']}" in
+ DEBIAN_FRONTEND=noninteractive
+ apt-get -y install iptables-persistent -qq
Scanning processes...
Scanning linux images...
+ enable_fail2ban
+ system_install_package fail2ban
+ '[' '!' -n fail2ban ']'
+ packages=('fail2ban')
+ local packages
+ case "${detected_distro['family']}" in
+ DEBIAN_FRONTEND=noninteractive
+ apt-get -y install fail2ban -qq
Scanning processes...
Scanning linux images...
+ cd /etc/fail2ban
+ cp fail2ban.conf fail2ban.local
+ cp jail.conf jail.local
+ sed -i -e 's/backend = auto/backend = systemd/' /etc/fail2ban/jail.local
+ systemctl enable fail2ban
Synchronizing state of fail2ban.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable fail2ban
+ systemctl start fail2ban
+ cd /root/
+ systemctl start fail2ban
+ systemctl enable fail2ban
Synchronizing state of fail2ban.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable fail2ban
I can sort of see the problem
+ '[' -x /usr/sbin/ufw ']'
+ ufw allow 22/tcp
Skipping adding existing rule
Skipping adding existing rule (v6)
+ save_firewall
So /usr/sbin/ufw seems to be being found so it is creating firewall rules using ufw, but then if you call add_ports it uses iptables instead
+ add_ports 22
+ ports=('22')
+ local -a ports
+ for i in "${ports[@]}"
+ add_port ipv4 22 tcp
+ '[' '!' -n ipv4 ']'
+ '[' '!' -n 22 ']'
+ '[' '!' -n tcp ']'
+ local -r standard=ipv4 port=22 protocol=tcp
+ case "${detected_distro[family]}" in
+ '[' -x /usr/sbin/ufw ']'
+ case "$standard" in
+ iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
+ add_port ipv6 22 tcp
+ '[' '!' -n ipv6 ']'
+ '[' '!' -n 22 ']'
+ '[' '!' -n tcp ']'
+ local -r standard=ipv6 port=22 protocol=tcp
+ case "${detected_distro[family]}" in
+ '[' -x /usr/sbin/ufw ']'
+ case "$standard" in
+ ip6tables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
+ save_firewall
+ case "${detected_distro[family]}" in
+ printf 'Saving firewall rules for IPv4 and IPv6...\n'
Saving firewall rules for IPv4 and IPv6...
+ sudo debconf-set-selections
+ echo iptables-persistent iptables-persistent/autosave_v4 boolean true
+ sudo debconf-set-selections
+ echo iptables-persistent iptables-persistent/autosave_v6 boolean true
+ system_install_package iptables-persistent
+ '[' '!' -n iptables-persistent ']'
+ packages=('iptables-persistent')
+ local packages
+ case "${detected_distro['family']}" in
+ DEBIAN_FRONTEND=noninteractive
+ apt-get -y install iptables-persistent -qq
+ exit
it looks like fail2ban is removing ufw and installing iptables so the rule for port 22 is being lost as it is set before iptables is installed
[edit] Actually I don't think fail2ban is removing ufw, because configure_basic_firewall
is using iptables, it's the calls it makes to add_port
which is trying to use ufw :| but after this runs /usr/sbin/ufw
does not exist on the instance
[second edit] From looking into it a bit more save_firewall
installs iptables-persistent
which results in ufw
being removed
root@localhost:~# apt install iptables-persistent
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
netfilter-persistent
The following packages will be REMOVED:
ufw
The following NEW packages will be installed:
iptables-persistent netfilter-persistent
0 upgraded, 2 newly installed, 1 to remove and 27 not upgraded.
Need to get 14.3 kB of archives.
After this operation, 778 kB disk space will be freed.
so the add_port
call because save_firewall
uses ufw
, but then save_firewall
removes ufw