initial commit
This commit is contained in:
commit
77ed02f107
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.python-version
|
||||||
|
secrets.enc
|
13
bootstrap.yml
Normal file
13
bootstrap.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
- hosts: bootstrap
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
created_username: phil
|
||||||
|
vars_prompt:
|
||||||
|
- name: tailscale_key
|
||||||
|
prompt: Enter the tailscale key
|
||||||
|
roles:
|
||||||
|
- bootstrap
|
||||||
|
- role: artis3n.tailscale
|
||||||
|
vars:
|
||||||
|
tailscale_authkey: "{{ tailscale_key }}"
|
10
hosts.yml
Normal file
10
hosts.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
bootstrap:
|
||||||
|
hosts:
|
||||||
|
netcup:
|
||||||
|
ansible_host: 152.53.36.98
|
||||||
|
|
||||||
|
docker:
|
||||||
|
hosts:
|
||||||
|
docker-01:
|
||||||
|
ansible_host: 100.70.169.99
|
||||||
|
ansible_user: phil
|
3
requirements.yml
Normal file
3
requirements.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
roles:
|
||||||
|
- name: artis3n.tailscale
|
55
roles/bootstrap/tasks/main.yml
Normal file
55
roles/bootstrap/tasks/main.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
- name: Install aptitude
|
||||||
|
apt:
|
||||||
|
name: aptitude
|
||||||
|
state: latest
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Setup passwordless sudo
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
state: present
|
||||||
|
regexp: '^%sudo'
|
||||||
|
line: '%sudo ALL=(ALL) NOPASSWD: ALL'
|
||||||
|
validate: '/usr/sbin/visudo -cf %s'
|
||||||
|
|
||||||
|
- name: Create a new regular user with sudo privileges
|
||||||
|
user:
|
||||||
|
name: "{{ created_username }}"
|
||||||
|
state: present
|
||||||
|
groups: sudo
|
||||||
|
append: true
|
||||||
|
create_home: true
|
||||||
|
|
||||||
|
- name: Set authorized key for remote user
|
||||||
|
ansible.posix.authorized_key:
|
||||||
|
user: "{{ created_username }}"
|
||||||
|
state: present
|
||||||
|
key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_ed25519.pub') }}"
|
||||||
|
|
||||||
|
- name: Disable password authentication for root
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
state: present
|
||||||
|
regexp: '^#?PermitRootLogin'
|
||||||
|
line: 'PermitRootLogin prohibit-password'
|
||||||
|
|
||||||
|
- name: Update apt and install required system packages
|
||||||
|
apt:
|
||||||
|
pkg:
|
||||||
|
- curl
|
||||||
|
- vim
|
||||||
|
- git
|
||||||
|
- ufw
|
||||||
|
state: latest
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: UFW - Allow SSH connections
|
||||||
|
community.general.ufw:
|
||||||
|
rule: allow
|
||||||
|
name: OpenSSH
|
||||||
|
|
||||||
|
- name: UFW - Enable and deny by default
|
||||||
|
community.general.ufw:
|
||||||
|
state: enabled
|
||||||
|
default: deny
|
10
roles/common/tasks/main.yml
Normal file
10
roles/common/tasks/main.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
- name: Install aptitude
|
||||||
|
apt:
|
||||||
|
name: aptitude
|
||||||
|
state: latest
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: UFW - Enable and deny by default
|
||||||
|
community.general.ufw:
|
||||||
|
state: enabled
|
||||||
|
default: deny
|
7
roles/docker/files/Caddyfile
Normal file
7
roles/docker/files/Caddyfile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
auth.thesatelliteoflove.com {
|
||||||
|
reverse_proxy authentik-server-1:9000
|
||||||
|
}
|
||||||
|
|
||||||
|
git.thesatelliteoflove.com {
|
||||||
|
reverse_proxy gitea:3000
|
||||||
|
}
|
22
roles/docker/files/caddy-compose.yml
Normal file
22
roles/docker/files/caddy-compose.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
image: caddy:2.8.4
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
- "443:443/udp"
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||||
|
- ./site:/srv
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external: true
|
||||||
|
name: lava
|
22
roles/docker/files/dockge-compose.yml
Normal file
22
roles/docker/files/dockge-compose.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
services:
|
||||||
|
dockge:
|
||||||
|
image: louislam/dockge:1
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
# Host Port : Container Port
|
||||||
|
- 5001:5001
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./data:/app/data
|
||||||
|
|
||||||
|
# If you want to use private registries, you need to share the auth file with Dockge:
|
||||||
|
# - /root/.docker/:/root/.docker
|
||||||
|
|
||||||
|
# Stacks Directory
|
||||||
|
# ⚠️ READ IT CAREFULLY. If you did it wrong, your data could end up writing into a WRONG PATH.
|
||||||
|
# ⚠️ 1. FULL path only. No relative path (MUST)
|
||||||
|
# ⚠️ 2. Left Stacks Path === Right Stacks Path (MUST)
|
||||||
|
- /opt/stacks:/opt/stacks
|
||||||
|
environment:
|
||||||
|
# Tell Dockge where is your stacks directory
|
||||||
|
- DOCKGE_STACKS_DIR=/opt/stacks
|
26
roles/docker/files/gitea-compose.yml
Normal file
26
roles/docker/files/gitea-compose.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: gitea/gitea:1.22.1
|
||||||
|
container_name: gitea
|
||||||
|
environment:
|
||||||
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- gitea:/data
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- 222:22
|
||||||
|
extra_hosts:
|
||||||
|
- 'auth.thesatelliteoflove.com:172.20.0.6'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
gitea:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external: true
|
||||||
|
name: lava
|
477
roles/docker/files/ufw-docker.sh
Normal file
477
roles/docker/files/ufw-docker.sh
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
[[ -n "${DEBUG:-}" ]] && set -x
|
||||||
|
|
||||||
|
LANG=en_US.UTF-8
|
||||||
|
LANGUAGE=en_US:
|
||||||
|
LC_ALL=en_US.UTF-8
|
||||||
|
PATH="/bin:/usr/bin:/sbin:/usr/sbin:/snap/bin/"
|
||||||
|
|
||||||
|
GREP_REGEXP_INSTANCE_NAME="[-_.[:alnum:]]\\+"
|
||||||
|
DEFAULT_PROTO=tcp
|
||||||
|
|
||||||
|
ufw_docker_agent=ufw-docker-agent
|
||||||
|
ufw_docker_agent_image="${UFW_DOCKER_AGENT_IMAGE:-chaifeng/${ufw_docker_agent}:221002-nf_tables}"
|
||||||
|
|
||||||
|
if [[ "${ufw_docker_agent_image}" = *-@(legacy|nf_tables) ]]; then
|
||||||
|
if iptables --version | grep -F '(legacy)' &>/dev/null; then
|
||||||
|
ufw_docker_agent_image="${ufw_docker_agent_image%-*}-legacy"
|
||||||
|
else
|
||||||
|
ufw_docker_agent_image="${ufw_docker_agent_image%-*}-nf_tables"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
test -n "$ufw_docker_agent_image"
|
||||||
|
|
||||||
|
function ufw-docker--status() {
|
||||||
|
ufw-docker--list "$GREP_REGEXP_INSTANCE_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--list() {
|
||||||
|
local INSTANCE_NAME="$1"
|
||||||
|
local INSTANCE_PORT="${2:-}"
|
||||||
|
local PROTO="${3:-${DEFAULT_PROTO}}"
|
||||||
|
local NETWORK="${4:-}"
|
||||||
|
|
||||||
|
if [[ -z "$INSTANCE_PORT" ]]; then
|
||||||
|
INSTANCE_PORT="[[:digit:]]\\+"
|
||||||
|
PROTO="\\(tcp\\|udp\\)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$NETWORK" ]]; then
|
||||||
|
NETWORK="[[:graph:]]*"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ufw status numbered | grep "# allow ${INSTANCE_NAME}\\( ${INSTANCE_PORT}\\/${PROTO}\\)\\( ${NETWORK}\\)\$" || \
|
||||||
|
ufw status numbered | grep "# allow ${INSTANCE_NAME}\\( ${INSTANCE_PORT}\\/${PROTO}\\)\$" || \
|
||||||
|
ufw status numbered | grep "# allow ${INSTANCE_NAME}\$"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--list-number() {
|
||||||
|
ufw-docker--list "$@" | sed -e 's/^\[[[:blank:]]*\([[:digit:]]\+\)\].*/\1/'
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--delete() {
|
||||||
|
for UFW_NUMBER in $(ufw-docker--list-number "$@" | sort -rn); do
|
||||||
|
echo "delete \"$UFW_NUMBER\""
|
||||||
|
echo y | ufw delete "$UFW_NUMBER" || true
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--allow() {
|
||||||
|
local INSTANCE_NAME="$1"
|
||||||
|
local INSTANCE_PORT="$2"
|
||||||
|
local PROTO="$3"
|
||||||
|
local NETWORK="${4:-}"
|
||||||
|
|
||||||
|
docker inspect "$INSTANCE_NAME" &>/dev/null ||
|
||||||
|
die "Docker instance \"$INSTANCE_NAME\" doesn't exist."
|
||||||
|
|
||||||
|
mapfile -t INSTANCE_IP_ADDRESSES < <(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{"\n"}}{{end}}' "$INSTANCE_NAME" 2>/dev/null | remove_blank_lines)
|
||||||
|
|
||||||
|
[[ -z "${INSTANCE_IP_ADDRESSES:-}" ]] && die "Could not find a running instance \"$INSTANCE_NAME\"."
|
||||||
|
|
||||||
|
mapfile -t INSTANCE_NETWORK_NAMES < <(docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{printf "%s\n" $k}}{{end}}' "$INSTANCE_NAME" 2>/dev/null | remove_blank_lines)
|
||||||
|
mapfile -t PORT_PROTO_LIST < <(docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{with $conf}}{{$p}}{{"\n"}}{{end}}{{end}}' "$INSTANCE_NAME" | remove_blank_lines)
|
||||||
|
|
||||||
|
if [[ -z "${PORT_PROTO_LIST:-}" ]]; then
|
||||||
|
err "\"$INSTANCE_NAME\" doesn't have any published ports."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RETVAL=1
|
||||||
|
for PORT_PROTO in "${PORT_PROTO_LIST[@]}"; do
|
||||||
|
if [[ -z "$INSTANCE_PORT" || "$PORT_PROTO" = "${INSTANCE_PORT}/${PROTO}" ]]; then
|
||||||
|
ITER=0
|
||||||
|
for IP in "${INSTANCE_IP_ADDRESSES[@]}"; do
|
||||||
|
INSTANCE_NETWORK="${INSTANCE_NETWORK_NAMES[$ITER]}"
|
||||||
|
ITER=$((ITER+1))
|
||||||
|
if [[ -n "$NETWORK" ]] && [[ "$NETWORK" != "$INSTANCE_NETWORK" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
ufw-docker--add-rule "$INSTANCE_NAME" "$IP" "${PORT_PROTO%/*}" "${PORT_PROTO#*/}" "${INSTANCE_NETWORK}"
|
||||||
|
RETVAL="$?"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$RETVAL" -ne 0 ]]; then
|
||||||
|
err "Fail to add rule(s), cannot find the published port ${INSTANCE_PORT}/${PROTO} of instance \"${INSTANCE_NAME}\" or cannot update outdated rule(s)."
|
||||||
|
fi
|
||||||
|
return "$RETVAL"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--add-service-rule() {
|
||||||
|
declare service_id="$1"
|
||||||
|
declare port="${2%/*}"
|
||||||
|
declare proto="${2#*/}"
|
||||||
|
|
||||||
|
declare target_ip_port
|
||||||
|
target_ip_port="$(iptables -t nat -L DOCKER-INGRESS | grep -E "^DNAT\\s+${proto}\\s+.+\\sto:[.0-9]+:${port}\$" | grep -Eo "[.0-9]+:${port}\$")"
|
||||||
|
|
||||||
|
[[ -z "$target_ip_port" ]] && die "Could not find VIP of service ${service_id}."
|
||||||
|
|
||||||
|
ufw-docker--add-rule "$service_id" "${target_ip_port%:*}" "$port" "$proto"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--add-rule() {
|
||||||
|
local INSTANCE_NAME="$1"
|
||||||
|
local INSTANCE_IP_ADDRESS="$2"
|
||||||
|
local PORT="$3"
|
||||||
|
local PROTO="$4"
|
||||||
|
local NETWORK="${5:-}"
|
||||||
|
|
||||||
|
declare comment
|
||||||
|
|
||||||
|
echo "allow ${INSTANCE_NAME} ${PORT}/${PROTO} ${NETWORK}"
|
||||||
|
typeset -a UFW_OPTS
|
||||||
|
UFW_OPTS=(route allow proto "${PROTO}"
|
||||||
|
from any to "$INSTANCE_IP_ADDRESS")
|
||||||
|
comment="allow ${INSTANCE_NAME}"
|
||||||
|
[[ -n "$PORT" ]] && {
|
||||||
|
UFW_OPTS+=(port "${PORT}")
|
||||||
|
comment="$comment ${PORT}/${PROTO}"
|
||||||
|
}
|
||||||
|
[[ -n "$NETWORK" ]] && {
|
||||||
|
comment="$comment ${NETWORK}"
|
||||||
|
}
|
||||||
|
UFW_OPTS+=(comment "$comment")
|
||||||
|
|
||||||
|
if ufw-docker--list "$INSTANCE_NAME" "$PORT" "$PROTO" "$NETWORK" &>/dev/null; then
|
||||||
|
ufw --dry-run "${UFW_OPTS[@]}" | grep "^Skipping" && return 0
|
||||||
|
err "Remove outdated rule."
|
||||||
|
ufw-docker--delete "$INSTANCE_NAME" "$PORT" "$PROTO" "$NETWORK"
|
||||||
|
fi
|
||||||
|
echo ufw "${UFW_OPTS[@]}"
|
||||||
|
ufw "${UFW_OPTS[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--instance-name() {
|
||||||
|
local INSTANCE_ID="$1"
|
||||||
|
{
|
||||||
|
{
|
||||||
|
docker inspect --format='{{.Name}}' "$INSTANCE_ID" 2>/dev/null | sed -e 's,^/,,' |
|
||||||
|
grep "^${GREP_REGEXP_INSTANCE_NAME}\$" 2>/dev/null
|
||||||
|
} || echo -n "$INSTANCE_ID";
|
||||||
|
} | remove_blank_lines
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--service() {
|
||||||
|
declare service_action="${1:-help}"
|
||||||
|
case "$service_action" in
|
||||||
|
delete)
|
||||||
|
shift || true
|
||||||
|
if [[ "${1:?Invalid 'delete' command syntax.}" != "allow" ]]; then
|
||||||
|
die "\"delete\" command only support removing allowed rules"
|
||||||
|
fi
|
||||||
|
shift || true
|
||||||
|
declare service_id_or_name="${1:?Missing swarm service name or service ID}"
|
||||||
|
|
||||||
|
"ufw-docker--service-${service_action}" "${service_id_or_name}"
|
||||||
|
;;
|
||||||
|
allow)
|
||||||
|
shift || true
|
||||||
|
declare service_id_or_name="${1:?Missing swarm service name or service ID}"
|
||||||
|
declare service_port="${2:?Missing the port number, such as '80/tcp'.}"
|
||||||
|
|
||||||
|
"ufw-docker--service-${service_action}" "${service_id_or_name}" "${service_port}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ufw-docker--help
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--get-service-id() {
|
||||||
|
declare service_name="$1"
|
||||||
|
docker service inspect "${service_name}" --format "{{.ID}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--get-service-name() {
|
||||||
|
declare service_name="$1"
|
||||||
|
docker service inspect "${service_name}" --format "{{.Spec.Name}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--service-allow() {
|
||||||
|
declare service_name="$1"
|
||||||
|
declare service_port="$2"
|
||||||
|
declare service_proto=tcp
|
||||||
|
|
||||||
|
if [[ -n "$service_port" ]] &&
|
||||||
|
! grep -E '^[0-9]+(/(tcp|udp))?$' <<< "$service_port" &>/dev/null; then
|
||||||
|
die "Invalid port syntax: $service_port"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$service_port" = */* ]]; then
|
||||||
|
service_proto="${service_port#*/}"
|
||||||
|
service_port="${service_port%/*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare service_id
|
||||||
|
service_id="$(ufw-docker--get-service-id "${service_name}")"
|
||||||
|
[[ -z "${service_id:-}" ]] && die "Could not find service \"$service_name\""
|
||||||
|
|
||||||
|
service_name="$(ufw-docker--get-service-name "${service_name}")"
|
||||||
|
|
||||||
|
exec 9< <(docker service inspect "$service_name" \
|
||||||
|
--format '{{range .Endpoint.Spec.Ports}}{{.PublishedPort}} {{.TargetPort}}/{{.Protocol}}{{"\n"}}{{end}}')
|
||||||
|
while read -u 9 -r port target_port; do
|
||||||
|
if [[ "$target_port" = "${service_port}/${service_proto}" ]]; then
|
||||||
|
declare service_env="ufw_public_${service_id}=${service_name}/${port}/${service_proto}"
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exec 9<&-
|
||||||
|
|
||||||
|
[[ -z "${service_env:-}" ]] && die "Service $service_name does not publish port $service_port."
|
||||||
|
|
||||||
|
if ! docker service inspect "$ufw_docker_agent" &>/dev/null; then
|
||||||
|
err "Not found ufw-docker-agent service, creating ..."
|
||||||
|
docker service create --name "$ufw_docker_agent" --mode global \
|
||||||
|
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
|
||||||
|
--mount type=bind,source=/etc/ufw,target=/etc/ufw,readonly=true \
|
||||||
|
--env ufw_docker_agent_image="${ufw_docker_agent_image}" \
|
||||||
|
--env DEBUG="${DEBUG:-}" \
|
||||||
|
--env "${service_env}" \
|
||||||
|
"${ufw_docker_agent_image}"
|
||||||
|
else
|
||||||
|
declare -a service_env_list
|
||||||
|
service_env_list+=(--env-add "${service_env}")
|
||||||
|
|
||||||
|
exec 8< <(ufw-docker--get-env-list)
|
||||||
|
while read -u 8 -r id value; do
|
||||||
|
[[ "$id" = "$service_id" ]] && continue
|
||||||
|
[[ "$value" = "${service_name}"/* ]] && service_env_list+=(--env-rm "ufw_public_${id}")
|
||||||
|
done
|
||||||
|
exec 8<&-
|
||||||
|
|
||||||
|
docker service update --update-parallelism=0 \
|
||||||
|
--env-add ufw_docker_agent_image="${ufw_docker_agent_image}" \
|
||||||
|
--env-add DEBUG="${DEBUG:-}" \
|
||||||
|
"${service_env_list[@]}" \
|
||||||
|
--image "${ufw_docker_agent_image}" \
|
||||||
|
"${ufw_docker_agent}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--get-env-list() {
|
||||||
|
docker service inspect "${ufw_docker_agent}" \
|
||||||
|
--format '{{range $k,$v := .Spec.TaskTemplate.ContainerSpec.Env}}{{ $v }}{{"\n"}}{{end}}' |
|
||||||
|
sed -e '/^ufw_public_/!d' \
|
||||||
|
-e 's/^ufw_public_//' \
|
||||||
|
-e 's/=/ /'
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--service-delete() {
|
||||||
|
declare service_name="$1"
|
||||||
|
|
||||||
|
exec 8< <(ufw-docker--get-env-list)
|
||||||
|
while read -u 8 -r id value; do
|
||||||
|
if [[ "$id" = "$service_name" ]] || [[ "$value" = "${service_name}"/* ]]; then
|
||||||
|
declare service_id="$id"
|
||||||
|
service_name="${value%%/*}"
|
||||||
|
declare service_env="ufw_public_${service_id}=${service_name}/deny"
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exec 8<&-
|
||||||
|
|
||||||
|
[[ -z "${service_env:-}" ]] && die "Could not find service \"$service_name\""
|
||||||
|
|
||||||
|
docker service update --update-parallelism=0 \
|
||||||
|
--env-add ufw_docker_agent_image="${ufw_docker_agent_image}" \
|
||||||
|
--env-add "${service_env}" \
|
||||||
|
--image "${ufw_docker_agent_image}" \
|
||||||
|
"${ufw_docker_agent}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--raw-command() {
|
||||||
|
ufw "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
after_rules="/etc/ufw/after.rules"
|
||||||
|
|
||||||
|
function ufw-docker--check() {
|
||||||
|
err "\\n########## iptables -n -L DOCKER-USER ##########"
|
||||||
|
iptables -n -L DOCKER-USER
|
||||||
|
|
||||||
|
err "\\n\\n########## diff $after_rules ##########"
|
||||||
|
ufw-docker--check-install && err "\\nCheck done."
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -a files_to_be_deleted
|
||||||
|
|
||||||
|
function rm-on-exit() {
|
||||||
|
[[ $# -gt 0 ]] && files_to_be_deleted+=("$@")
|
||||||
|
}
|
||||||
|
|
||||||
|
function on-exit() {
|
||||||
|
for file in "${files_to_be_deleted[@]:-}"; do
|
||||||
|
[[ -f "$file" ]] && rm -r "$file"
|
||||||
|
done
|
||||||
|
files_to_be_deleted=()
|
||||||
|
}
|
||||||
|
|
||||||
|
trap on-exit EXIT INT TERM QUIT ABRT ERR
|
||||||
|
|
||||||
|
function ufw-docker--check-install() {
|
||||||
|
after_rules_tmp="${after_rules_tmp:-$(mktemp)}"
|
||||||
|
rm-on-exit "$after_rules_tmp"
|
||||||
|
|
||||||
|
sed "/^# BEGIN UFW AND DOCKER/,/^# END UFW AND DOCKER/d" "$after_rules" > "$after_rules_tmp"
|
||||||
|
>> "${after_rules_tmp}" cat <<-\EOF
|
||||||
|
# BEGIN UFW AND DOCKER
|
||||||
|
*filter
|
||||||
|
:ufw-user-forward - [0:0]
|
||||||
|
:ufw-docker-logging-deny - [0:0]
|
||||||
|
:DOCKER-USER - [0:0]
|
||||||
|
-A DOCKER-USER -j ufw-user-forward
|
||||||
|
|
||||||
|
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
|
||||||
|
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
|
||||||
|
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
|
||||||
|
-A DOCKER-USER -j RETURN -s 100.64.0.0/10
|
||||||
|
|
||||||
|
-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
|
||||||
|
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 100.64.0.0/10
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
|
||||||
|
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 100.64.0.0/10
|
||||||
|
|
||||||
|
-A DOCKER-USER -j RETURN
|
||||||
|
|
||||||
|
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
|
||||||
|
-A ufw-docker-logging-deny -j DROP
|
||||||
|
|
||||||
|
COMMIT
|
||||||
|
# END UFW AND DOCKER
|
||||||
|
EOF
|
||||||
|
|
||||||
|
diff -u --color=auto "$after_rules" "$after_rules_tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--install() {
|
||||||
|
if ! ufw-docker--check-install; then
|
||||||
|
local after_rules_bak
|
||||||
|
after_rules_bak="${after_rules}-ufw-docker~$(date '+%Y-%m-%d-%H%M%S')~"
|
||||||
|
err "\\nBacking up $after_rules to $after_rules_bak"
|
||||||
|
cp "$after_rules" "$after_rules_bak"
|
||||||
|
cat "$after_rules_tmp" > "$after_rules"
|
||||||
|
err "Please restart UFW service manually by using the following command:"
|
||||||
|
if type systemctl &>/dev/null; then
|
||||||
|
err " sudo systemctl restart ufw"
|
||||||
|
else
|
||||||
|
err " sudo service ufw restart"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function ufw-docker--help() {
|
||||||
|
cat <<-EOF >&2
|
||||||
|
Usage:
|
||||||
|
ufw-docker <list|allow> [docker-instance-id-or-name [port[/tcp|/udp]] [network]]
|
||||||
|
ufw-docker delete allow [docker-instance-id-or-name [port[/tcp|/udp]] [network]]
|
||||||
|
|
||||||
|
ufw-docker service allow <swarm-service-id-or-name <port</tcp|/udp>>>
|
||||||
|
ufw-docker service delete allow <swarm-service-id-or-name>
|
||||||
|
|
||||||
|
ufw-docker <status|install|check|help>
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
ufw-docker help
|
||||||
|
|
||||||
|
ufw-docker check # Check the installation of firewall rules
|
||||||
|
ufw-docker install # Install firewall rules
|
||||||
|
|
||||||
|
ufw-docker status
|
||||||
|
|
||||||
|
ufw-docker list httpd
|
||||||
|
|
||||||
|
|
||||||
|
ufw-docker allow httpd
|
||||||
|
ufw-docker allow httpd 80
|
||||||
|
ufw-docker allow httpd 80/tcp
|
||||||
|
ufw-docker allow httpd 80/tcp default
|
||||||
|
|
||||||
|
ufw-docker delete allow httpd
|
||||||
|
ufw-docker delete allow httpd 80/tcp
|
||||||
|
ufw-docker delete allow httpd 80/tcp default
|
||||||
|
|
||||||
|
ufw-docker service allow httpd 80/tcp
|
||||||
|
|
||||||
|
ufw-docker service delete allow httpd
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_blank_lines() {
|
||||||
|
sed '/^[[:blank:]]*$/d'
|
||||||
|
}
|
||||||
|
|
||||||
|
function err() {
|
||||||
|
echo -e "$@" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
function die() {
|
||||||
|
err "ERROR:" "$@"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# __main__
|
||||||
|
|
||||||
|
if ! ufw status 2>/dev/null | grep -Fq "Status: active" ; then
|
||||||
|
die "UFW is disabled or you are not root user, or mismatched iptables legacy/nf_tables, current $(iptables --version)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker -v &> /dev/null; then
|
||||||
|
die "Docker executable not found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
ufw_action="${1:-help}"
|
||||||
|
|
||||||
|
case "$ufw_action" in
|
||||||
|
delete)
|
||||||
|
shift || true
|
||||||
|
if [[ "${1:?Invalid 'delete' command syntax.}" != "allow" ]]; then
|
||||||
|
die "\"delete\" command only support removing allowed rules"
|
||||||
|
fi
|
||||||
|
;&
|
||||||
|
list|allow)
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
INSTANCE_ID="${1:?Docker instance name/ID cannot be empty.}"
|
||||||
|
INSTANCE_NAME="$(ufw-docker--instance-name "$INSTANCE_ID")"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
INSTANCE_PORT="${1:-}"
|
||||||
|
if [[ -n "$INSTANCE_PORT" && ! "$INSTANCE_PORT" =~ [0-9]+(/(tcp|udp))? ]]; then
|
||||||
|
die "invalid port syntax: \"$INSTANCE_PORT\"."
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROTO="$DEFAULT_PROTO"
|
||||||
|
if [[ "$INSTANCE_PORT" = */udp ]]; then
|
||||||
|
PROTO=udp
|
||||||
|
fi
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
NETWORK="${1:-}"
|
||||||
|
|
||||||
|
INSTANCE_PORT="${INSTANCE_PORT%/*}"
|
||||||
|
|
||||||
|
"ufw-docker--$ufw_action" "$INSTANCE_NAME" "$INSTANCE_PORT" "$PROTO" "$NETWORK"
|
||||||
|
;;
|
||||||
|
service|raw-command|add-service-rule)
|
||||||
|
shift || true
|
||||||
|
"ufw-docker--$ufw_action" "$@"
|
||||||
|
;;
|
||||||
|
status|install|check)
|
||||||
|
ufw-docker--"$ufw_action"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ufw-docker--help
|
||||||
|
;;
|
||||||
|
esac
|
27
roles/docker/tasks/caddy.yml
Normal file
27
roles/docker/tasks/caddy.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
- name: make caddy directories
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item}}"
|
||||||
|
state: directory
|
||||||
|
loop:
|
||||||
|
- /opt/stacks/caddy
|
||||||
|
|
||||||
|
- name: copy Caddyfile
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: Caddyfile
|
||||||
|
dest: /opt/stacks/caddy/Caddyfile
|
||||||
|
owner: root
|
||||||
|
mode: 644
|
||||||
|
|
||||||
|
- name: copy caddy compose file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: caddy-compose.yml
|
||||||
|
dest: /opt/stacks/caddy/compose.yml
|
||||||
|
owner: root
|
||||||
|
mode: 644
|
||||||
|
|
||||||
|
- name: deploy caddy stack
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: /opt/stacks/caddy
|
||||||
|
recreate: always
|
||||||
|
files:
|
||||||
|
- compose.yml
|
19
roles/docker/tasks/gitea.yml
Normal file
19
roles/docker/tasks/gitea.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
- name: make caddy directories
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item}}"
|
||||||
|
state: directory
|
||||||
|
loop:
|
||||||
|
- /opt/stacks/gitea
|
||||||
|
|
||||||
|
- name: copy gitea compose file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: gitea-compose.yml
|
||||||
|
dest: /opt/stacks/gitea/compose.yml
|
||||||
|
owner: root
|
||||||
|
mode: 644
|
||||||
|
|
||||||
|
- name: deploy gitea stack
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: /opt/stacks/gitea
|
||||||
|
files:
|
||||||
|
- compose.yml
|
65
roles/docker/tasks/main.yml
Normal file
65
roles/docker/tasks/main.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
- name: Install required system packages
|
||||||
|
apt:
|
||||||
|
pkg:
|
||||||
|
- apt-transport-https
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- software-properties-common
|
||||||
|
- python3-pip
|
||||||
|
- virtualenv
|
||||||
|
- python3-setuptools
|
||||||
|
state: latest
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Add Docker GPG apt Key
|
||||||
|
apt_key:
|
||||||
|
url: https://download.docker.com/linux/ubuntu/gpg
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Add Docker Repository
|
||||||
|
apt_repository:
|
||||||
|
repo: deb https://download.docker.com/linux/debian bookworm stable
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update apt and install docker-ce
|
||||||
|
apt:
|
||||||
|
name: docker-ce
|
||||||
|
state: latest
|
||||||
|
update_cache: true
|
||||||
|
|
||||||
|
- name: copy over ufw-docker.sh
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: ufw-docker.sh
|
||||||
|
dest: /usr/local/bin/ufw-docker.sh
|
||||||
|
owner: root
|
||||||
|
mode: 755
|
||||||
|
|
||||||
|
- name: execute ufw-docker.sh
|
||||||
|
ansible.builtin.shell: /usr/local/bin/ufw-docker.sh install
|
||||||
|
|
||||||
|
- name: make directories
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
loop:
|
||||||
|
- /opt/stacks
|
||||||
|
- /opt/dockge
|
||||||
|
|
||||||
|
- name: copy dockge compose file
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: dockge-compose.yml
|
||||||
|
dest: /opt/dockge/dockge.yml
|
||||||
|
owner: root
|
||||||
|
mode: 644
|
||||||
|
|
||||||
|
- name: deploy dockge stack
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: /opt/dockge
|
||||||
|
files:
|
||||||
|
- dockge.yml
|
||||||
|
|
||||||
|
- name: Install caddy
|
||||||
|
import_tasks: caddy.yml
|
||||||
|
|
||||||
|
- name: Install gitea
|
||||||
|
import_tasks: gitea.yml
|
Loading…
Reference in New Issue
Block a user