feat(ansible): extract playbooks and roles into standalone repository

This commit is contained in:
Haitao Pan 2025-12-21 19:09:46 +08:00
parent 99029986aa
commit 3344b1e530
68 changed files with 14512 additions and 0 deletions

6
common_setup.yml Normal file
View File

@ -0,0 +1,6 @@
- name: Run infrastructure setup
hosts: all
become: yes
gather_facts: yes
roles:
- vhosts/common

6
k3s-cluster.yaml Normal file
View File

@ -0,0 +1,6 @@
- name: Run K3S-cluster setup
hosts: all
become: yes
gather_facts: yes
roles:
- roles/vhosts/k3s-cluster

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
enable_set_timezone: true # 默认启用 Set timezone
enable_set_hostname: true # 默认启用 Set hostname
enable_install_packages: true # 默认不安装额外的软件包
enable_all_hosts_update: false # 默认不更新所有主机的条目
rsyslog_log_rotation: # 可选的日志管理配置
enable: true # 启用 rsyslog 日志管理
rotate_count: 4 # 默认保留的日志文件数量
rotate_frequency: weekly # 默认每周轮换, 可选daily, hourly
max_log_size: 100M # 默认日志文件最大大小
journald_log_rotation: # 启用 journald 日志管理
enable: true # 启用 journald 日志管理
max_log_size: 100M # 默认日志文件最大大小
max_files: 100 # 默认保留的最大日志文件数
max_file_sec: 1month # 默认日志文件保存的最大时长
system_max_use: 1G # 默认系统日志最大使用空间
runtime_max_use: 500M # 默认运行时日志最大使用空间

View File

@ -0,0 +1,5 @@
#!/bin/bash
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update
sudo apt-get install -y vim iputils-ping rsync wireguard-tools

View File

@ -0,0 +1,11 @@
#!/bin/bash
# 设置 ~/.ssh/ 目录的权限
sudo chmod 700 ~/.ssh
# 设置 ~/.ssh/authorized_keys 文件的权限
sudo chmod 600 ~/.ssh/authorized_keys
# 使用 chattr +i 确保 authorized_keys 文件不能被删除
sudo chattr +i ~/.ssh/authorized_keys || true

View File

@ -0,0 +1,10 @@
---
- name: Restart logrotate service
service:
name: logrotate
state: restarted
- name: Restart systemd-journald service
service:
name: systemd-journald
state: restarted

View File

@ -0,0 +1,7 @@
---
- name: Configure journald log rotation using template
template:
src: journald_logrotate.j2
dest: /etc/systemd/journald.conf
when: journald_log_rotation.enable
notify: Restart systemd-journald service

View File

@ -0,0 +1,7 @@
---
- name: Configure logrotate for rsyslog using template
template:
src: rsyslog_logrotate.j2
dest: /etc/logrotate.d/rsyslog
when: rsyslog_log_rotation.enable
notify: Restart logrotate service

View File

@ -0,0 +1,27 @@
- name: Stop systemd-resolved
systemd:
name: systemd-resolved
state: stopped
enabled: no
- name: Remove /etc/resolv.conf if it's a symlink
file:
path: /etc/resolv.conf
state: absent
force: true
- name: Create static /etc/resolv.conf
copy:
dest: /etc/resolv.conf
content: |
nameserver 8.8.8.8
nameserver 1.1.1.1
owner: root
group: root
mode: '0644'
- name: Optionally make resolv.conf immutable to prevent changes
command: chattr +i /etc/resolv.conf
args:
warn: false
when: make_resolv_conf_immutable | default(false)

View File

@ -0,0 +1,17 @@
- name: Add NVIDIA repository
shell: |
add-apt-repository -y ppa:graphics-drivers
curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | tee /etc/apt/sources.list.d/nvidia-container-runtime.list
apt-get update
- name: Install NVIDIA driver and container runtime
apt:
name:
- nvidia-modprobe
- nvidia-driver-535
- nvidia-headless-535
- nvidia-container-runtime
state: present
update_cache: yes

View File

@ -0,0 +1,38 @@
- name: Set timezone
shell: "timedatectl set-timezone Asia/Shanghai"
- name: Set hostname
shell: "hostname -F /etc/hostname"
- name: update /etc/hostname
template: src=templates/hostname dest=/etc/hostname owner=root group=root mode=0644 unsafe_writes=yes
- name: Update /etc/hosts
template: src=templates/hosts dest=/etc/hosts owner=root group=root mode=0644 force=yes unsafe_writes=yes
- name: Set systemd-resolved and set static DNS
include_tasks: setup-systemd-resolved.yml
- name: Install packages
script: files/install-packages.sh
when: (ansible_facts['distribution'] == "Ubuntu") or (ansible_facts['distribution'] == "Debian")
- name: Include Privoxy sub task for SOCKS5 to HTTP proxy (optional)
include_tasks: setup-privoxy.yml
when: privoxy.enable | default(false)
#- name: Include GPU Configuration
# include_tasks: include_gpu.yaml
# when: (ansible_facts['distribution'] == "Ubuntu") or (ansible_facts['distribution'] == "Debian")
# tags:
# - k3s
# - gpu
# - nvidia
#- name: enable ip_forward
# shell: 'echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf; echo "net.ipv4.conf.all.proxy_arp = 1" >> /etc/sysctl.conf ; sysctl -p /etc/sysctl.conf'
#- name: Install packages
# shell: "yum makecache && yum install -y audit container-selinux"
# when: (ansible_facts['distribution'] != "Ubuntu") or (ansible_facts['distribution'] != "Debian")

View File

@ -0,0 +1,12 @@
- name: Check if systemctl is available
command: which hostnamectl
register: systemctl_check
ignore_errors: true
- name: Set hostname using systemctl if available
shell: "hostnamectl set-hostname {{ inventory_hostname }}"
when: systemctl_check.rc == 0
- name: Set hostname using hostname -F if systemctl is not available
shell: "hostname -F /etc/hostname"
when: systemctl_check.rc != 0

View File

@ -0,0 +1,2 @@
- name: Set timezone
shell: "timedatectl set-timezone Asia/Shanghai"

View File

@ -0,0 +1,24 @@
---
- name: Install privoxy (Debian)
apt:
name: privoxy
state: present
when: ansible_os_family == 'Debian'
- name: Install privoxy (RedHat)
yum:
name: privoxy
state: present
when: ansible_os_family == 'RedHat'
- name: Ensure SOCKS5 forwarding is configured in privoxy
lineinfile:
path: /etc/privoxy/config
line: "forward-socks5t / {{ proxy.socks5_host }}:{{ proxcy.socks5_port }} ."
state: present
- name: Restart and enable privoxy
systemd:
name: privoxy
state: restarted
enabled: yes

View File

@ -0,0 +1,36 @@
# playbooks/setup-systemd-resolved.yml
- name: Ensure systemd-resolved is installed
package:
name: systemd-resolved
state: present
- name: Enable and start systemd-resolved
systemd:
name: systemd-resolved
enabled: yes
state: started
- name: Configure /etc/systemd/resolved.conf
ini_file:
path: /etc/systemd/resolved.conf
section: "Resolve"
option: "{{ item.option }}"
value: "{{ item.value }}"
mode: '0644'
loop:
- { option: "DNSStubListener", value: "no" }
- { option: "DNS", value: "" }
- { option: "FallbackDNS", value: "" }
- name: Restart systemd-resolved
systemd:
name: systemd-resolved
state: restarted
daemon_reload: yes
- name: Ensure /etc/resolv.conf points to /run/systemd/resolve/resolv.conf
file:
src: /run/systemd/resolve/resolv.conf
dest: /etc/resolv.conf
state: link
force: true

View File

@ -0,0 +1,3 @@
{% for item in ssh_keys %}
{{ item }}
{% endfor %}

View File

@ -0,0 +1 @@
{{ inventory_hostname }}

View File

@ -0,0 +1,26 @@
# IPv4 localhost configuration
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
# IPv6 localhost configuration
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# IPv6 Local addresses (desirable for IPv6 capable hosts)
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
{{ ansible_default_ipv4.address }} {{ inventory_hostname }}
{% if enable_all_hosts_update is defined and enable_all_hosts_update %}
{% for item in groups['all'] %}
{{ hostvars[item]['ansible_host'] }} {{ item }}
{% endfor %}
{% endif %}
{% if extra_domain is defined %}
{% for ip, domain_name in extra_domain.items() %}
{{ ip }} {{ domain_name }}
{% endfor %}
{% endif %}

View File

@ -0,0 +1,5 @@
[Journal]
SystemMaxUse={{ journald_log_rotation.system_max_use }} # 设置最大日志使用空间
SystemMaxFiles={{ journald_log_rotation.max_files }} # 设置最大日志文件数
MaxFileSec={{ journald_log_rotation.max_file_sec }} # 设置日志文件的轮换频率(例如 weekly, daily, hourly
RuntimeMaxUse={{ journald_log_rotation.runtime_max_use }} # 设置运行时日志最大使用空间

View File

@ -0,0 +1,8 @@
/var/log/prometheus-agent.log
/var/log/prometheus-transfer.log {
rotate 12
monthly
compress
missingok
notifempty
}

View File

@ -0,0 +1,23 @@
/var/log/syslog
/var/log/mail* # 包括所有以 mail 开头的日志文件
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/lpr.log
/var/log/cron.log
/var/log/debug
/var/log/messages
{
rotate {{ rsyslog_log_rotation.rotate_count }}
{{ rsyslog_log_rotation.rotate_frequency }}
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
maxsize {{ rsyslog_log_rotation.max_log_size }}
}

View File

@ -0,0 +1,300 @@
#!/bin/bash
#https://github.com/containerd/nerdctl/releases/download/v2.0.2/nerdctl-2.0.2-linux-amd64.tar.gz
#https://github.com/containerd/nerdctl/releases/download/v2.0.2/nerdctl-full-2.0.2-linux-amd64.tar.gz
#wget https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-amd64-v1.6.2.tgz
#!/bin/bash
set -e
# =============================================
# ✅ 环境变量检查(可配置)
# =============================================
: "${REGISTRY_DOMAIN:=kube.registry.local}"
: "${REGISTRY_PORT:=5000}"
: "${NERDCTL_VERSION:=v2.0.2}"
: "${CNI_VERSION:=v1.6.2}"
: "${CNI_DIR:=/opt/cni/bin}"
: "${CERT_DIR:=/opt/registry/certs}"
: "${CONFIG_DIR:=/opt/registry/config}"
: "${REGISTRY_DATA:=/var/lib/registry}"
: "${REGISTRY_YAML:=registry.yaml}"
: "${COMPOSE_YAML:=compose.yaml}"
: "${TAR_FILE:=registry.tar}"
# =============================================
# ✅ 自动检测 containerd.sock
# =============================================
if [[ -S "/run/k3s/containerd/containerd.sock" ]]; then
export CONTAINERD_ADDRESS="/run/k3s/containerd/containerd.sock"
elif [[ -S "/run/containerd/containerd.sock" ]]; then
export CONTAINERD_ADDRESS="/run/containerd/containerd.sock"
elif [[ -S "/var/run/containerd/containerd.sock" ]]; then
export CONTAINERD_ADDRESS="/var/run/containerd/containerd.sock"
else
echo "❌ 未检测到有效的 containerd.sock请确认 containerd 是否正常运行。"
exit 1
fi
export NERDCTL_NAMESPACE="k8s.io"
# =============================================
echo "📦 准备 nerdctl 全功能版..."
if ! command -v nerdctl &>/dev/null; then
if [ ! -f /tmp/nerdctl-full.tgz ]; then
echo "⬇️ 下载 nerdctl..."
wget -O /tmp/nerdctl-full.tgz \
"https://github.com/containerd/nerdctl/releases/download/${NERDCTL_VERSION}/nerdctl-full-${NERDCTL_VERSION#v}-linux-amd64.tar.gz"
else
echo "📦 已存在 nerdctl-full.tgz跳过下载"
fi
echo "📦 解压 nerdctl 到 /usr/local..."
sudo tar -C /usr/local -xzf /tmp/nerdctl-full.tgz
echo "✅ nerdctl 安装完成: $(nerdctl --version)"
else
echo "✅ nerdctl 已存在: $(nerdctl --version)"
fi
# =============================================
echo "📦 安装 CNI 插件..."
if [ ! -f "${CNI_DIR}/bridge" ]; then
if [ ! -f /tmp/cni.tgz ]; then
echo "⬇️ 下载 CNI 插件..."
wget -O /tmp/cni.tgz \
"https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-amd64-${CNI_VERSION}.tgz"
else
echo "📦 已存在 cni.tgz跳过下载"
fi
sudo mkdir -p "${CNI_DIR}"
sudo tar -C "${CNI_DIR}" -xzf /tmp/cni.tgz
echo "✅ CNI 插件已安装到: ${CNI_DIR}"
else
echo "✅ CNI 插件已存在: ${CNI_DIR}/bridge"
fi
# =============================================
echo "📦 解压 SSL 证书..."
if [ ! -f "ssl_certificates.tar.gz" ]; then
echo "⬇️ 未找到 ssl_certificates.tar.gz尝试从 GitHub 下载..."
wget -O ssl_certificates.tar.gz \
"https://github.com/svc-design/ansible/releases/download/release-self-signed-cert_kube.registry.local/ssl_certificates.tar.gz" || {
echo "❌ 无法下载 ssl_certificates.tar.gz终止执行"
exit 1
}
else
if [ -f "ssl_certificates.tar.gz" ]; then
mkdir -p "$CERT_DIR"
tar -xvpf ssl_certificates.tar.gz
tar -xvpf ssl_certificates.tar.gz -C "$CERT_DIR"
echo "✅ 证书已解压至: $CERT_DIR"
fi
fi
# =============================================
# ============ 生成 registry-config ============
echo "⚙️ 准备 registry 配置..."
sudo mkdir -pv "$CONFIG_DIR"
sudo mkdir -pv "$REGISTRY_DATA"
echo "📝 写入 registry-config.yaml..."
sudo cat > "${CONFIG_DIR}/${REGISTRY_YAML}" <<EOF
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :$REGISTRY_PORT
headers:
X-Content-Type-Options: [nosniff]
tls:
certificate: /etc/docker/registry/domain.crt
key: /etc/docker/registry/domain.key
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
EOF
echo "✅ 写入完成: $REGISTRY_CONFIG"
# ========== 生成 registry.yaml ==========
echo "🛠️ 生成 registry 配置..."
sudo mkdir -p "$CONFIG_DIR"
cat <<EOF | sudo tee "${CONFIG_DIR}/registry.yaml" > /dev/null
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :${REGISTRY_PORT}
headers:
X-Content-Type-Options: [nosniff]
tls:
certificate: /etc/docker/registry/domain.crt
key: /etc/docker/registry/domain.key
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
EOF
echo "✅ registry.yaml 已创建"
# ========== 生成 compose.yaml ==========
echo "🛠️ 生成 compose 配置..."
cat <<EOF | sudo tee "${CONFIG_DIR}/compose.yaml" > /dev/null
services:
registry:
image: registry:latest
container_name: registry
restart: always
network_mode: host
volumes:
- /var/lib/registry:/var/lib/registry
- ${CONFIG_DIR}/registry.yaml:/etc/docker/registry/config.yml
- ${CERT_DIR}/kube.registry.local.cert:/etc/docker/registry/domain.crt
- ${CERT_DIR}/kube.registry.local.key:/etc/docker/registry/domain.key
EOF
echo "✅ compose.yaml 已创建"
# =============================================
echo "📦 导入本地 registry 镜像..."
if [ -f "/usr/local/deepflow/$TAR_FILE" ]; then
sudo CONTAINERD_ADDRESS="$CONTAINERD_ADDRESS" nerdctl --namespace $NERDCTL_NAMESPACE load -i "/usr/local/deepflow/$TAR_FILE"
else
echo "⚠️ 本地镜像文件不存在:/usr/local/deepflow/$TAR_FILE"
fi
# =============================================
echo "🔁 重启 registry 服务..."
sudo CONTAINERD_ADDRESS="$CONTAINERD_ADDRESS" nerdctl --namespace $NERDCTL_NAMESPACE compose -f "$CONFIG_DIR/compose.yaml" down || true
sudo CONTAINERD_ADDRESS="$CONTAINERD_ADDRESS" nerdctl --namespace $NERDCTL_NAMESPACE compose -f "$CONFIG_DIR/compose.yaml" up -d
# =============================================
echo "🔗 添加 hosts 映射..."
if ! grep -q "$REGISTRY_DOMAIN" /etc/hosts; then
echo "127.0.0.1 $REGISTRY_DOMAIN" | sudo tee -a /etc/hosts
echo "✅ /etc/hosts 已添加 $REGISTRY_DOMAIN"
else
echo "✅ hosts 中已存在 $REGISTRY_DOMAIN"
fi
echo "✅ Registry 启动成功: https://$REGISTRY_DOMAIN:$REGISTRY_PORT"
# =============================================
echo "🔐 安装 CA 证书到系统信任目录..."
CA_CERT="${CERT_DIR}/ca.cert"
if [ ! -f "$CA_CERT" ]; then
echo "❌ 未找到 CA 证书: $CA_CERT"
else
if grep -qi "ubuntu\|debian" /etc/os-release; then
sudo cp "$CA_CERT" "/usr/local/share/ca-certificates/kube-registry-ca.crt"
sudo update-ca-certificates
echo "✅ 已导入 CA 到 Ubuntu/Debian 系统信任目录"
elif grep -qi "rhel\|centos\|rocky" /etc/os-release; then
sudo cp "$CA_CERT" "/etc/pki/ca-trust/source/anchors/kube-registry-ca.crt"
sudo update-ca-trust extract
echo "✅ 已导入 CA 到 RHEL/CentOS 系统信任目录"
else
echo "⚠️ 未知发行版,跳过系统 CA 导入"
fi
fi
# =============================================
echo "🐳 安装 CA 到容器运行时 (Docker/Containerd)..."
# --- Docker CA ---
if command -v docker &>/dev/null; then
echo "🔧 配置 Docker..."
DOCKER_CA_DIR="/etc/docker/certs.d/kube.registry.local"
sudo mkdir -p "$DOCKER_CA_DIR"
sudo cp "$CA_CERT" "${DOCKER_CA_DIR}/ca.crt"
echo "✅ 已导入 CA 到 Docker: $DOCKER_CA_DIR"
sudo systemctl restart docker
fi
# --- Containerd CA ---
if command -v containerd &>/dev/null || [ -S "$CONTAINERD_SOCK" ]; then
echo "🔧 配置 Containerd..."
# Alpine/K3s: /etc/containerd/certs.d
# cri-o/nerdctl: /etc/containerd/certs.d/kube.registry.local/ca.crt
CONTAINERD_CA_DIR="/etc/containerd/certs.d/kube.registry.local"
sudo mkdir -p "$CONTAINERD_CA_DIR"
sudo cp "$CA_CERT" "${CONTAINERD_CA_DIR}/ca.crt"
echo "✅ 已导入 CA 到 Containerd: $CONTAINERD_CA_DIR"
sudo systemctl restart containerd || echo "⚠️ containerd 重启失败,可能在 K3s 中不适用"
fi
# --- K3s CA ---
if [[ -S "/run/k3s/containerd/containerd.sock" ]]; then
echo "🔧 检测到 K3s 环境,准备配置自定义 registry CA..."
# === 配置参数 ===
REGISTRY_DOMAIN="kube.registry.local"
REGISTRY_PORT="5000"
CA_CERT_PATH="/opt/registry/certs/ca.cert"
REGISTRIES_YAML="/etc/rancher/k3s/registries.yaml"
CA_DST_DIR="/etc/rancher/k3s/registries.d/${REGISTRY_DOMAIN}"
CA_DST_FILE="${CA_DST_DIR}/ca.crt"
# === 准备目录并拷贝证书 ===
sudo mkdir -p "${CA_DST_DIR}"
sudo cp "${CA_CERT_PATH}" "${CA_DST_FILE}"
# === 写入 registries.yaml ===
echo "[INFO] 写入 registries.yaml 配置..."
sudo tee "${REGISTRIES_YAML}" > /dev/null <<EOF
mirrors:
"${REGISTRY_DOMAIN}:${REGISTRY_PORT}":
endpoint:
- "https://${REGISTRY_DOMAIN}:${REGISTRY_PORT}"
configs:
"${REGISTRY_DOMAIN}:${REGISTRY_PORT}":
tls:
ca_file: "${CA_DST_FILE}"
EOF
cat /etc/rancher/k3s/registries.yaml << EOF
mirrors:
"kube.registry.local:5000":
endpoint:
- "http://kube.registry.local:5000"
configs:
"kube.registry.local:5000":
tls:
insecure_skip_verify: true
EOF
# === 重启 K3s 生效 ===
echo "[INFO] 重启 K3s 服务..."
if systemctl list-units --type=service | grep -q 'k3s-agent'; then
sudo systemctl restart k3s-agent
else
sudo systemctl restart k3s
fi
echo "[✅ SUCCESS] 已配置自定义 registry 并导入 CAhttps://${REGISTRY_DOMAIN}:${REGISTRY_PORT}"
fi

View File

@ -0,0 +1,37 @@
#!/bin/bash
set -e
# === 必要参数 ===
INSTALL_K3S_URL="https://get.k3s.io"
FLANNEL_IFACE=${FLANNEL_IFACE:-br0}
# === 安装命令拼接(最简)===
INSTALL_K3S_EXEC="server \
--disable=traefik,servicelb,local-storage \
--data-dir=/opt/rancher/k3s \
--advertise-address=$(hostname -I | awk '{print $1}') \
--kube-apiserver-arg=service-node-port-range=0-50000"
[[ -n "$FLANNEL_IFACE" ]] && INSTALL_K3S_EXEC+=" --flannel-iface=${FLANNEL_IFACE}"
# === 下载并执行安装 ===
curl -sfL ${INSTALL_K3S_URL} -o install_k3s.sh && chmod +x install_k3s.sh
INSTALL_K3S_EXEC="$INSTALL_K3S_EXEC" ./install_k3s.sh
# === 等待 CoreDNS 启动 ===
echo "⏳ 等待 CoreDNS 启动..."
until kubectl get pods -A 2>/dev/null | grep -q "coredns.*Running"; do
sleep 3
done
# === 设置本地 kubeconfig ===
mkdir -p ~/.kube
cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
chmod 600 ~/.kube/config
export KUBECONFIG=~/.kube/config
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
echo "✅ K3s 安装完成kubectl/helm 已就绪"

View File

@ -0,0 +1,44 @@
---
- name: Ensure remote tmp directory exists
file:
path: /tmp/ansible-{{ ansible_user }}
state: directory
mode: '0755'
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
- name: Sync setup_k3s.sh to remote
synchronize:
src: files/setup_k3s.sh
dest: /tmp/ansible-{{ ansible_user }}/setup_k3s.sh
mode: push
delegate_to: localhost
become: false
- name: Sync set-registry.sh to remote
synchronize:
src: files/set-registry.sh
dest: /tmp/ansible-{{ ansible_user }}/set-registry.sh
mode: push
delegate_to: localhost
become: false
- name: Ensure setup_k3s.sh is executable
file:
path: /tmp/ansible-{{ ansible_user }}/setup_k3s.sh
mode: '0755'
- name: Ensure set-registry.sh is executable
file:
path: /tmp/ansible-{{ ansible_user }}/set-registry.sh
mode: '0755'
- name: Run setup_k3s.sh
command: ./setup_k3s.sh
args:
chdir: /tmp/ansible-{{ ansible_user }}
- name: Run set-registry.sh
command: ./set-registry.sh
args:
chdir: /tmp/ansible-{{ ansible_user }}

View File

@ -0,0 +1,6 @@
---
# 从 vpn-overlay.yaml 加载 overlay 结构
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
# 脚本默认路径
dnat_script_path: /usr/local/bin/setup-dnat.sh

View File

@ -0,0 +1,40 @@
---
- name: 加载 overlay 配置(标准 YAML
set_fact:
overlay_data: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: 提取当前节点信息(作为 current_node
set_fact:
current_node: >-
{{ (overlay_data.sites + overlay_data.hubs)
| selectattr('name', 'equalto', inventory_hostname)
| list | first }}
- name: 设置本节点 DNAT 所需变量
set_fact:
dnat_public_ip: "{{ current_node.public_ip }}"
dnat_internal_ip: "{{ current_node.wg_ip }}"
pod_cidr: "{{ current_node.pod_cidr }}"
wireguard_cidr: "{{ current_node.wireguard_cidr }}"
- name: 模板渲染 DNAT 脚本
template:
src: setup-dnat.sh.j2
dest: "{{ dnat_script_path }}"
mode: "0755"
- name: 安装 systemd 服务
template:
src: dnat-rules.service.j2
dest: /etc/systemd/system/dnat-rules.service
mode: "0644"
- name: Reload systemd daemon
command: systemctl daemon-reexec
changed_when: false
- name: 启动并启用 DNAT 服务
systemd:
name: dnat-rules.service
enabled: true
state: started

View File

@ -0,0 +1,12 @@
[Unit]
Description=Setup DNAT rules for exposing services via WireGuard
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart={{ dnat_script_path }}
RemainAfterExit=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,26 @@
#!/bin/bash
ACTION=$1
if [[ "$ACTION" == "clean" ]]; then
echo "[DNAT] 清理规则..."
iptables -t nat -D PREROUTING -p tcp -d {{ dnat_public_ip }} --dport 80 -j DNAT --to-destination {{ dnat_internal_ip }}:80
iptables -t nat -D PREROUTING -p tcp -d {{ dnat_public_ip }} --dport 443 -j DNAT --to-destination {{ dnat_internal_ip }}:443
iptables -D FORWARD -p tcp -d {{ pod_cidr }} --dport 80 -j ACCEPT
iptables -D FORWARD -p tcp -d {{ pod_cidr }} --dport 443 -j ACCEPT
iptables -D FORWARD -p tcp -d {{ wireguard_cidr }} --dport 80 -j ACCEPT
iptables -D FORWARD -p tcp -d {{ wireguard_cidr }} --dport 443 -j ACCEPT
else
echo "[DNAT] 添加规则..."
iptables -t nat -A PREROUTING -p tcp -d {{ dnat_public_ip }} --dport 80 -j DNAT --to-destination {{ dnat_internal_ip }}:80
iptables -t nat -A PREROUTING -p tcp -d {{ dnat_public_ip }} --dport 443 -j DNAT --to-destination {{ dnat_internal_ip }}:443
iptables -A FORWARD -p tcp -d {{ pod_cidr }} --dport 80 -j ACCEPT
iptables -A FORWARD -p tcp -d {{ pod_cidr }} --dport 443 -j ACCEPT
iptables -A FORWARD -p tcp -d {{ wireguard_cidr }} --dport 80 -j ACCEPT
iptables -A FORWARD -p tcp -d {{ wireguard_cidr }} --dport 443 -j ACCEPT
fi

View File

@ -0,0 +1,7 @@
---
# Default VXLAN settings for site nodes
vxlan_dev_if: wg0
vxlan_vni: 100
vxlan_cidr_suffix: 16
overlay_config_path: config/sit/vpn-overlay.yaml

View File

@ -0,0 +1,83 @@
#!/bin/bash
# 安全增强版 VXLAN Overlay 脚本(支持 wg0 作为通道设备 + 可选公网 DNAT 映射)
# 用法: ./setup_sit_vxlan.sh <dev_if> <local_ip> <remote_ip> <br0_ip> [cidr_suffix] [vxlan_id] [mtu] [expose_port]
set -e
DEV_IF="$1"
LOCAL_IP="$2"
REMOTE_IP="$3"
BRIDGE_IP="$4"
CIDR_SUFFIX="${5:-16}"
VNI="${6:-100}"
MTU="${7:-1400}"
EXPOSE_PORT="${8:-443}"
if [[ -z "$DEV_IF" || -z "$LOCAL_IP" || -z "$REMOTE_IP" || -z "$BRIDGE_IP" ]]; then
echo "Usage: $0 <dev_if> <local_ip> <remote_ip> <br0_ip> [cidr_suffix] [vxlan_id] [mtu] [expose_port]"
exit 1
fi
VXLAN_IF="vxlan${VNI}"
BR_IF="br0"
BRIDGE_CIDR="${BRIDGE_IP}/${CIDR_SUFFIX}"
SUBNET="$(echo "$BRIDGE_IP" | cut -d. -f1-2).0.0/${CIDR_SUFFIX}"
# 自动判断 dev 是否能用于 VXLAN需支持广播
function is_vxlan_dev_usable() {
[[ -d "/sys/class/net/$1" ]] && grep -q "broadcast" "/sys/class/net/$1/flags"
}
echo "🔍 检查 $DEV_IF 是否可用于 VXLAN..."
USE_DEV_PARAM=true
if ! is_vxlan_dev_usable "$DEV_IF"; then
echo "⚠️ $DEV_IF 不支持广播,将省略 dev 参数(通过路由走隧道)"
USE_DEV_PARAM=false
fi
# 清理旧接口
for iface in "$VXLAN_IF" "$BR_IF"; do
ip link show "$iface" &>/dev/null && ip link set "$iface" down && ip link del "$iface"
done
# 创建 VXLAN 接口
echo "🛠️ 创建 VXLAN 接口 $VXLAN_IF ..."
if $USE_DEV_PARAM; then
ip link add "$VXLAN_IF" type vxlan id "$VNI" dstport 4789 local "$LOCAL_IP" remote "$REMOTE_IP" dev "$DEV_IF"
else
ip link add "$VXLAN_IF" type vxlan id "$VNI" dstport 4789 local "$LOCAL_IP" remote "$REMOTE_IP"
fi
ip link set "$VXLAN_IF" mtu "$MTU"
ip link set "$VXLAN_IF" up
# 创建 br0 桥
ip link add "$BR_IF" type bridge
ip link set "$BR_IF" mtu "$MTU"
ip link set "$VXLAN_IF" master "$BR_IF"
ip link set "$BR_IF" up
# 配置 IP
ip addr add "$BRIDGE_CIDR" dev "$BR_IF"
# 启用转发 + SNAT仅主机出网时
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -C POSTROUTING -s "$SUBNET" -o "$DEV_IF" -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s "$SUBNET" -o "$DEV_IF" -j MASQUERADE
# ⚠️ 可选:仅在 EXPOSE_PORT 被定义时启用 DNAT 公网端口映射
if [[ -n "$EXPOSE_PORT" ]]; then
echo "🌐 添加 DNAT 映射规则:公网:$EXPOSE_PORT${BRIDGE_IP}:443"
iptables -t nat -C PREROUTING -p tcp -m tcp --dport "$EXPOSE_PORT" -j DNAT --to-destination "${BRIDGE_IP}:443" 2>/dev/null || \
iptables -t nat -A PREROUTING -p tcp -m tcp --dport "$EXPOSE_PORT" -j DNAT --to-destination "${BRIDGE_IP}:443"
fi
# 展示结果
echo ""
echo "✅ VXLAN Overlay 已建立"
echo " - bridge: $BR_IF ($BRIDGE_CIDR)"
echo " - vxlan: $VXLAN_IF ($LOCAL_IP$REMOTE_IP, id=$VNI, mtu=$MTU)"
echo " - SNAT: $SUBNET$DEV_IF"
if [[ -n "$EXPOSE_PORT" ]]; then
echo " - DNAT: 公网:$EXPOSE_PORT${BRIDGE_IP}:443"
fi

View File

@ -0,0 +1,50 @@
---
- name: Load site overlay config
set_fact:
overlay_data: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: Select current site from config
set_fact:
current_hub: "{{ overlay_data.hubs | selectattr('name', 'equalto', inventory_hostname) | list | first }}"
- name: Fail if no matching site found
fail:
msg: "当前主机 {{ inventory_hostname }} 未在 config 中定义"
when: current_hub is not defined
- name: Set VXLAN parameters
set_fact:
vxlan_dev_if: "{{ current_hub.interface}}"
vxlan_local_ip: "{{ current_hub.local_ip }}"
vxlan_remote_ip: "{{ current_hub.remote_ip }}"
vxlan_br_ip: "{{ current_hub.br_ip }}"
vxlan_cidr_suffix: "{{ overlay_data.vxlan_cidr_suffix | default(16) }}"
vxlan_vni: "{{ overlay_data.vxlan_id | default(100) }}"
- name: 使用 rsync 分发 VXLAN 脚本
synchronize:
src: "files/setup_sit_vxlan.sh"
dest: /tmp/setup_sit_vxlan.sh
mode: push
- name: 移动脚本并赋权到 /usr/local/bin
shell: |
mv /tmp/setup_sit_vxlan.sh /usr/local/bin/setup_sit_vxlan.sh
chmod +x /usr/local/bin/setup_sit_vxlan.sh
become: true
- name: Render systemd unit for VXLAN
template:
src: vxlan-setup.service.j2
dest: /etc/systemd/system/vxlan-setup.service
mode: '0644'
- name: Reload systemd daemon
systemd:
daemon_reload: true
- name: Enable and start VXLAN overlay setup
systemd:
name: vxlan-setup
enabled: true
state: started

View File

@ -0,0 +1,12 @@
[Unit]
Description=VXLAN Overlay Setup for {{ inventory_hostname }}
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup_sit_vxlan.sh {{ vxlan_dev_if }} {{ vxlan_local_ip }} {{ vxlan_remote_ip }} {{ vxlan_br_ip }} {{ vxlan_cidr_suffix }} {{ vxlan_vni }}
RemainAfterExit=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,7 @@
---
# Default VXLAN settings for site nodes
vxlan_dev_if: wg0
vxlan_vni: 100
vxlan_cidr_suffix: 16
overlay_config_path: config/sit/vpn-overlay.yaml

View File

@ -0,0 +1,104 @@
#!/bin/bash
# 多 peer 自动化 VXLAN Overlay 脚本(读取 /etc/vxlan-config.yaml
# 用法: ./setup_sit_vxlan.sh [reset]
set -e
CONFIG_FILE="/etc/vxlan-config.yaml"
BR_IF="br0"
# 需要 yq 解析 yaml
command -v yq >/dev/null 2>&1 || { echo >&2 "❌ 请安装 yq 命令https://github.com/mikefarah/yq"; exit 1; }
if [[ "$1" == "reset" ]]; then
echo "🔄 正在清理 VXLAN Overlay 配置..."
# 删除所有 vxlan 接口
ip -o link show | awk -F': ' '/vxlan[0-9]+/ {print $2}' | while read -r iface; do
ip link set "$iface" down
ip link del "$iface"
echo "🧹 已删除接口 $iface"
done
ip link show "$BR_IF" &>/dev/null && {
ip link set "$BR_IF" down
ip link del "$BR_IF"
echo "🧹 已删除桥接器 $BR_IF"
}
echo "✅ 清理完成"
exit 0
fi
# 解析 config
DEV_IF="$(yq e '.dev_if' "$CONFIG_FILE")"
BRIDGE_IP="$(yq e '.bridge_ip' "$CONFIG_FILE")"
CIDR_SUFFIX="$(yq e '.cidr_suffix' "$CONFIG_FILE")"
PEER_COUNT=$(yq e '.peers | length' "$CONFIG_FILE")
if [[ -z "$DEV_IF" || -z "$BRIDGE_IP" || "$PEER_COUNT" -eq 0 ]]; then
echo "❌ 配置错误:请检查 $CONFIG_FILE"
exit 1
fi
BRIDGE_CIDR="${BRIDGE_IP}/${CIDR_SUFFIX}"
# 检查 dev_if 是否可用于 VXLAN
function is_vxlan_dev_usable() {
[[ -d "/sys/class/net/$1" ]] && grep -q "broadcast" "/sys/class/net/$1/flags"
}
echo "🔍 检查 $DEV_IF 是否可用于 VXLAN..."
USE_DEV_PARAM=true
if ! is_vxlan_dev_usable "$DEV_IF"; then
echo "⚠️ $DEV_IF 不支持广播,将省略 dev 参数(通过路由走隧道)"
USE_DEV_PARAM=false
fi
# 创建 bridge
if ! ip link show "$BR_IF" &>/dev/null; then
echo "🛠️ 创建桥接器 $BR_IF"
ip link add "$BR_IF" type bridge
ip link set "$BR_IF" up
ip addr add "$BRIDGE_CIDR" dev "$BR_IF"
fi
# 启用转发
sysctl -w net.ipv4.ip_forward=1
# 遍历 peers
for i in $(seq 0 $((PEER_COUNT - 1))); do
LOCAL_IP=$(yq e ".peers[$i].local_ip" "$CONFIG_FILE")
REMOTE_IP=$(yq e ".peers[$i].remote_ip" "$CONFIG_FILE")
VNI=$(yq e ".peers[$i].vxlan_id" "$CONFIG_FILE")
MTU=$(yq e ".peers[$i].mtu" "$CONFIG_FILE")
EXPOSE_PORT=$(yq e ".peers[$i].expose_port" "$CONFIG_FILE")
VXLAN_IF="vxlan${VNI}"
echo "🛠️ 创建 VXLAN 接口 $VXLAN_IF (local: $LOCAL_IP, remote: $REMOTE_IP, vni: $VNI)"
# 清理旧接口
ip link show "$VXLAN_IF" &>/dev/null && ip link set "$VXLAN_IF" down && ip link del "$VXLAN_IF"
# 创建 vxlan 接口
if $USE_DEV_PARAM; then
ip link add "$VXLAN_IF" type vxlan id "$VNI" dstport 4789 local "$LOCAL_IP" remote "$REMOTE_IP" dev "$DEV_IF"
else
ip link add "$VXLAN_IF" type vxlan id "$VNI" dstport 4789 local "$LOCAL_IP" remote "$REMOTE_IP"
fi
ip link set "$VXLAN_IF" mtu "$MTU"
ip link set "$VXLAN_IF" up
ip link set "$VXLAN_IF" master "$BR_IF"
# 可选添加 DNAT
if [[ -n "$EXPOSE_PORT" && "$EXPOSE_PORT" != "null" ]]; then
echo "🌐 添加 DNAT 规则:公网:$EXPOSE_PORT${BRIDGE_IP}:443"
iptables -t nat -C PREROUTING -p tcp --dport "$EXPOSE_PORT" -j DNAT --to-destination "${BRIDGE_IP}:443" 2>/dev/null || \
iptables -t nat -A PREROUTING -p tcp --dport "$EXPOSE_PORT" -j DNAT --to-destination "${BRIDGE_IP}:443"
fi
done
echo ""
echo "✅ 所有 VXLAN Overlay 配置完成"

View File

@ -0,0 +1,54 @@
---
- name: Load site overlay config
set_fact:
overlay_data: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: Select current site from config
set_fact:
current_site: "{{ overlay_data.sites | selectattr('name', 'equalto', inventory_hostname) | list | first }}"
- name: Fail if no matching site found
fail:
msg: "当前主机 {{ inventory_hostname }} 未在 config 中定义"
when: current_site is not defined
- name: Select first hub as default
set_fact:
selected_hub: "{{ overlay_data.hubs[0] }}"
- name: Set VXLAN parameters
set_fact:
vxlan_dev_if: "{{ current_site.interface}}"
vxlan_local_ip: "{{ current_site.local_ip }}"
vxlan_remote_ip: "{{ current_site.remote_ip }}"
vxlan_br_ip: "{{ current_site.br_ip }}"
vxlan_cidr_suffix: "{{ overlay_data.vxlan_cidr_suffix | default(16) }}"
vxlan_vni: "{{ overlay_data.vxlan_id | default(100) }}"
- name: 使用 rsync 分发 VXLAN 脚本
synchronize:
src: "files/setup_sit_vxlan.sh"
dest: /tmp/setup_sit_vxlan.sh
mode: push
- name: 移动脚本并赋权到 /usr/local/bin
shell: |
mv /tmp/setup_sit_vxlan.sh /usr/local/bin/setup_sit_vxlan.sh
chmod +x /usr/local/bin/setup_sit_vxlan.sh
become: true
- name: Render systemd unit for VXLAN
template:
src: vxlan-setup.service.j2
dest: /etc/systemd/system/vxlan-setup.service
mode: '0644'
- name: Reload systemd daemon
systemd:
daemon_reload: true
- name: Enable and start VXLAN overlay setup
systemd:
name: vxlan-setup
enabled: true
state: started

View File

@ -0,0 +1,12 @@
dev_if: eth0
bridge_ip: 10.253.253.1
cidr_suffix: 16
peers:
- local_ip: 172.30.0.1
remote_ip: 172.30.0.10
vxlan_id: 100
mtu: 1400
- local_ip: 172.30.0.1
remote_ip: 172.31.0.1
vxlan_id: 100
mtu: 1400

View File

@ -0,0 +1,12 @@
[Unit]
Description=VXLAN Overlay Setup for {{ inventory_hostname }}
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/setup_sit_vxlan.sh add {{ vxlan_dev_if }} {{ vxlan_local_ip }} {{ vxlan_remote_ip }} {{ vxlan_br_ip }} {{ vxlan_cidr_suffix }} {{ vxlan_vni }}
RemainAfterExit=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,4 @@
wg_interface: "wg0"
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
overlay_keys_path: "{{ playbook_dir }}/../../config/sit/vpn-keys.yaml"
wg_port: "51820"

View File

@ -0,0 +1,56 @@
---
- name: 加载 overlay 配置(标准 YAML
set_fact:
overlay_data: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: 加载密钥配置(支持 !vault
include_vars:
file: "{{ overlay_keys_path }}"
name: overlay_keys
- name: 提取当前 Hub 节点信息
set_fact:
current_node: >-
{{ overlay_data.hubs | selectattr('name', 'equalto', inventory_hostname) | list | first }}
current_key: >-
{{ overlay_keys['keys'] | selectattr('name', 'equalto', inventory_hostname) | list | first }}
- name: 提取所有对端 Peer 信息
set_fact:
peer_nodes: >-
{{ (overlay_data.sites + overlay_data.hubs)
| selectattr('name', 'in', current_node.wireguard_peer) | list }}
peer_keys: >-
{{ overlay_keys['keys']
| selectattr('name', 'in', current_node.wireguard_peer) | list }}
- name: 校验 Peer 节点数量
fail:
msg: "没有找到任何对端节点或对应密钥,请检查 wireguard_peer 设置"
when: peer_nodes | length == 0 or peer_keys | length == 0
- name: Ensure wireguard-tools is installed (Debian/Ubuntu)
apt:
name: wireguard-tools
state: present
when: ansible_os_family == 'Debian'
- name: Ensure wireguard-tools is installed (RHEL/CentOS)
yum:
name: wireguard-tools
state: present
when: ansible_os_family == 'RedHat'
- name: 渲染 wg0.conf
template:
src: wg0.conf.j2
dest: /etc/wireguard/wg0.conf
mode: '0600'
become: true
- name: 启用并启动 wg-quick@wg0
systemd:
name: wg-quick@wg0
enabled: true
state: started
become: true

View File

@ -0,0 +1,16 @@
[Interface]
PrivateKey = {{ current_key.private_key }}
Address = {{ current_node.wg_ip }}/32
ListenPort = {{ wg_port }}
DNS = 8.8.8.8
MTU = 1400
{% for peer, key in peer_nodes | zip(peer_keys) %}
{% if peer.name != inventory_hostname %}
[Peer]
PublicKey = {{ key.public_key }}
AllowedIPs = {{ peer.wg_ip }}/32
Endpoint = {{ peer.public_ip }}:{{ overlay_data.hub_port | default(wg_port) }}
PersistentKeepalive = 25
{% endif %}
{% endfor %}

View File

@ -0,0 +1,8 @@
wg_interface: "wg0"
# 默认配置文件路径,可在 playbook 中覆盖
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
overlay_keys_path: "{{ playbook_dir }}/../../config/sit/vpn-keys.yaml"
# WireGuard 默认端口(若 overlay_data.hub_port 未定义)
wg_port: "51820"

View File

@ -0,0 +1,71 @@
---
- name: 加载 overlay 配置(标准 YAML
set_fact:
overlay_data: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: 加载密钥配置(支持 !vault
include_vars:
file: "{{ overlay_keys_path }}"
name: overlay_keys
- name: 提取当前节点信息(作为 current
set_fact:
current_node: >-
{{ (overlay_data.sites + overlay_data.hubs)
| selectattr('name', 'equalto', inventory_hostname)
| list | first }}
current_key: >-
{{ overlay_keys['keys']
| selectattr('name', 'equalto', inventory_hostname)
| list | first }}
features: "{{ overlay_data.features }}"
- name: 标准化 wireguard_peer 为列表
set_fact:
wireguard_peers: >-
{{ [current_node.wireguard_peer] if current_node.wireguard_peer is string else current_node.wireguard_peer }}
- name: 提取对端 peer 节点列表
set_fact:
peer_node_list: >-
{{ (overlay_data.sites + overlay_data.hubs)
| selectattr('name', 'in', wireguard_peers)
| list }}
peer_key_list: >-
{{ overlay_keys['keys']
| selectattr('name', 'in', wireguard_peers)
| list }}
- name: 校验 wireguard_peer 是否匹配成功
fail:
msg: "未找到对端节点 '{{ current_node.wireguard_peer }}' 或其密钥"
when: peer_node_list | length == 0 or peer_key_list | length == 0
- name: 设置对端节点与密钥
set_fact:
peer_nodes: "{{ peer_node_list }}"
peer_keys: "{{ peer_key_list }}"
- name: 提取最终配置变量(私钥、公钥、端口等)
set_fact:
wg_port: "{{ wg_port }}"
current_wg_ip: "{{ current_node.wg_ip }}"
current_interface: "{{ current_node.interface }}"
current_private_key: "{{ current_key.private_key }}"
current_allowed_ips: "{{ current_node.allowed_ips }}"
peer_public_key: "{{ peer_keys[0].public_key }}"
peer_endpoint: "{{ peer_nodes[0].public_ip }}:{{ overlay_data.hub_port | default(wg_port) }}"
- name: 渲染 wg0.conf
template:
src: wg0.conf.j2
dest: /etc/wireguard/wg0.conf
mode: '0600'
become: true
- name: 启用并启动 wg-quick@wg0
systemd:
name: wg-quick@wg0
enabled: true
state: started
become: true

View File

@ -0,0 +1,24 @@
[Interface]
PrivateKey = {{ current_private_key }}
Address = {{ current_wg_ip }}/32
ListenPort = {{ wg_port }}
DNS = 8.8.8.8
MTU = 1400
PostUp = iptables -I FORWARD -o %i -j ACCEPT
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o {{ current_interface }} -j MASQUERADE
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o {{ current_interface }} -j MASQUERADE
[Peer]
PublicKey = {{ peer_public_key }}
AllowedIPs = {{ current_allowed_ips }}
{% if features.enable_vless %}
Endpoint = 127.0.0.1:51830
{% else %}
Endpoint = {{ peer_endpoint }}
{% endif %}
PersistentKeepalive = 25

View File

@ -0,0 +1,5 @@
xray_main_port: 1443
xray_cert_path: "/etc/ssl/onwalk.net.pem"
xray_key_path: "/etc/ssl/onwalk.net.key"
xray_bin_path: /usr/local/bin/xray
xray_config_dir: /usr/local/etc/xray

View File

@ -0,0 +1,6 @@
---
- name: Restart xray service
systemd:
name: xray.service
state: restarted
enabled: yes

View File

@ -0,0 +1,70 @@
---
- name: Load overlay config from file
set_fact:
overlay_config: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: Convert overlay_config.hubs list to dict (hubs_map)
set_fact:
hubs_map: "{{ dict(overlay_config.hubs | map(attribute='name') | zip(overlay_config.hubs)) }}"
when: overlay_config.hubs is defined
- name: Convert overlay_config.sites list to dict (sites_map)
set_fact:
sites_map: "{{ dict(overlay_config.sites | map(attribute='name') | zip(overlay_config.sites)) }}"
when: overlay_config.sites is defined
- name: 显示主机名
debug:
var: overlay_config
when: debug | default(false)
- set_fact:
xray_uuid: "{{ hubs_map[inventory_hostname].xray.uuid }}"
xray_remote_domain: "{{ hubs_map[inventory_hostname].xray.remote_domain }}"
xray_cert_path: "{{ hubs_map[inventory_hostname].xray.cert_path }}"
xray_key_path: "{{ hubs_map[inventory_hostname].xray.key_path }}"
- name: Install Xray using official script
shell: |
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)"
args:
creates: /usr/local/bin/xray
notify:
- Restart xray service
- name: Ensure required directories exist
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "{{ xray_bin_path | dirname }}"
- "{{ xray_config_dir }}"
- name: Deploy Xray config templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0644'
loop:
- { src: "config.json.j2", dest: "{{ xray_config_dir }}/config.json" }
- name: Deploy systemd service templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0644'
loop:
- { src: "xray.service.j2", dest: "/etc/systemd/system/xray.service" }
- name: Reload systemd
systemd:
daemon_reload: yes
- name: Enable and start xray services
systemd:
name: "{{ item }}"
enabled: yes
state: started
loop:
- xray.service

View File

@ -0,0 +1,84 @@
{
"log": {
"loglevel": "warning"
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": [
"geoip:cn"
],
"outboundTag": "block"
}
]
},
"inbounds": [
{
"listen": "0.0.0.0",
"port": {{ xray_main_port }},
"protocol": "vless",
"settings": {
"clients": [
{
"id": "{{ xray_uuid }}",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none",
"fallbacks": [
{
"dest": "8001",
"xver": 1
},
{
"alpn": "h2",
"dest": "8002",
"xver": 1
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"rejectUnknownSni": true,
"minVersion": "1.2",
"certificates": [
{
"ocspStapling": 3600,
"certificateFile": "{{ xray_cert_path }}",
"keyFile": "{{ xray_key_path }}"
}
]
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"policy": {
"levels": {
"0": {
"handshake": 2,
"connIdle": 120
}
}
}
}

View File

@ -0,0 +1,18 @@
[Unit]
Description=Xray Service
Documentation=https://github.com/xtls
After=network.target nss-lookup.target
[Service]
User=nobody
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStart=/usr/local/bin/xray run -config /usr/local/etc/xray/config.json
Restart=on-failure
RestartPreventExitStatus=23
LimitNPROC=10000
LimitNOFILE=1000000
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,6 @@
xray_main_port: 1443
xray_tproxy_port: 51830
xray_cert_path: "/etc/ssl/onwalk.net.pem"
xray_key_path: "/etc/ssl/onwalk.net.key"
xray_bin_path: /usr/local/bin/xray
xray_config_dir: /usr/local/etc/xray

View File

@ -0,0 +1,6 @@
---
- name: Restart Xray-client service
systemd:
name: xray-client.service
state: restarted
enabled: yes

View File

@ -0,0 +1,70 @@
---
- name: Load overlay config from file
set_fact:
overlay_config: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: Convert overlay_config.hubs list to dict (hubs_map)
set_fact:
hubs_map: "{{ dict(overlay_config.hubs | map(attribute='name') | zip(overlay_config.hubs)) }}"
when: overlay_config.hubs is defined
- name: Convert overlay_config.sites list to dict (sites_map)
set_fact:
sites_map: "{{ dict(overlay_config.sites | map(attribute='name') | zip(overlay_config.sites)) }}"
when: overlay_config.sites is defined
- name: 显示主机名
debug:
var: overlay_config
when: debug | default(false)
- set_fact:
xray_uuid: "{{ sites_map[inventory_hostname].xray.uuid }}"
xray_remote_domain: "{{ sites_map[inventory_hostname].xray.remote_domain }}"
xray_cert_path: "{{ sites_map[inventory_hostname].xray.cert_path }}"
xray_key_path: "{{ sites_map[inventory_hostname].xray.key_path }}"
- name: Install Xray using official script
shell: |
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)"
args:
creates: /usr/local/bin/xray
notify:
- Restart xray service
- name: Ensure required directories exist
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "{{ xray_bin_path | dirname }}"
- "{{ xray_config_dir }}"
- name: Deploy Xray config templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0644'
loop:
- { src: "client-config.json.j2", dest: "{{ xray_config_dir }}/client-config.json" }
- name: Deploy systemd service templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0644'
loop:
- { src: "xray-client.service.j2", dest: "/etc/systemd/system/xray-client.service" }
- name: Reload systemd
systemd:
daemon_reload: yes
- name: Enable and start xray services
systemd:
name: "{{ item }}"
enabled: yes
state: started
loop:
- xray-client.service

View File

@ -0,0 +1,94 @@
{
"log": {
"loglevel": "debug"
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": [
"geosite:cn",
"geosite:private"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
"geoip:cn",
"geoip:private"
],
"outboundTag": "direct"
}
]
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": 1080,
"protocol": "socks",
"settings": {
"udp": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
},
{
"listen": "127.0.0.1",
"port": 1081,
"protocol": "http",
"settings": {},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "{{ xray_remote_domain }}",
"port": {{ xray_main_port }},
"users": [
{
"id": "{{ xray_uuid }}",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "{{ xray_remote_domain }}",
"allowInsecure": false,
"fingerprint": "chrome"
}
},
"tag": "proxy"
},
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
]
}

View File

@ -0,0 +1,18 @@
[Unit]
Description=Xray Client Service
Documentation=https://github.com/xtls
After=network.target nss-lookup.target
[Service]
User=nobody
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStart=/usr/local/bin/xray run -config /usr/local/etc/xray/client-config.json
Restart=on-failure
RestartPreventExitStatus=23
LimitNPROC=10000
LimitNOFILE=1000000
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,6 @@
xray_main_port: 1443
xray_tproxy_port: 51830
xray_cert_path: "/etc/ssl/onwalk.net.pem"
xray_key_path: "/etc/ssl/onwalk.net.key"
xray_bin_path: /usr/local/bin/xray
xray_config_dir: /usr/local/etc/xray

View File

@ -0,0 +1,6 @@
---
- name: Restart xray-tproxy service
systemd:
name: xray-tproxy.service
state: restarted
enabled: yes

View File

@ -0,0 +1,82 @@
---
- name: Load overlay config from file
set_fact:
overlay_config: "{{ lookup('file', overlay_config_path) | from_yaml }}"
- name: Convert overlay_config.hubs list to dict (hubs_map)
set_fact:
hubs_map: "{{ dict(overlay_config.hubs | map(attribute='name') | zip(overlay_config.hubs)) }}"
when: overlay_config.hubs is defined
- name: Convert overlay_config.sites list to dict (sites_map)
set_fact:
sites_map: "{{ dict(overlay_config.sites | map(attribute='name') | zip(overlay_config.sites)) }}"
when: overlay_config.sites is defined
- name: Convert all nodes (hubs + sites) to one dict as node_map
set_fact:
node_map: >-
{{ dict((overlay_config.hubs + overlay_config.sites)
| map(attribute='name')
| zip(overlay_config.hubs + overlay_config.sites)) }}
- name: 显示主机名
debug:
var: node_map
when: debug | default(true)
- name: Show value for this node
debug:
msg: "{{ node_map[inventory_hostname] }}"
when: debug | default(true)
- set_fact:
xray_uuid: "{{ node_map[inventory_hostname].xray.uuid }}"
xray_remote_domain: "{{ node_map[inventory_hostname].xray.remote_domain }}"
xray_cert_path: "{{ node_map[inventory_hostname].xray.cert_path }}"
xray_key_path: "{{ node_map[inventory_hostname].xray.key_path }}"
- name: Install Xray using official script
shell: |
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)"
args:
creates: /usr/local/bin/xray
notify:
- Restart xray-tproxy service
- name: Ensure required directories exist
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "{{ xray_bin_path | dirname }}"
- "{{ xray_config_dir }}"
- name: Deploy Xray config templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0644'
loop:
- { src: "tproxy-config.json.j2", dest: "{{ xray_config_dir }}/tproxy-config.json" }
- name: Deploy systemd service templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '0644'
loop:
- { src: "xray-tproxy.service.j2", dest: "/etc/systemd/system/xray-tproxy.service" }
- name: Reload systemd
systemd:
daemon_reload: yes
- name: Enable and start xray services
systemd:
name: "{{ item }}"
enabled: yes
state: started
loop:
- xray-tproxy.service

View File

@ -0,0 +1,58 @@
{
"log": {
"loglevel": "info"
},
"routing": {
"rules": []
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": 51830,
"protocol": "dokodemo-door",
"settings": {
"address": "{{ xray_remote_domain }}",
"port": 51820,
"network": "udp"
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "{{ xray_remote_domain }}",
"port": 1443,
"users": [
{
"id": "{{ xray_uuid }}",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"serverName": "{{ xray_remote_domain }}",
"allowInsecure": false,
"fingerprint": "chrome"
}
},
"tag": "proxy"
},
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
]
}

View File

@ -0,0 +1,18 @@
[Unit]
Description=Xray Tproxy Service
Documentation=https://github.com/xtls
After=network.target nss-lookup.target
[Service]
User=nobody
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStart=/usr/local/bin/xray run -config /usr/local/etc/xray/tproxy-config.json
Restart=on-failure
RestartPreventExitStatus=23
LimitNPROC=10000
LimitNOFILE=1000000
[Install]
WantedBy=multi-user.target

8
vpn-overlay-dnat.yaml Normal file
View File

@ -0,0 +1,8 @@
- name: Setup DNAT rules
hosts: all
become: yes
gather_facts: no
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
roles:
- vhosts/vpn-overlay/setup-dnat

View File

@ -0,0 +1,8 @@
- name: Run infrastructure setup
hosts: all
become: yes
gather_facts: yes
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
roles:
- vhosts/vpn-overlay/vxlan/hub

View File

@ -0,0 +1,8 @@
- name: Run infrastructure setup
hosts: all
become: yes
gather_facts: yes
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
roles:
- vhosts/vpn-overlay/vxlan/site

8
vpn-wireguard-hub.yaml Normal file
View File

@ -0,0 +1,8 @@
- name: Setup WireGuard for hub
hosts: all
become: true
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
overlay_keys_path: "{{ playbook_dir }}/../../config/sit/vpn-keys.yaml"
roles:
- role: vhosts/vpn-overlay/wireguard/hub

8
vpn-wireguard-site.yaml Normal file
View File

@ -0,0 +1,8 @@
- name: Setup WireGuard for site
hosts: all
become: true
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
overlay_keys_path: "{{ playbook_dir }}/../../config/sit/vpn-keys.yaml"
roles:
- vhosts/vpn-overlay/wireguard/site

8
vpn-xray-client.yaml Normal file
View File

@ -0,0 +1,8 @@
- name: Setup Xray for hub
hosts: all
become: true
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
overlay_keys_path: "{{ playbook_dir }}/../../config/sit/vpn-keys.yaml"
roles:
- role: vhosts/vpn-overlay/xray/site

8
vpn-xray-hub.yaml Normal file
View File

@ -0,0 +1,8 @@
- name: Setup Xray for hub
hosts: all
become: true
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
overlay_keys_path: "{{ playbook_dir }}/../../config/sit/vpn-keys.yaml"
roles:
- role: vhosts/vpn-overlay/xray/hub

8
vpn-xray-tproxy.yaml Normal file
View File

@ -0,0 +1,8 @@
- name: Setup Xray for hub
hosts: all
become: true
vars:
overlay_config_path: "{{ playbook_dir }}/../../config/sit/vpn-overlay.yaml"
overlay_keys_path: "{{ playbook_dir }}/../../config/sit/vpn-keys.yaml"
roles:
- role: vhosts/vpn-overlay/xray/tproxy/