Skip to content

Instantly share code, notes, and snippets.

@qguv
Last active July 8, 2022 02:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qguv/850bba7b9b1c4170e28044a0b6f72307 to your computer and use it in GitHub Desktop.
Save qguv/850bba7b9b1c4170e28044a0b6f72307 to your computer and use it in GitHub Desktop.
Prepare LXD container which can only access the internet through an http proxy (uses tinyproxy and firewalld). Written for Fedora 31, but will probably work elsewhere too.
#!/bin/bash
set -e
create_container="${create_container:-true}"
lxd_bridge="${lxd_bridge:-lxdbr0}"
container_image="${container_image:-images:debian/8/amd64}"
container_name="${container_name:-onpremise-proxy-test}"
tinyproxy_conf="${tinyproxy_conf:-/etc/tinyproxy/tinyproxy.conf}"
sudo_conf="${sudo_conf:-/etc/sudoers.d/client}"
apt_conf="${apt_conf:-/etc/apt/apt.conf.d/proxy.conf}"
# don't configure apt for centos containers
case "$container_image" in *centos*) apt_conf=;; esac
# check that preconditions are met
if [ "$UID" -eq 0 ]; then
echo 'This must not be run as root!'
exit 16
elif ! lxc version >/dev/null; then
echo 'LXD must be installed!'
exit 17
elif ! sudo firewall-cmd --version >/dev/null; then
echo 'firewalld must be installed!'
exit 18
elif ! sudo systemctl is-active firewalld >/dev/null; then
echo 'firewalld must be running!'
exit 19
elif ! sudo tinyproxy -v >/dev/null; then
echo 'tinyproxy must be installed!'
exit 20
elif [ "$create_container" = true ] && lxc info "$container_name" >/dev/null; then
printf '[ERROR] Container "%s" already exists!\n' "$container_name"
printf 'Hint: either tell this script to use the existing container:\n\tcreate_container=false %s\n\n' "$0 $@"
printf 'or delete the container with:\n\tlxc delete --force "%s"\n\nbefore re-running this script.\n' "$container_name"
exit 21
fi
printf 'Getting lxd bridge interface subnet... '
lxd_route="$(ip route | grep lxdbr0)"
if [ -z "$lxd_route" ]; then
printf 'No lxd bridge interface found at %s!\n' "$lxd_bridge"
exit 22
fi
lxd_subnet="${lxd_route%% *}"
printf 'got %s\n' "$lxd_subnet"
printf 'Getting tinyproxy port... '
tinyproxy_port="$(sed -n 's/^\s*Port\s\+\([0-9]\+\)\s*$/\1/p' "$tinyproxy_conf")"
tinyproxy_port="${tinyproxy_port:-8888}"
printf 'got %s\n' "$tinyproxy_port"
printf 'Configuring tinyproxy to allow connections from lxd subnet %s... ' "$lxd_subnet"
if grep --quiet --line-regexp --fixed-strings "Allow $lxd_subnet" "$tinyproxy_conf"; then
echo already ok
else
sudo tee -a "$tinyproxy_conf" >/dev/null <<-EOF
Allow $lxd_subnet
EOF
echo added ok
fi
printf 'Restarting tinyproxy... '
sudo systemctl restart tinyproxy
echo ok
printf 'Disabling lxc network and firewall... '
lxc network set lxdbr0 ipv4.nat false
lxc network set lxdbr0 ipv6.nat false
lxc network set lxdbr0 ipv6.firewall false
lxc network set lxdbr0 ipv4.firewall false
echo ok
printf '\nConfiguring firewalld to serve as firewall and NAT for lxd containers... \n'
(
set -v
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i "$lxd_bridge" -s "$lxd_subnet" -m comment --comment "generated by firewalld for LXD" -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -o "$lxd_bridge" -d "$lxd_subnet" -m comment --comment "generated by firewalld for LXD" -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i "$lxd_bridge" -s "$lxd_subnet" -m comment --comment "generated by firewalld for LXD" -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s "$lxd_subnet" ! -d "$lxd_subnet" -m comment --comment "generated by firewalld for LXD" -j MASQUERADE
sudo firewall-cmd --reload
)
printf 'Finished configuring firewalld\n\n'
if [ "$create_container" = true ]; then
printf 'Creating a new container...\n'
(
set -v
lxc init "$container_image" "$container_name"
lxc config set "$container_name" security.privileged true
lxc start "$container_name" >/dev/null || true
)
printf 'Finished creating a new container\n\n'
fi
printf 'Waiting for container IP... '
while
container_ip="$(lxc info "$container_name" | grep 'eth0:\sinet\s' | cut -f3)"
[ -z "$container_ip" ]
do
sleep 1
done
printf 'got %s\n' "$container_ip"
printf 'Getting interface ip... '
interface_ip="$(ip route get "$container_ip" | sed -n 's/.*src \(\S\+\).*/\1/p')"
printf 'got %s\n' "$interface_ip"
proxy_url="http://$interface_ip:$tinyproxy_port"
printf '\nConfiguring http proxy for new container...\n'
lxc exec "$container_name" -- tee -a /etc/environment <<-EOF
HTTP_PROXY='$proxy_url'
HTTPS_PROXY='$proxy_url'
FTP_PROXY='$proxy_url'
NO_PROXY='localhost,127.0.0.1,$interface_ip'
http_proxy='$proxy_url'
https_proxy='$proxy_url'
ftp_proxy='$proxy_url'
no_proxy='localhost,127.0.0.1,$interface_ip'
EOF
printf 'Finished configuring http proxy for new container\n\n'
printf 'Configuring sudo to propagate proxy env variables... '
lxc exec "$container_name" -- sh <<-EOF
sudo_dir="\$(dirname "$sudo_conf")"
[ -d "\$sudo_dir" ] || mkdir -p "\$sudo_dir"
EOF
sudo_defaults='Defaults env_keep = "HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY http_proxy https_proxy ftp_proxy no_proxy"'
if lxc exec "$container_name" -- cat "$sudo_conf" 2>/dev/null | grep --quiet --line-regexp --fixed-strings "$sudo_defaults"; then
echo already ok
else
printf '%s\n' "$sudo_defaults" | lxc exec "$container_name" -- tee -a "$sudo_conf" >/dev/null
echo added ok
fi
if [ -n "$apt_conf" ]; then
printf 'Configuring apt to use http proxy... '
if lxc exec "$container_name" -- cat "$apt_conf" 2>/dev/null | grep --quiet --line-regexp --fixed-strings 'Acquire::http::Proxy "'"$proxy_url"'";'; then
echo already ok
else
lxc exec "$container_name" -- tee -a "$apt_conf" >/dev/null <<-EOF
Acquire::http::Proxy "$proxy_url";
Acquire::https::Proxy "$proxy_url";
EOF
echo added ok
fi
fi
printf 'Removing direct route to internet for new container... '
if lxc exec "$container_name" -- ip route show default | grep --quiet '^default'; then
lxc exec "$container_name" ip route delete default
echo ok
else
echo already ok
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment