Compare commits
150
Commits
dc3ff78d6d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bafa7d09e | ||
|
|
841d56380b | ||
|
|
827f8de6d3 | ||
|
|
1a0b38d066 | ||
|
|
046d3bc236 | ||
|
|
b2d6c9a436 | ||
|
|
f8ccc7ec35 | ||
|
|
c935564ed8 | ||
|
|
59a7749b18 | ||
|
|
74c3c54feb | ||
|
|
e3d7c8ccbf | ||
|
|
f153bcdbd1 | ||
|
|
6529b49e1f | ||
|
|
638f043a49 | ||
|
|
928878e057 | ||
|
|
92ce67732c | ||
|
|
41404085f5 | ||
|
|
0e96d60ece | ||
|
|
2e829d9380 | ||
|
|
2cd106b84c | ||
|
|
46824776ef | ||
|
|
d5b56f1a20 | ||
|
|
65ef3341a4 | ||
|
|
db7c95845b | ||
|
|
5cf7f11b88 | ||
|
|
857e63ca68 | ||
|
|
9c7ef29a43 | ||
|
|
81174047bd | ||
|
|
17f91d833e | ||
|
|
32428d0156 | ||
|
|
e49cf515e9 | ||
|
|
eb9c8767b0 | ||
|
|
f61ad5763e | ||
|
|
3cca8246db | ||
|
|
573590ef45 | ||
|
|
4a80762cbc | ||
|
|
f98b83514f | ||
|
|
055bb45be4 | ||
|
|
40a499decf | ||
|
|
14bf5c9e63 | ||
|
|
ab9d07c379 | ||
|
|
39bdb810a2 | ||
|
|
b3a17ab5e4 | ||
|
|
322bfa9513 | ||
|
|
c0cd22a73a | ||
|
|
744aa4f14a | ||
|
|
2a9ac8cbfd | ||
|
|
70f6d6c51f | ||
|
|
e9d39360b3 | ||
|
|
87557bee15 | ||
|
|
abd88f5744 | ||
|
|
aea97a1d2a | ||
|
|
d1ae1afe34 | ||
|
|
f369bf2eaf | ||
|
|
05a95ae3c0 | ||
|
|
e37db8dd7a | ||
|
|
1bd13b2c1b | ||
|
|
35fce0220c | ||
|
|
d5b589abf8 | ||
|
|
1548c1f524 | ||
|
|
05c38bb63f | ||
|
|
7b1c637a76 | ||
|
|
fe2fe590e4 | ||
|
|
860f09e8f8 | ||
|
|
0a38cf4237 | ||
|
|
ce7654908c | ||
|
|
185f7a52a1 | ||
|
|
8dbca5368d | ||
|
|
df9f965f38 | ||
|
|
cb401219e8 | ||
|
|
16aa567053 | ||
|
|
6c95ad59a7 | ||
|
|
c21e2b175c | ||
|
|
649dd5a076 | ||
|
|
5168b22eba | ||
|
|
5ef1d4830e | ||
|
|
9b47d7a216 | ||
|
|
340802ec1a | ||
|
|
4cfc9597b4 | ||
|
|
bf6aa03fcb | ||
|
|
ae9ca5d07f | ||
|
|
2b1d98292b | ||
|
|
fb5003ef0c | ||
|
|
6742f89420 | ||
|
|
54b4a23446 | ||
|
|
2e0b494000 | ||
|
|
4057d5d7f1 | ||
|
|
037372d500 | ||
|
|
6852d740b5 | ||
|
|
058044074a | ||
|
|
8ed947e48b | ||
|
|
7d66b0e296 | ||
|
|
18b0242fdf | ||
|
|
ec79b653dc | ||
|
|
b18ecaeef6 | ||
|
|
c65c5cc54d | ||
|
|
60a5778194 | ||
|
|
ef1503aa3a | ||
|
|
e9ca74b236 | ||
|
|
de9ed1e9ee | ||
|
|
9148838e12 | ||
|
|
27bdb48466 | ||
|
|
04256f9f37 | ||
|
|
9c9ad292b2 | ||
|
|
8f3dcc415a | ||
|
|
780707123d | ||
|
|
9b66b72729 | ||
|
|
8dada68767 | ||
|
|
e715d577b1 | ||
|
|
86fb248d85 | ||
|
|
0f6c4525b2 | ||
|
|
7602db6ae7 | ||
|
|
cf079ba65b | ||
|
|
9346736842 | ||
|
|
09880697ce | ||
|
|
673e2c838d | ||
|
|
e6bfa13eb2 | ||
|
|
592b3a8748 | ||
|
|
23b0ef3f41 | ||
|
|
12abbde629 | ||
|
|
c09754a161 | ||
|
|
b1ec5566d6 | ||
|
|
9b3fba0052 | ||
|
|
abeaf1ee45 | ||
|
|
d4cf64c21f | ||
|
|
e366b3dd3f | ||
|
|
dc76e48e21 | ||
|
|
47acec436b | ||
|
|
62a263c9f6 | ||
|
|
6aa829f649 | ||
|
|
1e2f3dca1e | ||
|
|
6123168567 | ||
|
|
4b4a6653d5 | ||
|
|
9490423c0f | ||
|
|
234cfe7758 | ||
|
|
4eb8313209 | ||
|
|
12d77337a8 | ||
|
|
065114bad2 | ||
|
|
b7cd9d2a60 | ||
|
|
29097f94b2 | ||
|
|
edea54820a | ||
|
|
81110fa55b | ||
|
|
76ebb3cd13 | ||
|
|
9e9a3e3c6b | ||
|
|
d4f8ccc561 | ||
|
|
a25f353f50 | ||
|
|
0935602fcd | ||
|
|
a07449c229 | ||
|
|
e3b484812e | ||
|
|
9b361aa169 |
@@ -1,31 +0,0 @@
|
||||
name: Deploy cloud-pc
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'cloud-pc/**'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Deploy changed stacks
|
||||
run: |
|
||||
ssh -o StrictHostKeyChecking=no ${{ secrets.CLOUD-PC_USER }}@${{ secrets.CLOUD-PC_HOST }} << 'ENDSSH'
|
||||
cd /opt/services
|
||||
git pull
|
||||
for dir in $(git diff --name-only HEAD~1 HEAD | grep '^cloud-pc/' | cut -d/ -f2 | sort -u); do
|
||||
echo "==> Deploying $dir"
|
||||
cd /opt/services/cloud-pc/$dir
|
||||
if grep -qE '^[[:space:]]*build:' docker-compose.yml; then
|
||||
docker compose up -d --build --remove-orphans
|
||||
else
|
||||
docker compose pull
|
||||
docker compose up -d --remove-orphans
|
||||
fi
|
||||
cd /opt/services
|
||||
done
|
||||
ENDSSH
|
||||
@@ -1,31 +0,0 @@
|
||||
name: Deploy mini-pc
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'mini-pc/**'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Deploy changed stacks
|
||||
run: |
|
||||
ssh -o StrictHostKeyChecking=no ${{ secrets.MINI-PC_USER }}@${{ secrets.MINI-PC_HOST }} << 'ENDSSH'
|
||||
cd /opt/services
|
||||
git pull
|
||||
for dir in $(git diff --name-only HEAD~1 HEAD | grep '^mini-pc/' | cut -d/ -f2 | sort -u); do
|
||||
echo "==> Deploying $dir"
|
||||
cd /opt/services/mini-pc/$dir
|
||||
if grep -qE '^[[:space:]]*build:' docker-compose.yml; then
|
||||
docker compose up -d --build --remove-orphans
|
||||
else
|
||||
docker compose pull
|
||||
docker compose up -d --remove-orphans
|
||||
fi
|
||||
cd /opt/services
|
||||
done
|
||||
ENDSSH
|
||||
@@ -1,31 +0,0 @@
|
||||
name: Deploy ru-vps
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'ru-vps/**'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Deploy changed stacks
|
||||
run: |
|
||||
ssh -o StrictHostKeyChecking=no ${{ secrets.RU-VPS_USER }}@${{ secrets.RU-VPS_HOST }} << 'ENDSSH'
|
||||
cd /opt/services
|
||||
git pull
|
||||
for dir in $(git diff --name-only HEAD~1 HEAD | grep '^ru-vps/' | cut -d/ -f2 | sort -u); do
|
||||
echo "==> Deploying $dir"
|
||||
cd /opt/services/ru-vps/$dir
|
||||
if grep -qE '^[[:space:]]*build:' docker-compose.yml; then
|
||||
docker compose up -d --build --remove-orphans
|
||||
else
|
||||
docker compose pull
|
||||
docker compose up -d --remove-orphans
|
||||
fi
|
||||
cd /opt/services
|
||||
done
|
||||
ENDSSH
|
||||
@@ -1,3 +1,8 @@
|
||||
.env
|
||||
*.env
|
||||
!*.env.example
|
||||
passwd
|
||||
.venv/
|
||||
ansible/.venv/
|
||||
ansible/collections/
|
||||
ansible/generated/
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# HomeLab Infrastructure
|
||||
|
||||
Active HomeLab infrastructure is managed through Ansible.
|
||||
|
||||
## Active Files
|
||||
|
||||
- `ansible/` — current control plane.
|
||||
- `ansible/inventory/hosts.yml` — inventory and host facts.
|
||||
- `ansible/playbooks/check.yml` — safe connectivity/facts check.
|
||||
|
||||
## Archive
|
||||
|
||||
Historical pre-Proxmox material is kept under:
|
||||
|
||||
```text
|
||||
archive/2026-07-proxmox-migration/
|
||||
```
|
||||
|
||||
It contains old NixOS configs, Docker Compose service definitions, Gitea workflows, deploy scripts and old Ansible bootstrap playbooks.
|
||||
|
||||
## Basic Check
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
ansible-playbook playbooks/check.yml
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
export PROXMOX_HOST=192.168.1.10
|
||||
export PROXMOX_USER='ansible@pve'
|
||||
export PROXMOX_TOKEN_ID='homelab'
|
||||
export PROXMOX_TOKEN_SECRET='replace-me'
|
||||
export PROXMOX_VALIDATE_CERTS=false
|
||||
|
||||
# Override if the downloaded template name differs.
|
||||
export PVE_LXC_OSTEMPLATE='local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst'
|
||||
@@ -0,0 +1,82 @@
|
||||
# HomeLab Ansible
|
||||
|
||||
Ansible is the control plane for HomeLab infrastructure changes.
|
||||
|
||||
## Contract
|
||||
|
||||
- The operator keeps passwords, SSH access and network reachability working.
|
||||
- The agent changes infrastructure only through inventory, roles and playbooks in this directory.
|
||||
- Manual server changes are allowed only for break-glass recovery; afterwards they must be captured in Ansible.
|
||||
- Secrets stay outside git. Use `.env`, vault files or local prompt input, not committed variables.
|
||||
|
||||
## Layout
|
||||
|
||||
- `inventory/hosts.yml` — canonical host list and host-specific facts.
|
||||
- `playbooks/` — entry points for tasks.
|
||||
- `roles/` — reusable configuration units.
|
||||
|
||||
## Current Groups
|
||||
|
||||
- `ru-vps` — public VPS, JumpHost, qdevice, ZeroTier member.
|
||||
- `pve_nodes` — Proxmox hosts: `cloud-pc`, `mini-pc`.
|
||||
- `lxc_infra` — infrastructure LXC containers: `pbs`, `zt-cloud`, `zt-mini`.
|
||||
- `vpn_openvpn` — OpenVPN transport hosts: `ru-vps`, `wg-mini`.
|
||||
- `shell_hosts` — hosts with unified bash config: `ru-vps`, `cloud-pc`, `mini-pc`.
|
||||
- `servers` — all managed hosts.
|
||||
|
||||
## First Checks
|
||||
|
||||
Install control-node dependencies locally:
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
. .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
ansible-galaxy collection install -r requirements.yml -p collections
|
||||
```
|
||||
|
||||
Run from `ansible/`:
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/check.yml
|
||||
```
|
||||
|
||||
For Proxmox API playbooks, create ignored `.env` from `.env.example` and load it:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
. ./.env
|
||||
.venv/bin/ansible-playbook playbooks/pve-wg-mini.yml
|
||||
```
|
||||
|
||||
Or bootstrap the token from `mini-pc` with sudo:
|
||||
|
||||
```bash
|
||||
.venv/bin/ansible-playbook playbooks/bootstrap-pve-api-token.yml -K
|
||||
```
|
||||
|
||||
OpenVPN transport:
|
||||
|
||||
```bash
|
||||
.venv/bin/ansible-playbook playbooks/openvpn-vps-mini.yml -K
|
||||
.venv/bin/ansible-playbook playbooks/openvpn-check.yml
|
||||
```
|
||||
|
||||
Bootstrap the Ansible service account on shell hosts:
|
||||
|
||||
```bash
|
||||
.venv/bin/ansible-playbook -i inventory/hosts.yml playbooks/bootstrap-ansible-user.yml -K
|
||||
```
|
||||
|
||||
When a task needs privilege escalation:
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/<name>.yml -K
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Describe the desired infrastructure change.
|
||||
2. Add or update a role/playbook.
|
||||
3. Run the smallest safe check or playbook.
|
||||
4. Document non-obvious decisions in the project notes.
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
[defaults]
|
||||
inventory = inventory/hosts.yml
|
||||
roles_path = roles
|
||||
collections_path = collections
|
||||
retry_files_enabled = false
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = true
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=accept-new
|
||||
|
||||
+82
-10
@@ -1,18 +1,30 @@
|
||||
all:
|
||||
vars:
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
ansible_user: ada
|
||||
ansible_user: ansible
|
||||
ansible_ssh_private_key_file: ~/.ssh/id_ed25519_homelab_ansible
|
||||
ansible_become: true
|
||||
homelab_lan_cidr: 192.168.1.0/24
|
||||
homelab_service_range: 192.168.1.5-192.168.1.40
|
||||
homelab_zerotier_network_id: 743993800fa5a34c
|
||||
openvpn_network_cidr: 10.78.0.0/30
|
||||
openvpn_listen_port: 8443
|
||||
|
||||
children:
|
||||
servers:
|
||||
homelab:
|
||||
hosts:
|
||||
ru-vps:
|
||||
ansible_host: vps
|
||||
openvpn_local_ip: 10.78.0.1
|
||||
openvpn_peer_ip: 10.78.0.2
|
||||
openvpn_role: server
|
||||
|
||||
pve_nodes:
|
||||
hosts:
|
||||
cloud-pc:
|
||||
ansible_host: 10.122.62.51
|
||||
# ansible_host: 192.168.1.5
|
||||
ansible_ssh_common_args: '-J mini-pc'
|
||||
ansible_host: 192.168.1.5
|
||||
ansible_ssh_common_args: >-
|
||||
-o ProxyCommand="ssh -i ~/.ssh/id_ed25519_homelab_ansible -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -p 3422 -W %h:%p ansible@157.22.231.198"
|
||||
expected_lan_ip: 192.168.1.5
|
||||
storage_mounts:
|
||||
- src: UUID=ae278333-4116-4225-9b95-595496fadd26
|
||||
@@ -21,11 +33,71 @@ all:
|
||||
- src: UUID=940a9a4e-10cf-4380-9d22-a71549066561
|
||||
path: /mnt/backup
|
||||
fstype: ext4
|
||||
- src: UUID=89024d30-1ab6-4b2e-b23b-62837455b24a
|
||||
path: /opt/samba
|
||||
fstype: ext4
|
||||
|
||||
mini-pc:
|
||||
ansible_host: 10.122.62.95
|
||||
ansible_ssh_common_args: '-J vps'
|
||||
ansible_host: 192.168.1.10
|
||||
ansible_ssh_common_args: >-
|
||||
-o ProxyCommand="ssh -i ~/.ssh/id_ed25519_homelab_ansible -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -p 3422 -W %h:%p ansible@157.22.231.198"
|
||||
expected_lan_ip: 192.168.1.10
|
||||
|
||||
lxc_infra:
|
||||
hosts:
|
||||
pbs:
|
||||
ansible_host: 192.168.1.20
|
||||
expected_lan_ip: 192.168.1.20
|
||||
|
||||
zt-cloud:
|
||||
ansible_host: 192.168.1.21
|
||||
expected_lan_ip: 192.168.1.21
|
||||
zerotier_ip: 10.122.62.206
|
||||
zerotier_node_id: c606e6d181
|
||||
|
||||
zt-mini:
|
||||
ansible_host: 192.168.1.22
|
||||
expected_lan_ip: 192.168.1.22
|
||||
zerotier_ip: 10.122.62.213
|
||||
zerotier_node_id: 022ac284e1
|
||||
|
||||
wg-mini:
|
||||
ansible_host: 192.168.1.23
|
||||
ansible_user: root
|
||||
ansible_ssh_private_key_file: ~/.ssh/id_ed25519_homelab
|
||||
expected_lan_ip: 192.168.1.23
|
||||
openvpn_local_ip: 10.78.0.2
|
||||
openvpn_peer_ip: 10.78.0.1
|
||||
openvpn_role: gateway
|
||||
|
||||
vaultwarden:
|
||||
ansible_host: 192.168.1.24
|
||||
ansible_user: root
|
||||
ansible_become: false
|
||||
ansible_ssh_private_key_file: ~/.ssh/id_ed25519_homelab
|
||||
ansible_ssh_common_args: >-
|
||||
-o ProxyCommand="ssh -i ~/.ssh/id_ed25519_homelab_ansible -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -p 3422 -W %h:%p ansible@157.22.231.198"
|
||||
expected_lan_ip: 192.168.1.24
|
||||
|
||||
gitea:
|
||||
ansible_host: 192.168.1.25
|
||||
ansible_user: root
|
||||
ansible_become: false
|
||||
ansible_ssh_private_key_file: ~/.ssh/id_ed25519_homelab
|
||||
ansible_ssh_common_args: >-
|
||||
-o ProxyCommand="ssh -i ~/.ssh/id_ed25519_homelab_ansible -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -p 3422 -W %h:%p ansible@157.22.231.198"
|
||||
expected_lan_ip: 192.168.1.25
|
||||
|
||||
vpn_openvpn:
|
||||
hosts:
|
||||
ru-vps:
|
||||
wg-mini:
|
||||
|
||||
shell_hosts:
|
||||
hosts:
|
||||
ru-vps:
|
||||
cloud-pc:
|
||||
mini-pc:
|
||||
|
||||
servers:
|
||||
children:
|
||||
homelab:
|
||||
pve_nodes:
|
||||
lxc_infra:
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
- name: Configure unified bash profile
|
||||
hosts: shell_hosts
|
||||
gather_facts: true
|
||||
become: true
|
||||
roles:
|
||||
- bash_config
|
||||
@@ -0,0 +1,38 @@
|
||||
- name: Bootstrap Ansible service account
|
||||
hosts: shell_hosts
|
||||
gather_facts: true
|
||||
become: true
|
||||
vars:
|
||||
ansible_service_user: ansible
|
||||
ansible_service_public_key_file: ~/.ssh/id_ed25519_homelab_ansible.pub
|
||||
ansible_service_sudoers_file: /etc/sudoers.d/99-ansible-service-user
|
||||
|
||||
tasks:
|
||||
- name: Read local Ansible service public key
|
||||
ansible.builtin.set_fact:
|
||||
ansible_service_public_key: "{{ lookup('file', ansible_service_public_key_file) }}"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
|
||||
- name: Ensure Ansible service user exists
|
||||
ansible.builtin.user:
|
||||
name: "{{ ansible_service_user }}"
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
- name: Install Ansible service SSH key
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ ansible_service_user }}"
|
||||
key: "{{ ansible_service_public_key }}"
|
||||
state: present
|
||||
exclusive: true
|
||||
|
||||
- name: Allow passwordless sudo for Ansible service user
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ ansible_service_sudoers_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
content: "{{ ansible_service_user }} ALL=(ALL) NOPASSWD: ALL\n"
|
||||
validate: /usr/sbin/visudo -cf %s
|
||||
@@ -0,0 +1,53 @@
|
||||
- name: Bootstrap Proxmox API token for Ansible
|
||||
hosts: mini-pc
|
||||
gather_facts: false
|
||||
become: true
|
||||
vars:
|
||||
pve_api_user: ansible@pve
|
||||
pve_api_token_id: homelab
|
||||
pve_api_role: Administrator
|
||||
tasks:
|
||||
- name: Ensure Proxmox API user exists
|
||||
ansible.builtin.command: "pveum user add {{ pve_api_user }} --comment 'Ansible automation user'"
|
||||
register: pve_user_add
|
||||
changed_when: pve_user_add.rc == 0
|
||||
failed_when:
|
||||
- pve_user_add.rc != 0
|
||||
- "'already exists' not in (pve_user_add.stderr | lower)"
|
||||
|
||||
- name: Grant Proxmox API user permissions
|
||||
ansible.builtin.command: "pveum aclmod / -user {{ pve_api_user }} -role {{ pve_api_role }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Remove old Ansible token if present
|
||||
ansible.builtin.command: "pveum user token remove {{ pve_api_user }} {{ pve_api_token_id }}"
|
||||
register: pve_token_remove
|
||||
changed_when: pve_token_remove.rc == 0
|
||||
failed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Create Proxmox API token
|
||||
ansible.builtin.command: "pveum user token add {{ pve_api_user }} {{ pve_api_token_id }} --privsep 0 --comment 'HomeLab Ansible token' --output-format json"
|
||||
register: pve_token_add
|
||||
changed_when: true
|
||||
no_log: true
|
||||
|
||||
- name: Store token data
|
||||
ansible.builtin.set_fact:
|
||||
pve_token_data: "{{ pve_token_add.stdout | from_json }}"
|
||||
no_log: true
|
||||
|
||||
- name: Write local ansible/.env
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ playbook_dir }}/../.env"
|
||||
mode: "0600"
|
||||
content: |
|
||||
export PROXMOX_HOST={{ ansible_host }}
|
||||
export PROXMOX_USER='{{ pve_api_user }}'
|
||||
export PROXMOX_TOKEN_ID='{{ pve_api_token_id }}'
|
||||
export PROXMOX_TOKEN_SECRET='{{ pve_token_data.value }}'
|
||||
export PROXMOX_VALIDATE_CERTS=false
|
||||
export PVE_LXC_OSTEMPLATE='local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst'
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
no_log: true
|
||||
@@ -0,0 +1,15 @@
|
||||
- name: Check HomeLab Ansible connectivity
|
||||
hosts: servers
|
||||
gather_facts: true
|
||||
become: false
|
||||
tasks:
|
||||
- name: Ping host
|
||||
ansible.builtin.ping:
|
||||
|
||||
- name: Check expected LAN IP when defined
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- expected_lan_ip in ansible_facts.all_ipv4_addresses
|
||||
fail_msg: "{{ inventory_hostname }} must have {{ expected_lan_ip }}; found: {{ ansible_facts.all_ipv4_addresses | join(', ') }}"
|
||||
success_msg: "{{ inventory_hostname }} has {{ expected_lan_ip }}"
|
||||
when: expected_lan_ip is defined
|
||||
@@ -0,0 +1,19 @@
|
||||
- name: Check OpenVPN tunnel from ru-vps
|
||||
hosts: ru-vps
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: Ping VPN LXC OpenVPN address
|
||||
ansible.builtin.command: ping -c 3 -W 2 10.78.0.2
|
||||
changed_when: false
|
||||
|
||||
- name: Check mini-pc PVE port through OpenVPN gateway
|
||||
ansible.builtin.command: nc -vz -w 5 192.168.1.10 8006
|
||||
changed_when: false
|
||||
|
||||
- name: Check cloud-pc PVE port through OpenVPN gateway
|
||||
ansible.builtin.command: nc -vz -w 5 192.168.1.5 8006
|
||||
changed_when: false
|
||||
|
||||
- name: Check PBS port through OpenVPN gateway
|
||||
ansible.builtin.command: nc -vz -w 5 192.168.1.20 8007
|
||||
changed_when: false
|
||||
@@ -0,0 +1,197 @@
|
||||
- name: Configure OpenVPN laptop access through ru-vps
|
||||
hosts: ru-vps
|
||||
gather_facts: true
|
||||
become: true
|
||||
vars:
|
||||
laptop_profile_name: ada-x1
|
||||
laptop_openvpn_dir: /etc/openvpn/homelab-laptop
|
||||
laptop_openvpn_config: /etc/openvpn/homelab-laptop/server.conf
|
||||
laptop_openvpn_key: /etc/openvpn/homelab-laptop/static.key
|
||||
laptop_openvpn_service: homelab-openvpn-laptop
|
||||
laptop_openvpn_port: 9443
|
||||
laptop_openvpn_interface: tun1
|
||||
laptop_openvpn_site_interface: tun0
|
||||
laptop_openvpn_network_cidr: 10.79.0.0/30
|
||||
laptop_openvpn_server_ip: 10.79.0.1
|
||||
laptop_openvpn_client_ip: 10.79.0.2
|
||||
laptop_openvpn_remote: 157.22.231.198
|
||||
laptop_openvpn_client_config_local: "{{ playbook_dir }}/../generated/{{ laptop_profile_name }}.ovpn"
|
||||
laptop_openvpn_home_routes:
|
||||
- network: 192.168.1.5
|
||||
netmask: 255.255.255.255
|
||||
- network: 192.168.1.6
|
||||
netmask: 255.255.255.254
|
||||
- network: 192.168.1.8
|
||||
netmask: 255.255.255.248
|
||||
- network: 192.168.1.16
|
||||
netmask: 255.255.255.240
|
||||
- network: 192.168.1.32
|
||||
netmask: 255.255.255.248
|
||||
- network: 192.168.1.40
|
||||
netmask: 255.255.255.255
|
||||
|
||||
tasks:
|
||||
- name: Install OpenVPN packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- openvpn
|
||||
- iptables
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Ensure laptop OpenVPN config directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ laptop_openvpn_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: Generate laptop static key if missing
|
||||
ansible.builtin.command: "openvpn --genkey secret {{ laptop_openvpn_key }}"
|
||||
args:
|
||||
creates: "{{ laptop_openvpn_key }}"
|
||||
no_log: true
|
||||
|
||||
- name: Enable IPv4 forwarding on ru-vps
|
||||
ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: "1"
|
||||
state: present
|
||||
reload: true
|
||||
|
||||
- name: Allow laptop OpenVPN TCP in UFW
|
||||
ansible.builtin.command: "ufw allow {{ laptop_openvpn_port }}/tcp"
|
||||
register: ufw_allow_laptop_openvpn
|
||||
changed_when: "'Rule added' in ufw_allow_laptop_openvpn.stdout or 'Rules updated' in ufw_allow_laptop_openvpn.stdout"
|
||||
failed_when: false
|
||||
|
||||
- name: Render laptop OpenVPN server config
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ laptop_openvpn_config }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
content: |
|
||||
dev {{ laptop_openvpn_interface }}
|
||||
ifconfig {{ laptop_openvpn_server_ip }} {{ laptop_openvpn_client_ip }}
|
||||
secret {{ laptop_openvpn_key }}
|
||||
cipher AES-256-CBC
|
||||
auth SHA256
|
||||
proto tcp-server
|
||||
port {{ laptop_openvpn_port }}
|
||||
persist-key
|
||||
persist-tun
|
||||
verb 3
|
||||
keepalive 10 60
|
||||
notify: restart laptop openvpn
|
||||
|
||||
- name: Render laptop OpenVPN up script
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ laptop_openvpn_dir }}/up.sh"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
content: |
|
||||
#!/bin/sh
|
||||
iptables -t nat -C POSTROUTING -s {{ laptop_openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o {{ laptop_openvpn_site_interface }} -j MASQUERADE 2>/dev/null || \
|
||||
iptables -t nat -A POSTROUTING -s {{ laptop_openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o {{ laptop_openvpn_site_interface }} -j MASQUERADE
|
||||
notify: restart laptop openvpn
|
||||
|
||||
- name: Render laptop OpenVPN down script
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ laptop_openvpn_dir }}/down.sh"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
content: |
|
||||
#!/bin/sh
|
||||
iptables -t nat -D POSTROUTING -s {{ laptop_openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o {{ laptop_openvpn_site_interface }} -j MASQUERADE 2>/dev/null || true
|
||||
notify: restart laptop openvpn
|
||||
|
||||
- name: Render laptop OpenVPN systemd service
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/systemd/system/{{ laptop_openvpn_service }}.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
[Unit]
|
||||
Description=HomeLab OpenVPN laptop access
|
||||
After=network-online.target homelab-openvpn.service
|
||||
Wants=network-online.target
|
||||
Requires=homelab-openvpn.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStartPre={{ laptop_openvpn_dir }}/up.sh
|
||||
ExecStart=/usr/sbin/openvpn --config {{ laptop_openvpn_config }}
|
||||
ExecStopPost={{ laptop_openvpn_dir }}/down.sh
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
notify:
|
||||
- reload systemd
|
||||
- restart laptop openvpn
|
||||
|
||||
- name: Flush handlers before starting laptop OpenVPN
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Enable and start laptop OpenVPN
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ laptop_openvpn_service }}"
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Read laptop static key for client profile
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ laptop_openvpn_key }}"
|
||||
register: laptop_openvpn_static_key_raw
|
||||
no_log: true
|
||||
|
||||
- name: Ensure local generated directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ playbook_dir }}/../generated"
|
||||
state: directory
|
||||
mode: "0700"
|
||||
become: false
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Write local laptop OpenVPN profile
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ laptop_openvpn_client_config_local }}"
|
||||
mode: "0600"
|
||||
content: |
|
||||
client
|
||||
dev tun
|
||||
proto tcp-client
|
||||
remote {{ laptop_openvpn_remote }} {{ laptop_openvpn_port }}
|
||||
ifconfig {{ laptop_openvpn_client_ip }} {{ laptop_openvpn_server_ip }}
|
||||
cipher AES-256-CBC
|
||||
auth SHA256
|
||||
persist-key
|
||||
persist-tun
|
||||
resolv-retry infinite
|
||||
nobind
|
||||
verb 3
|
||||
{% for route in laptop_openvpn_home_routes %}
|
||||
route {{ route.network }} {{ route.netmask }}
|
||||
{% endfor %}
|
||||
<secret>
|
||||
{{ laptop_openvpn_static_key_raw.content | b64decode }}
|
||||
</secret>
|
||||
become: false
|
||||
delegate_to: localhost
|
||||
no_log: true
|
||||
|
||||
handlers:
|
||||
- name: reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: restart laptop openvpn
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ laptop_openvpn_service }}"
|
||||
state: restarted
|
||||
@@ -0,0 +1,6 @@
|
||||
- name: Configure OpenVPN on ru-vps and VPN LXC
|
||||
hosts: vpn_openvpn
|
||||
gather_facts: true
|
||||
become: true
|
||||
roles:
|
||||
- openvpn_gateway
|
||||
@@ -0,0 +1,197 @@
|
||||
- name: Create Gitea LXC on cloud-pc
|
||||
hosts: cloud-pc
|
||||
gather_facts: false
|
||||
vars:
|
||||
gitea_vmid: 141
|
||||
gitea_hostname: gitea
|
||||
gitea_ip: 192.168.1.25/24
|
||||
gitea_gateway: 192.168.1.1
|
||||
gitea_bridge: vmbr0
|
||||
gitea_rootfs: data:32
|
||||
gitea_host_data_dir: /opt/data/gitea
|
||||
gitea_host_data_uid: 101000
|
||||
gitea_host_data_gid: 101000
|
||||
gitea_container_data_dir: /opt/gitea/data
|
||||
gitea_ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
|
||||
gitea_pubkey_file: ~/.ssh/id_ed25519_homelab.pub
|
||||
handlers:
|
||||
- name: restart gitea lxc
|
||||
ansible.builtin.shell: "pct stop {{ gitea_vmid }} || true; pct start {{ gitea_vmid }}"
|
||||
changed_when: true
|
||||
tasks:
|
||||
- name: Check if Gitea LXC exists
|
||||
ansible.builtin.command: "pct config {{ gitea_vmid }}"
|
||||
register: gitea_pct_config
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install Gitea LXC SSH public key on PVE host
|
||||
ansible.builtin.copy:
|
||||
dest: /tmp/gitea-lxc.pub
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
content: "{{ lookup('file', gitea_pubkey_file) }}\n"
|
||||
when: gitea_pct_config.rc != 0
|
||||
|
||||
- name: Create Gitea LXC
|
||||
ansible.builtin.command: >-
|
||||
pct create {{ gitea_vmid }} {{ gitea_ostemplate }}
|
||||
--hostname {{ gitea_hostname }}
|
||||
--rootfs {{ gitea_rootfs }}
|
||||
--cores 2
|
||||
--memory 2048
|
||||
--swap 1024
|
||||
--net0 name=eth0,bridge={{ gitea_bridge }},gw={{ gitea_gateway }},ip={{ gitea_ip }},firewall=1
|
||||
--nameserver 1.1.1.1
|
||||
--unprivileged 1
|
||||
--features nesting=1,keyctl=1
|
||||
--onboot 1
|
||||
--startup order=50
|
||||
--cmode shell
|
||||
--ssh-public-keys /tmp/gitea-lxc.pub
|
||||
when: gitea_pct_config.rc != 0
|
||||
|
||||
- name: Start Gitea LXC
|
||||
ansible.builtin.command: "pct start {{ gitea_vmid }}"
|
||||
register: gitea_pct_start
|
||||
changed_when: gitea_pct_start.rc == 0
|
||||
failed_when: gitea_pct_start.rc not in [0, 255]
|
||||
|
||||
- name: Allow FUSE device in Gitea LXC config
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/etc/pve/lxc/{{ gitea_vmid }}.conf"
|
||||
line: "lxc.cgroup2.devices.allow: c 10:229 rwm"
|
||||
state: present
|
||||
notify: restart gitea lxc
|
||||
|
||||
- name: Bind mount FUSE device in Gitea LXC config
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/etc/pve/lxc/{{ gitea_vmid }}.conf"
|
||||
line: "lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file"
|
||||
state: present
|
||||
notify: restart gitea lxc
|
||||
|
||||
- name: Bind mount Gitea data directory in LXC config
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/etc/pve/lxc/{{ gitea_vmid }}.conf"
|
||||
line: "mp0: {{ gitea_host_data_dir }},mp={{ gitea_container_data_dir }}"
|
||||
state: present
|
||||
notify: restart gitea lxc
|
||||
|
||||
- name: Ensure Gitea host data directory ownership matches unprivileged LXC uid map
|
||||
ansible.builtin.file:
|
||||
path: "{{ gitea_host_data_dir }}"
|
||||
state: directory
|
||||
owner: "{{ gitea_host_data_uid }}"
|
||||
group: "{{ gitea_host_data_gid }}"
|
||||
recurse: true
|
||||
|
||||
- name: Apply pending LXC config changes
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Wait for Gitea SSH through ru-vps
|
||||
ansible.builtin.wait_for_connection:
|
||||
timeout: 120
|
||||
delegate_to: gitea
|
||||
vars:
|
||||
ansible_become: false
|
||||
|
||||
- name: Configure Docker and Gitea inside LXC
|
||||
hosts: gitea
|
||||
gather_facts: true
|
||||
vars:
|
||||
ansible_become: false
|
||||
gitea_data_dir: /opt/gitea/data
|
||||
gitea_image: gitea/gitea:latest
|
||||
gitea_container_name: gitea
|
||||
gitea_http_port: 3000
|
||||
gitea_ssh_port: 2222
|
||||
tasks:
|
||||
- name: Install Docker packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- docker.io
|
||||
- fuse-overlayfs
|
||||
- sqlite3
|
||||
- rsync
|
||||
- ca-certificates
|
||||
- curl
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Ensure Docker config directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/docker
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure Docker storage driver for unprivileged LXC
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/docker/daemon.json
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
{
|
||||
"storage-driver": "fuse-overlayfs"
|
||||
}
|
||||
register: docker_daemon_config
|
||||
|
||||
- name: Ensure Docker service is enabled and running
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: "{{ 'restarted' if docker_daemon_config.changed else 'started' }}"
|
||||
enabled: true
|
||||
|
||||
- name: Ensure Gitea data directory has container ownership
|
||||
ansible.builtin.file:
|
||||
path: "{{ gitea_data_dir }}"
|
||||
state: directory
|
||||
owner: "1000"
|
||||
group: "1000"
|
||||
mode: "0750"
|
||||
|
||||
- name: Install Gitea systemd unit
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/systemd/system/gitea.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Gitea container
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
ExecStartPre=-/usr/bin/docker rm -f {{ gitea_container_name }}
|
||||
ExecStart=/usr/bin/docker run --rm \
|
||||
--name {{ gitea_container_name }} \
|
||||
--pull always \
|
||||
-p {{ gitea_http_port }}:3000 \
|
||||
-p {{ gitea_ssh_port }}:22 \
|
||||
-v {{ gitea_data_dir }}:/data \
|
||||
-e USER_UID=1000 \
|
||||
-e USER_GID=1000 \
|
||||
{{ gitea_image }}
|
||||
ExecStop=/usr/bin/docker stop {{ gitea_container_name }}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
register: gitea_unit
|
||||
|
||||
- name: Reload systemd when Gitea unit changes
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: gitea_unit.changed
|
||||
|
||||
- name: Enable and start Gitea
|
||||
ansible.builtin.systemd:
|
||||
name: gitea
|
||||
state: started
|
||||
enabled: true
|
||||
@@ -0,0 +1,174 @@
|
||||
- name: Create Vaultwarden LXC on mini-pc
|
||||
hosts: mini-pc
|
||||
gather_facts: false
|
||||
vars:
|
||||
vaultwarden_vmid: 140
|
||||
vaultwarden_hostname: vaultwarden
|
||||
vaultwarden_ip: 192.168.1.24/24
|
||||
vaultwarden_gateway: 192.168.1.1
|
||||
vaultwarden_bridge: vmbr0
|
||||
vaultwarden_rootfs: local-lvm:16
|
||||
vaultwarden_ostemplate: local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst
|
||||
vaultwarden_pubkey_file: ~/.ssh/id_ed25519_homelab.pub
|
||||
handlers:
|
||||
- name: restart vaultwarden lxc
|
||||
ansible.builtin.shell: "pct stop {{ vaultwarden_vmid }} || true; pct start {{ vaultwarden_vmid }}"
|
||||
changed_when: true
|
||||
tasks:
|
||||
- name: Check if Vaultwarden LXC exists
|
||||
ansible.builtin.command: "pct config {{ vaultwarden_vmid }}"
|
||||
register: vaultwarden_pct_config
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Install Vaultwarden LXC SSH public key on PVE host
|
||||
ansible.builtin.copy:
|
||||
dest: /tmp/vaultwarden-lxc.pub
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
content: "{{ lookup('file', vaultwarden_pubkey_file) }}\n"
|
||||
when: vaultwarden_pct_config.rc != 0
|
||||
|
||||
- name: Create Vaultwarden LXC
|
||||
ansible.builtin.command: >-
|
||||
pct create {{ vaultwarden_vmid }} {{ vaultwarden_ostemplate }}
|
||||
--hostname {{ vaultwarden_hostname }}
|
||||
--rootfs {{ vaultwarden_rootfs }}
|
||||
--cores 2
|
||||
--memory 1024
|
||||
--swap 512
|
||||
--net0 name=eth0,bridge={{ vaultwarden_bridge }},gw={{ vaultwarden_gateway }},ip={{ vaultwarden_ip }},firewall=1
|
||||
--nameserver 1.1.1.1
|
||||
--unprivileged 1
|
||||
--features nesting=1,keyctl=1
|
||||
--onboot 1
|
||||
--startup order=40
|
||||
--cmode shell
|
||||
--ssh-public-keys /tmp/vaultwarden-lxc.pub
|
||||
when: vaultwarden_pct_config.rc != 0
|
||||
|
||||
- name: Start Vaultwarden LXC
|
||||
ansible.builtin.command: "pct start {{ vaultwarden_vmid }}"
|
||||
register: vaultwarden_pct_start
|
||||
changed_when: vaultwarden_pct_start.rc == 0
|
||||
failed_when: vaultwarden_pct_start.rc not in [0, 255]
|
||||
|
||||
- name: Allow FUSE device in Vaultwarden LXC config
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/etc/pve/lxc/{{ vaultwarden_vmid }}.conf"
|
||||
line: "lxc.cgroup2.devices.allow: c 10:229 rwm"
|
||||
state: present
|
||||
notify: restart vaultwarden lxc
|
||||
|
||||
- name: Bind mount FUSE device in Vaultwarden LXC config
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/etc/pve/lxc/{{ vaultwarden_vmid }}.conf"
|
||||
line: "lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file"
|
||||
state: present
|
||||
notify: restart vaultwarden lxc
|
||||
|
||||
- name: Apply pending LXC config changes
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: Wait for Vaultwarden SSH through ru-vps
|
||||
ansible.builtin.wait_for_connection:
|
||||
timeout: 120
|
||||
delegate_to: vaultwarden
|
||||
vars:
|
||||
ansible_become: false
|
||||
|
||||
- name: Configure Docker and Vaultwarden inside LXC
|
||||
hosts: vaultwarden
|
||||
gather_facts: true
|
||||
vars:
|
||||
ansible_become: false
|
||||
vaultwarden_data_dir: /opt/vaultwarden/data
|
||||
vaultwarden_image: vaultwarden/server:latest
|
||||
vaultwarden_container_name: vaultwarden
|
||||
vaultwarden_http_port: 80
|
||||
tasks:
|
||||
- name: Install Docker packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- docker.io
|
||||
- fuse-overlayfs
|
||||
- sqlite3
|
||||
- ca-certificates
|
||||
- curl
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Ensure Docker config directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/docker
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure Docker storage driver for unprivileged LXC
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/docker/daemon.json
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
{
|
||||
"storage-driver": "fuse-overlayfs"
|
||||
}
|
||||
register: docker_daemon_config
|
||||
|
||||
- name: Ensure Docker service is enabled and running
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: "{{ 'restarted' if docker_daemon_config.changed else 'started' }}"
|
||||
enabled: true
|
||||
|
||||
- name: Ensure Vaultwarden data directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ vaultwarden_data_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
- name: Install Vaultwarden systemd unit
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/systemd/system/vaultwarden.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Vaultwarden container
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
ExecStartPre=-/usr/bin/docker rm -f {{ vaultwarden_container_name }}
|
||||
ExecStart=/usr/bin/docker run --rm \
|
||||
--name {{ vaultwarden_container_name }} \
|
||||
--pull always \
|
||||
-p {{ vaultwarden_http_port }}:80 \
|
||||
-v {{ vaultwarden_data_dir }}:/data \
|
||||
-e WEBSOCKET_ENABLED=true \
|
||||
{{ vaultwarden_image }}
|
||||
ExecStop=/usr/bin/docker stop {{ vaultwarden_container_name }}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
register: vaultwarden_unit
|
||||
|
||||
- name: Reload systemd when Vaultwarden unit changes
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: vaultwarden_unit.changed
|
||||
|
||||
- name: Enable and start Vaultwarden
|
||||
ansible.builtin.systemd:
|
||||
name: vaultwarden
|
||||
state: started
|
||||
enabled: true
|
||||
@@ -0,0 +1,38 @@
|
||||
- name: Create wg-mini LXC on mini-pc
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
roles:
|
||||
- role: pve_lxc
|
||||
vars:
|
||||
pve_lxc_vmid: 132
|
||||
pve_lxc_node: mini-pc
|
||||
pve_lxc_hostname: wg-mini
|
||||
pve_lxc_ip: 192.168.1.23/24
|
||||
pve_lxc_gateway: 192.168.1.1
|
||||
pve_lxc_storage: local-lvm
|
||||
pve_lxc_disk: local-lvm:8
|
||||
pve_lxc_ostemplate: "{{ lookup('env', 'PVE_LXC_OSTEMPLATE') | default('local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst', true) }}"
|
||||
|
||||
- name: Allow TUN device in wg-mini LXC config
|
||||
hosts: mini-pc
|
||||
gather_facts: false
|
||||
become: true
|
||||
handlers:
|
||||
- name: restart wg-mini lxc
|
||||
ansible.builtin.shell: pct stop 132 || true; pct start 132
|
||||
changed_when: true
|
||||
tasks:
|
||||
- name: Allow /dev/net/tun device
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/pve/lxc/132.conf
|
||||
line: "lxc.cgroup2.devices.allow: c 10:200 rwm"
|
||||
state: present
|
||||
notify: restart wg-mini lxc
|
||||
|
||||
- name: Bind mount /dev/net/tun
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/pve/lxc/132.conf
|
||||
line: "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file"
|
||||
state: present
|
||||
notify: restart wg-mini lxc
|
||||
@@ -0,0 +1,65 @@
|
||||
- name: Configure Gitea reverse proxy on ru-vps
|
||||
hosts: ru-vps
|
||||
gather_facts: false
|
||||
vars:
|
||||
caddy_dir: /opt/services/ru-vps/caddy
|
||||
caddyfile_path: /opt/services/ru-vps/caddy/Caddyfile
|
||||
gitea_domain: git.ada-dev.ru
|
||||
gitea_upstream: 192.168.1.25:3000
|
||||
|
||||
tasks:
|
||||
- name: Ensure Caddy service directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure Gitea Caddy site
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ caddyfile_path }}"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
marker: "# {mark} ANSIBLE MANAGED GITEA SITE"
|
||||
block: |
|
||||
{{ gitea_domain }} {
|
||||
reverse_proxy {{ gitea_upstream }}
|
||||
}
|
||||
register: gitea_caddy_site
|
||||
|
||||
- name: Remove legacy unmanaged Gitea Caddy site
|
||||
ansible.builtin.replace:
|
||||
path: "{{ caddyfile_path }}"
|
||||
regexp: '(?ms)^git\.ada-dev\.ru \{\n\s*reverse_proxy 10\.122\.62\.51:3002\n\}\n+'
|
||||
replace: ''
|
||||
register: gitea_legacy_site
|
||||
|
||||
- name: Validate host Caddy config
|
||||
ansible.builtin.command: caddy validate --config {{ caddyfile_path }}
|
||||
changed_when: false
|
||||
|
||||
- name: Check Caddy container mounted config
|
||||
ansible.builtin.command: docker exec caddy grep -F 'reverse_proxy {{ gitea_upstream }}' /etc/caddy/Caddyfile
|
||||
register: gitea_container_caddyfile
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Restart Caddy when config changed or bind mount is stale
|
||||
ansible.builtin.command: docker restart caddy
|
||||
when: >-
|
||||
gitea_caddy_site.changed or
|
||||
gitea_legacy_site.changed or
|
||||
gitea_container_caddyfile.rc != 0
|
||||
|
||||
- name: Validate Caddy container config after restart
|
||||
ansible.builtin.command: docker exec caddy caddy validate --config /etc/caddy/Caddyfile
|
||||
changed_when: false
|
||||
|
||||
- name: Check Gitea upstream from ru-vps
|
||||
ansible.builtin.command: curl -fsS -o /dev/null -w '%{http_code}' http://{{ gitea_upstream }}/
|
||||
register: gitea_upstream_http
|
||||
changed_when: false
|
||||
failed_when: gitea_upstream_http.stdout != '200'
|
||||
@@ -0,0 +1,65 @@
|
||||
- name: Configure Vaultwarden reverse proxy on ru-vps
|
||||
hosts: ru-vps
|
||||
gather_facts: false
|
||||
vars:
|
||||
caddy_dir: /opt/services/ru-vps/caddy
|
||||
caddyfile_path: /opt/services/ru-vps/caddy/Caddyfile
|
||||
vaultwarden_domain: pass.ada-dev.ru
|
||||
vaultwarden_upstream: 192.168.1.24:80
|
||||
|
||||
tasks:
|
||||
- name: Ensure Caddy service directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ caddy_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure Vaultwarden Caddy site
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ caddyfile_path }}"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
marker: "# {mark} ANSIBLE MANAGED VAULTWARDEN SITE"
|
||||
block: |
|
||||
{{ vaultwarden_domain }} {
|
||||
reverse_proxy {{ vaultwarden_upstream }}
|
||||
}
|
||||
register: vaultwarden_caddy_site
|
||||
|
||||
- name: Remove legacy unmanaged Vaultwarden Caddy site
|
||||
ansible.builtin.replace:
|
||||
path: "{{ caddyfile_path }}"
|
||||
regexp: '(?ms)^pass\.ada-dev\.ru \{\n\s*reverse_proxy 10\.122\.62\.95:10380\n\}\n+'
|
||||
replace: ''
|
||||
register: vaultwarden_legacy_site
|
||||
|
||||
- name: Validate host Caddy config
|
||||
ansible.builtin.command: caddy validate --config {{ caddyfile_path }}
|
||||
changed_when: false
|
||||
|
||||
- name: Check Caddy container mounted config
|
||||
ansible.builtin.command: docker exec caddy grep -F 'reverse_proxy {{ vaultwarden_upstream }}' /etc/caddy/Caddyfile
|
||||
register: vaultwarden_container_caddyfile
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Restart Caddy when config changed or bind mount is stale
|
||||
ansible.builtin.command: docker restart caddy
|
||||
when: >-
|
||||
vaultwarden_caddy_site.changed or
|
||||
vaultwarden_legacy_site.changed or
|
||||
vaultwarden_container_caddyfile.rc != 0
|
||||
|
||||
- name: Validate Caddy container config after restart
|
||||
ansible.builtin.command: docker exec caddy caddy validate --config /etc/caddy/Caddyfile
|
||||
changed_when: false
|
||||
|
||||
- name: Check Vaultwarden upstream from ru-vps
|
||||
ansible.builtin.command: curl -fsS -o /dev/null -w '%{http_code}' http://{{ vaultwarden_upstream }}/
|
||||
register: vaultwarden_upstream_http
|
||||
changed_when: false
|
||||
failed_when: vaultwarden_upstream_http.stdout != '200'
|
||||
@@ -0,0 +1,3 @@
|
||||
ansible-core>=2.19
|
||||
proxmoxer>=2.3
|
||||
requests>=2.31
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
collections:
|
||||
- name: ansible.posix
|
||||
version: ">=2.0.0"
|
||||
- name: community.proxmox
|
||||
version: ">=2.0.0"
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
bash_config_file: /etc/profile.d/homelab.sh
|
||||
bash_config_bashrc: /etc/bash.bashrc
|
||||
bash_config_legacy_user: ada
|
||||
bash_config_legacy_home: "/home/{{ bash_config_legacy_user }}"
|
||||
bash_config_legacy_profile_files:
|
||||
- /etc/profile.d/custom_bash.sh
|
||||
@@ -0,0 +1,63 @@
|
||||
- name: Install unified HomeLab bash config
|
||||
ansible.builtin.template:
|
||||
src: homelab.sh.j2
|
||||
dest: "{{ bash_config_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Ensure system bashrc sources managed config
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ bash_config_bashrc }}"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
marker: "# {mark} ANSIBLE MANAGED HOMELAB BASH CONFIG"
|
||||
block: |
|
||||
if [ -f /etc/profile.d/homelab.sh ]; then
|
||||
. /etc/profile.d/homelab.sh
|
||||
fi
|
||||
|
||||
- name: List bash users with local home directories
|
||||
ansible.builtin.shell: >-
|
||||
getent passwd | awk -F: '$7 ~ /\/bash$/ && $6 ~ /^\// { print $1 ":" $6 }'
|
||||
register: bash_config_bash_users_raw
|
||||
changed_when: false
|
||||
|
||||
- name: Ensure bash users source managed system config
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ item.split(':')[1] }}/.bashrc"
|
||||
create: true
|
||||
owner: "{{ item.split(':')[0] }}"
|
||||
group: "{{ item.split(':')[0] }}"
|
||||
mode: "0644"
|
||||
marker: "# {mark} ANSIBLE MANAGED HOMELAB SYSTEM BASH CONFIG"
|
||||
block: |
|
||||
if [ -f /etc/profile.d/homelab.sh ]; then
|
||||
. /etc/profile.d/homelab.sh
|
||||
fi
|
||||
loop: "{{ bash_config_bash_users_raw.stdout_lines }}"
|
||||
|
||||
- name: Check legacy per-user bashrc
|
||||
ansible.builtin.stat:
|
||||
path: "{{ bash_config_legacy_home }}/.bashrc"
|
||||
register: bash_config_legacy_bashrc
|
||||
|
||||
- name: Remove legacy per-user bashrc source block
|
||||
ansible.builtin.blockinfile:
|
||||
path: "{{ bash_config_legacy_home }}/.bashrc"
|
||||
marker: "# {mark} ANSIBLE MANAGED HOMELAB BASH CONFIG"
|
||||
state: absent
|
||||
when: bash_config_legacy_bashrc.stat.exists
|
||||
|
||||
- name: Remove legacy per-user bash config file
|
||||
ansible.builtin.file:
|
||||
path: "{{ bash_config_legacy_home }}/.bashrc.d/homelab.sh"
|
||||
state: absent
|
||||
|
||||
- name: Remove legacy profile scripts that conflict with unified config
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ bash_config_legacy_profile_files }}"
|
||||
@@ -0,0 +1,44 @@
|
||||
# Managed by Ansible: HomeLab unified bash config.
|
||||
|
||||
export EDITOR="${EDITOR:-vim}"
|
||||
export PAGER="${PAGER:-less}"
|
||||
export LESS="${LESS:--FRSX}"
|
||||
export HISTCONTROL="ignoreboth:erasedups"
|
||||
export HISTSIZE=50000
|
||||
export HISTFILESIZE=100000
|
||||
shopt -s histappend 2>/dev/null || true
|
||||
shopt -s checkwinsize 2>/dev/null || true
|
||||
|
||||
case ":$PATH:" in
|
||||
*":$HOME/.local/bin:"*) ;;
|
||||
*) export PATH="$HOME/.local/bin:$PATH" ;;
|
||||
esac
|
||||
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
alias grep='grep --color=auto'
|
||||
alias ip='ip --color=auto'
|
||||
alias df='df -h'
|
||||
alias du='du -h'
|
||||
alias free='free -h'
|
||||
alias ..='cd ..'
|
||||
alias ...='cd ../..'
|
||||
alias please='sudo $(history -p !!)'
|
||||
|
||||
alias gs='git status --short --branch'
|
||||
alias gd='git diff'
|
||||
alias gl='git log --oneline --decorate -10'
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
alias sctl='systemctl'
|
||||
alias jctl='journalctl'
|
||||
fi
|
||||
|
||||
if [ -n "$PS1" ]; then
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
PS1='\[\e[31m\]\u@\h\[\e[0m\]:\[\e[34m\]\w\[\e[0m\]# '
|
||||
else
|
||||
PS1='\[\e[32m\]\u@\h\[\e[0m\]:\[\e[34m\]\w\[\e[0m\]\$ '
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
openvpn_interface: tun0
|
||||
openvpn_listen_port: 8443
|
||||
openvpn_proto_server: tcp-server
|
||||
openvpn_proto_client: tcp-client
|
||||
openvpn_config_dir: /etc/openvpn/homelab
|
||||
openvpn_config_path: /etc/openvpn/homelab/homelab.conf
|
||||
openvpn_static_key_path: /etc/openvpn/homelab/static.key
|
||||
openvpn_service_name: homelab-openvpn
|
||||
openvpn_home_routes:
|
||||
- network: 192.168.1.5
|
||||
netmask: 255.255.255.255
|
||||
- network: 192.168.1.6
|
||||
netmask: 255.255.255.254
|
||||
- network: 192.168.1.8
|
||||
netmask: 255.255.255.248
|
||||
- network: 192.168.1.16
|
||||
netmask: 255.255.255.240
|
||||
- network: 192.168.1.32
|
||||
netmask: 255.255.255.248
|
||||
- network: 192.168.1.40
|
||||
netmask: 255.255.255.255
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: restart openvpn
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ openvpn_service_name }}"
|
||||
state: restarted
|
||||
enabled: true
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
- name: Install OpenVPN packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- openvpn
|
||||
- iptables
|
||||
- iproute2
|
||||
- netcat-openbsd
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Ensure OpenVPN config directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ openvpn_config_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: Generate static key on server if missing
|
||||
ansible.builtin.command: "openvpn --genkey secret {{ openvpn_static_key_path }}"
|
||||
args:
|
||||
creates: "{{ openvpn_static_key_path }}"
|
||||
when: openvpn_role == 'server'
|
||||
no_log: true
|
||||
|
||||
- name: Read static key from server
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ openvpn_static_key_path }}"
|
||||
register: openvpn_static_key_raw
|
||||
when: openvpn_role == 'server'
|
||||
no_log: true
|
||||
|
||||
- name: Copy static key to all peers
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ openvpn_static_key_path }}"
|
||||
content: "{{ hostvars['ru-vps'].openvpn_static_key_raw.content | b64decode }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
notify: restart openvpn
|
||||
|
||||
- name: Enable IPv4 forwarding on gateway
|
||||
ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: "1"
|
||||
state: present
|
||||
reload: true
|
||||
when: openvpn_role == 'gateway'
|
||||
|
||||
- name: Allow OpenVPN TCP in UFW on server
|
||||
ansible.builtin.command: "ufw allow {{ openvpn_listen_port }}/tcp"
|
||||
register: ufw_allow_openvpn
|
||||
changed_when: "'Rule added' in ufw_allow_openvpn.stdout or 'Rules updated' in ufw_allow_openvpn.stdout"
|
||||
failed_when: false
|
||||
when: openvpn_role == 'server'
|
||||
|
||||
- name: Render OpenVPN config
|
||||
ansible.builtin.template:
|
||||
src: homelab.conf.j2
|
||||
dest: "{{ openvpn_config_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
notify: restart openvpn
|
||||
|
||||
- name: Render OpenVPN gateway up script
|
||||
ansible.builtin.template:
|
||||
src: up.sh.j2
|
||||
dest: "{{ openvpn_config_dir }}/up.sh"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
notify: restart openvpn
|
||||
when: openvpn_role == 'gateway'
|
||||
|
||||
- name: Render OpenVPN gateway down script
|
||||
ansible.builtin.template:
|
||||
src: down.sh.j2
|
||||
dest: "{{ openvpn_config_dir }}/down.sh"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
notify: restart openvpn
|
||||
when: openvpn_role == 'gateway'
|
||||
|
||||
- name: Render OpenVPN systemd service
|
||||
ansible.builtin.template:
|
||||
src: homelab-openvpn.service.j2
|
||||
dest: "/etc/systemd/system/{{ openvpn_service_name }}.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: restart openvpn
|
||||
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Enable and start OpenVPN
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ openvpn_service_name }}"
|
||||
state: started
|
||||
enabled: true
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
iptables -t nat -D POSTROUTING -s {{ openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o eth0 -j MASQUERADE 2>/dev/null || true
|
||||
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=HomeLab OpenVPN {{ openvpn_role }}
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/sbin/openvpn --config {{ openvpn_config_path }}
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,26 @@
|
||||
dev {{ openvpn_interface }}
|
||||
ifconfig {{ openvpn_local_ip }} {{ openvpn_peer_ip }}
|
||||
secret {{ openvpn_static_key_path }}
|
||||
cipher AES-256-CBC
|
||||
auth SHA256
|
||||
persist-key
|
||||
persist-tun
|
||||
verb 3
|
||||
keepalive 10 60
|
||||
{% if openvpn_role == 'server' %}
|
||||
proto {{ openvpn_proto_server }}
|
||||
port {{ openvpn_listen_port }}
|
||||
{% for route in openvpn_home_routes %}
|
||||
route {{ route.network }} {{ route.netmask }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if openvpn_role == 'gateway' %}
|
||||
proto {{ openvpn_proto_client }}
|
||||
remote 157.22.231.198 {{ openvpn_listen_port }}
|
||||
resolv-retry infinite
|
||||
nobind
|
||||
route-nopull
|
||||
script-security 2
|
||||
up /etc/openvpn/homelab/up.sh
|
||||
down /etc/openvpn/homelab/down.sh
|
||||
{% endif %}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
iptables -t nat -C POSTROUTING -s {{ openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o eth0 -j MASQUERADE 2>/dev/null || \
|
||||
iptables -t nat -A POSTROUTING -s {{ openvpn_network_cidr }} -d {{ homelab_lan_cidr }} -o eth0 -j MASQUERADE
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
pve_lxc_vmid: 132
|
||||
pve_lxc_node: mini-pc
|
||||
pve_lxc_hostname: wg-mini
|
||||
pve_lxc_ip: 192.168.1.23/24
|
||||
pve_lxc_gateway: 192.168.1.1
|
||||
pve_lxc_bridge: vmbr0
|
||||
pve_lxc_storage: local-lvm
|
||||
pve_lxc_disk: local-lvm:8
|
||||
pve_lxc_cores: 1
|
||||
pve_lxc_memory: 256
|
||||
pve_lxc_swap: 128
|
||||
pve_lxc_onboot: true
|
||||
pve_lxc_startup: order=30
|
||||
pve_lxc_unprivileged: true
|
||||
pve_lxc_features:
|
||||
- nesting=1
|
||||
pve_lxc_nameserver: 1.1.1.1
|
||||
pve_lxc_pubkey_file: ~/.ssh/id_ed25519_homelab.pub
|
||||
pve_lxc_cmode: shell
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
- name: Validate Proxmox API environment
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- lookup('env', 'PROXMOX_HOST') | length > 0
|
||||
- lookup('env', 'PROXMOX_USER') | length > 0
|
||||
- lookup('env', 'PROXMOX_TOKEN_ID') | length > 0
|
||||
- lookup('env', 'PROXMOX_TOKEN_SECRET') | length > 0
|
||||
fail_msg: Load ansible/.env with PROXMOX_HOST, PROXMOX_USER, PROXMOX_TOKEN_ID and PROXMOX_TOKEN_SECRET.
|
||||
|
||||
- name: Create or update LXC container
|
||||
community.proxmox.proxmox:
|
||||
api_host: "{{ lookup('env', 'PROXMOX_HOST') }}"
|
||||
api_user: "{{ lookup('env', 'PROXMOX_USER') }}"
|
||||
api_token_id: "{{ lookup('env', 'PROXMOX_TOKEN_ID') }}"
|
||||
api_token_secret: "{{ lookup('env', 'PROXMOX_TOKEN_SECRET') }}"
|
||||
validate_certs: "{{ lookup('env', 'PROXMOX_VALIDATE_CERTS') | default('false', true) | bool }}"
|
||||
vmid: "{{ pve_lxc_vmid }}"
|
||||
node: "{{ pve_lxc_node }}"
|
||||
hostname: "{{ pve_lxc_hostname }}"
|
||||
ostemplate: "{{ pve_lxc_ostemplate }}"
|
||||
disk: "{{ pve_lxc_disk }}"
|
||||
cores: "{{ pve_lxc_cores }}"
|
||||
memory: "{{ pve_lxc_memory }}"
|
||||
swap: "{{ pve_lxc_swap }}"
|
||||
nameserver: "{{ pve_lxc_nameserver }}"
|
||||
cmode: "{{ pve_lxc_cmode }}"
|
||||
netif:
|
||||
net0: "name=eth0,gw={{ pve_lxc_gateway }},ip={{ pve_lxc_ip }},bridge={{ pve_lxc_bridge }},firewall=1"
|
||||
pubkey: "{{ lookup('file', pve_lxc_pubkey_file) }}"
|
||||
features: "{{ pve_lxc_features }}"
|
||||
unprivileged: "{{ pve_lxc_unprivileged }}"
|
||||
onboot: "{{ pve_lxc_onboot }}"
|
||||
startup: "{{ pve_lxc_startup }}"
|
||||
update: true
|
||||
state: present
|
||||
|
||||
- name: Start LXC container
|
||||
community.proxmox.proxmox:
|
||||
api_host: "{{ lookup('env', 'PROXMOX_HOST') }}"
|
||||
api_user: "{{ lookup('env', 'PROXMOX_USER') }}"
|
||||
api_token_id: "{{ lookup('env', 'PROXMOX_TOKEN_ID') }}"
|
||||
api_token_secret: "{{ lookup('env', 'PROXMOX_TOKEN_SECRET') }}"
|
||||
validate_certs: "{{ lookup('env', 'PROXMOX_VALIDATE_CERTS') | default('false', true) | bool }}"
|
||||
vmid: "{{ pve_lxc_vmid }}"
|
||||
state: started
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Deploy cloud-pc
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stack:
|
||||
description: "Stack to deploy (leave empty to reconcile all)"
|
||||
required: false
|
||||
push:
|
||||
paths:
|
||||
- ".gitea/workflows/deploy-cloud-pc.yml"
|
||||
- "cloud-pc/**"
|
||||
- "scripts/deploy.sh"
|
||||
schedule:
|
||||
- cron: "0 4 * * *"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: cloud-pc
|
||||
steps:
|
||||
- name: Sync repo
|
||||
run: |
|
||||
git config --global --replace-all safe.directory /opt/services
|
||||
git -C /opt/services pull origin main --ff-only
|
||||
chown -R 1000:100 /opt/services
|
||||
|
||||
- name: Deploy stacks
|
||||
run: /opt/services/scripts/deploy.sh cloud-pc "${{ inputs.stack }}"
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Deploy mini-pc
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stack:
|
||||
description: "Stack to deploy (leave empty to reconcile all)"
|
||||
required: false
|
||||
push:
|
||||
paths:
|
||||
- ".gitea/workflows/deploy-mini-pc.yml"
|
||||
- "mini-pc/**"
|
||||
- "scripts/deploy.sh"
|
||||
schedule:
|
||||
- cron: "0 4 * * *"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: mini-pc
|
||||
steps:
|
||||
- name: Sync repo
|
||||
run: |
|
||||
git config --global --replace-all safe.directory /opt/services
|
||||
git -C /opt/services pull origin main --ff-only
|
||||
chown -R 1000:100 /opt/services
|
||||
|
||||
- name: Deploy stacks
|
||||
run: /opt/services/scripts/deploy.sh mini-pc "${{ inputs.stack }}"
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Deploy ru-vps
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stack:
|
||||
description: "Stack to deploy (leave empty to reconcile all)"
|
||||
required: false
|
||||
|
||||
push:
|
||||
paths:
|
||||
- ".gitea/workflows/deploy-ru-vps.yml"
|
||||
- "ru-vps/**"
|
||||
schedule:
|
||||
- cron: "0 4 * * *"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ru-vps
|
||||
steps:
|
||||
- name: Sync repo
|
||||
run: |
|
||||
git config --global --replace-all safe.directory /opt/services
|
||||
git -C /opt/services pull origin main --ff-only
|
||||
chown -R 1000:100 /opt/services
|
||||
|
||||
- name: Deploy stacks
|
||||
run: /opt/services/scripts/deploy.sh ru-vps "${{ inputs.stack }}"
|
||||
@@ -0,0 +1,33 @@
|
||||
name: NixOS switch cloud-pc
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- ".gitea/workflows/nixos-switch-cloud-pc.yml"
|
||||
- "nixos/flake.nix"
|
||||
- "nixos/flake.lock"
|
||||
- "nixos/modules/**"
|
||||
- "nixos/hosts/cloud-pc/**"
|
||||
|
||||
jobs:
|
||||
switch:
|
||||
runs-on: cloud-pc
|
||||
steps:
|
||||
- name: Sync repo
|
||||
run: |
|
||||
git config --global --replace-all safe.directory /opt/services
|
||||
git -C /opt/services pull origin main --ff-only
|
||||
chown -R 1000:100 /opt/services
|
||||
|
||||
- name: Switch NixOS generation
|
||||
env:
|
||||
NIXOS_DEPLOY_KEY: ${{ secrets.NIXOS_DEPLOY_KEY }}
|
||||
run: |
|
||||
KEY=$(mktemp)
|
||||
echo "$NIXOS_DEPLOY_KEY" > "$KEY"
|
||||
chmod 600 "$KEY"
|
||||
trap "rm -f $KEY" EXIT
|
||||
ssh -i "$KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
ada@192.168.1.5 \
|
||||
"sudo /run/current-system/sw/bin/nixos-rebuild switch --flake /opt/services/nixos#cloud-pc"
|
||||
@@ -0,0 +1,33 @@
|
||||
name: NixOS switch mini-pc
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- ".gitea/workflows/nixos-switch-mini-pc.yml"
|
||||
- "nixos/flake.nix"
|
||||
- "nixos/flake.lock"
|
||||
- "nixos/modules/**"
|
||||
- "nixos/hosts/mini-pc/**"
|
||||
|
||||
jobs:
|
||||
switch:
|
||||
runs-on: mini-pc
|
||||
steps:
|
||||
- name: Sync repo
|
||||
run: |
|
||||
git config --global --replace-all safe.directory /opt/services
|
||||
git -C /opt/services pull origin main --ff-only
|
||||
chown -R 1000:100 /opt/services
|
||||
|
||||
- name: Switch NixOS generation
|
||||
env:
|
||||
NIXOS_DEPLOY_KEY: ${{ secrets.NIXOS_DEPLOY_KEY }}
|
||||
run: |
|
||||
KEY=$(mktemp)
|
||||
echo "$NIXOS_DEPLOY_KEY" > "$KEY"
|
||||
chmod 600 "$KEY"
|
||||
trap "rm -f $KEY" EXIT
|
||||
ssh -i "$KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
ada@192.168.1.10 \
|
||||
"sudo /run/current-system/sw/bin/nixos-rebuild switch --flake /opt/services/nixos#mini-pc"
|
||||
@@ -0,0 +1,13 @@
|
||||
# Archive: 2026-07 Proxmox Migration
|
||||
|
||||
Historical configuration from the pre-Proxmox HomeLab setup.
|
||||
|
||||
Moved here:
|
||||
|
||||
- `nixos/` — old NixOS host configs and modules.
|
||||
- `cloud-pc/`, `mini-pc/`, `ru-vps/` — old Docker Compose service trees.
|
||||
- `.gitea/` — old GitOps workflows.
|
||||
- `scripts/` — old deploy scripts.
|
||||
- `legacy-ansible/` — old bootstrap/base/dirs/restic/rclone playbooks and bootstrap role.
|
||||
|
||||
Use these files only as reference when implementing new Ansible roles/playbooks.
|
||||
@@ -0,0 +1,4 @@
|
||||
FROM gitea/act_runner:latest
|
||||
|
||||
USER root
|
||||
RUN apk add --no-cache docker-cli docker-cli-compose openssh-client
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
services:
|
||||
gitea-runner:
|
||||
image: gitea/act_runner:latest
|
||||
build: .
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /opt/services:/opt/services
|
||||
- /opt/data/gitea-runner:/data
|
||||
environment:
|
||||
GITEA_INSTANCE_URL: https://git.ada-dev.ru
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN: ${TOKEN}
|
||||
GITEA_RUNNER_NAME: cloud-pc-runner
|
||||
GITEA_RUNNER_LABELS: cloud-pc
|
||||
+5
-25
@@ -1,18 +1,4 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:${PG_VER}-alpine
|
||||
container_name: postgres
|
||||
hostname: postgres
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /opt/data/postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
TZ: Europe/Moscow
|
||||
POSTGRES_DB: ${DB_NAME}
|
||||
POSTGRES_USER: ${DB_USER}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis:alpine
|
||||
container_name: redis
|
||||
@@ -20,6 +6,7 @@ services:
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
TZ: Europe/Moscow
|
||||
|
||||
nextcloud:
|
||||
image: nextcloud:${NC_VER}
|
||||
container_name: nextcloud
|
||||
@@ -32,7 +19,6 @@ services:
|
||||
extra_hosts:
|
||||
- ${NC_DOMAIN}:host-gateway
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
volumes:
|
||||
- /opt/configs/nextcloud/app:/var/www/html
|
||||
@@ -43,14 +29,11 @@ services:
|
||||
VIRTUAL_HOST: ${NC_DOMAIN}
|
||||
NEXTCLOUD_TRUSTED_DOMAINS: ${NC_DOMAIN} 127.0.0.1
|
||||
NEXTCLOUD_OVERWRITEPROTOCOL: https
|
||||
NEXTCLOUD_ADMIN_USER: ${NC_ADMIN_USER}
|
||||
NEXTCLOUD_ADMIN_PASSWORD: ${NC_ADMIN_PASSWORD}
|
||||
POSTGRES_HOST: postgres
|
||||
POSTGRES_DB: ${DB_NAME}
|
||||
POSTGRES_USER: ${DB_USER}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
NEXTCLOUD_OVERWRITEHOST: ${NC_DOMAIN}
|
||||
NEXTCLOUD_OVERWRITECLIURL: https://${NC_DOMAIN}
|
||||
REDIS_HOST: redis
|
||||
TRUSTED_PROXIES: ${TRUSTED_PROXIES}
|
||||
|
||||
cron:
|
||||
image: nextcloud:${NC_VER}
|
||||
hostname: cron
|
||||
@@ -64,9 +47,6 @@ services:
|
||||
- nextcloud
|
||||
environment:
|
||||
TZ: Europe/Moscow
|
||||
POSTGRES_HOST: postgres
|
||||
POSTGRES_DB: ${DB_NAME}
|
||||
POSTGRES_USER: ${DB_USER}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
REDIS_HOST: redis
|
||||
|
||||
networks: {}
|
||||
+43
@@ -53,3 +53,46 @@ version = "1"
|
||||
|
||||
[samba.forget]
|
||||
schedule = "03:30"
|
||||
|
||||
# ── Yandex Disk: ночная удалённая копия ───────────────────────────────────────
|
||||
[yadisk-default]
|
||||
inherit = "default"
|
||||
|
||||
[yadisk-default.check]
|
||||
schedule = "Sun 06:00"
|
||||
schedule-permission = "system"
|
||||
schedule-after-network-online = true
|
||||
|
||||
[yadisk-services]
|
||||
inherit = "yadisk-default"
|
||||
repository = "rclone:yadisk:System/Backups/cloud-pc/restic/services"
|
||||
|
||||
[yadisk-services.backup]
|
||||
source = ["/opt/data", "/opt/configs"]
|
||||
tag = ["cloud-pc", "services", "yadisk"]
|
||||
exclude-caches = true
|
||||
one-file-system = true
|
||||
schedule = "04:00"
|
||||
schedule-permission = "system"
|
||||
schedule-after-network-online = true
|
||||
schedule-log = "/var/log/resticprofile/yadisk-services.log"
|
||||
|
||||
[yadisk-services.forget]
|
||||
schedule = "04:30"
|
||||
|
||||
[yadisk-samba]
|
||||
inherit = "yadisk-default"
|
||||
repository = "rclone:yadisk:System/Backups/cloud-pc/restic/samba"
|
||||
|
||||
[yadisk-samba.backup]
|
||||
source = ["/opt/samba"]
|
||||
tag = ["cloud-pc", "samba", "yadisk"]
|
||||
exclude-caches = true
|
||||
one-file-system = true
|
||||
schedule = "05:00"
|
||||
schedule-permission = "system"
|
||||
schedule-after-network-online = true
|
||||
schedule-log = "/var/log/resticprofile/yadisk-samba.log"
|
||||
|
||||
[yadisk-samba.forget]
|
||||
schedule = "05:30"
|
||||
@@ -0,0 +1 @@
|
||||
SAMBA_PASSWORD=
|
||||
+1
@@ -6,6 +6,7 @@
|
||||
- name: Ping hosts
|
||||
ansible.builtin.ping:
|
||||
|
||||
|
||||
- name: Check expected LAN IP
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
+2
@@ -88,9 +88,11 @@
|
||||
- /opt/data/caddy
|
||||
- /opt/data/gitea-runner
|
||||
- /opt/data/npm
|
||||
- /opt/data/uptime-kuma
|
||||
- /opt/data/zerotier
|
||||
- /opt/configs/adguard
|
||||
- /opt/configs/caddy
|
||||
- /opt/configs/caddy/lego
|
||||
- /opt/configs/npm/letsencrypt
|
||||
|
||||
- name: Directory structure — ru-vps
|
||||
@@ -0,0 +1,9 @@
|
||||
- name: Install rclone
|
||||
hosts: servers
|
||||
become: true
|
||||
tasks:
|
||||
- name: Install rclone package
|
||||
ansible.builtin.apt:
|
||||
name: rclone
|
||||
state: present
|
||||
update_cache: true
|
||||
@@ -0,0 +1,36 @@
|
||||
- name: Install resticprofile
|
||||
hosts: servers
|
||||
become: true
|
||||
tasks:
|
||||
- name: Install restic package
|
||||
ansible.builtin.apt:
|
||||
name: restic
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Create restic config directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/restic
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Create resticprofile log directory
|
||||
ansible.builtin.file:
|
||||
path: /var/log/resticprofile
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Download resticprofile install script
|
||||
ansible.builtin.get_url:
|
||||
url: https://raw.githubusercontent.com/creativeprojects/resticprofile/master/install.sh
|
||||
dest: /tmp/resticprofile-install.sh
|
||||
mode: "0755"
|
||||
|
||||
- name: Install resticprofile binary
|
||||
ansible.builtin.command:
|
||||
cmd: /tmp/resticprofile-install.sh -b /usr/local/bin
|
||||
creates: /usr/local/bin/resticprofile
|
||||
+3
@@ -1,2 +1,5 @@
|
||||
bootstrap_user: ada
|
||||
bootstrap_ssh_public_keys: [ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIWNsUQmppB6cQccXX1ZaBbFIcmM6RmghsTVbG9TgoZB ada@ada-x1]
|
||||
docker_registry_mirrors:
|
||||
- https://mirror.gcr.io
|
||||
- https://dockerhub.timeweb.cloud
|
||||
+25
@@ -18,6 +18,31 @@
|
||||
loop: "{{ bootstrap_ssh_public_keys }}"
|
||||
when: bootstrap_ssh_public_keys | length > 0
|
||||
|
||||
- name: Gather service facts
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Ensure Docker config directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/docker
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Configure Docker registry mirrors
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/docker/daemon.json
|
||||
content: "{{ {'registry-mirrors': docker_registry_mirrors} | to_nice_json }}\n"
|
||||
mode: "0644"
|
||||
register: docker_daemon_config
|
||||
when: docker_registry_mirrors | length > 0
|
||||
|
||||
- name: Restart Docker after daemon config change
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: restarted
|
||||
when:
|
||||
- docker_daemon_config is changed
|
||||
- "'docker.service' in ansible_facts.services"
|
||||
|
||||
# Команды, которые реально нужны без пароля:
|
||||
# apt / apt-get — обновление пакетов
|
||||
# systemctl — управление сервисами
|
||||
+1
-1
@@ -31,7 +31,7 @@ services:
|
||||
- 53:53/udp # DNS - **mandatory**
|
||||
#- "67:67/udp" # DHCP server - optional, only if AdGuard handles DHCP
|
||||
#- "68:68/udp" # DHCP server - optional
|
||||
- 808:80/tcp # Dashboard HTTP - optional, only if not using HTTPS
|
||||
- 8088:80/tcp # Dashboard HTTP - optional, only if not using HTTPS
|
||||
- 4438:443/tcp # DNS-over-HTTPS / dashboard HTTPS - optional but recommended if using DoH
|
||||
- 784:784/udp # DNS-over-QUIC (beta) - optional, experimental
|
||||
- 853:853/tcp # DNS-over-TLS - optional
|
||||
@@ -0,0 +1,2 @@
|
||||
LEGO_EMAIL=you@example.com
|
||||
TIMEWEBCLOUD_AUTH_TOKEN=changeme
|
||||
@@ -0,0 +1,19 @@
|
||||
git.ada-dev.ru {
|
||||
tls /config/lego/certificates/ada-dev.ru.crt /config/lego/certificates/ada-dev.ru.key
|
||||
reverse_proxy 192.168.1.5:3002
|
||||
}
|
||||
|
||||
cloud.ada-dev.ru {
|
||||
tls /config/lego/certificates/ada-dev.ru.crt /config/lego/certificates/ada-dev.ru.key
|
||||
reverse_proxy 192.168.1.5:8081
|
||||
}
|
||||
|
||||
router.ada-dev.ru {
|
||||
tls /config/lego/certificates/ada-dev.ru.crt /config/lego/certificates/ada-dev.ru.key
|
||||
reverse_proxy 192.168.1.1:80
|
||||
}
|
||||
|
||||
dns.ada-dev.ru {
|
||||
tls /config/lego/certificates/ada-dev.ru.crt /config/lego/certificates/ada-dev.ru.key
|
||||
reverse_proxy 192.168.1.10:8088
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
services:
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
container_name: caddy
|
||||
restart: unless-stopped
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "443:443/udp"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- /opt/data/caddy:/data
|
||||
- /opt/configs/caddy:/config
|
||||
entrypoint: /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
while [ ! -f /config/lego/certificates/ada-dev.ru.crt ] || [ ! -f /config/lego/certificates/ada-dev.ru.key ]; do
|
||||
sleep 10
|
||||
done
|
||||
exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
|
||||
lego:
|
||||
image: git.ada-dev.ru/ada/lego:latest
|
||||
container_name: caddy-lego
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
env_file:
|
||||
- .env
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /opt/configs/caddy/lego:/lego-data
|
||||
entrypoint: /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
while true; do
|
||||
if [ -f /lego-data/certificates/ada-dev.ru.crt ]; then
|
||||
/lego renew \
|
||||
--email "$$LEGO_EMAIL" \
|
||||
--dns timewebcloud \
|
||||
--dns.resolvers 1.1.1.1:53 \
|
||||
--dns.resolvers 8.8.8.8:53 \
|
||||
--dns.propagation-timeout 5m \
|
||||
--domains ada-dev.ru \
|
||||
--domains '*.ada-dev.ru' \
|
||||
--path /lego-data \
|
||||
--days 30
|
||||
sleep 24h
|
||||
else
|
||||
/lego run \
|
||||
--email "$$LEGO_EMAIL" \
|
||||
--accept-tos \
|
||||
--dns timewebcloud \
|
||||
--dns.resolvers 1.1.1.1:53 \
|
||||
--dns.resolvers 8.8.8.8:53 \
|
||||
--dns.propagation-timeout 5m \
|
||||
--domains ada-dev.ru \
|
||||
--domains '*.ada-dev.ru' \
|
||||
--path /lego-data
|
||||
sleep 10m
|
||||
fi
|
||||
done
|
||||
networks: {}
|
||||
@@ -0,0 +1,4 @@
|
||||
FROM gitea/act_runner:latest
|
||||
|
||||
USER root
|
||||
RUN apk add --no-cache docker-cli docker-cli-compose openssh-client
|
||||
+6
-1
@@ -1,11 +1,16 @@
|
||||
services:
|
||||
gitea-runner:
|
||||
image: gitea/act_runner:latest
|
||||
build: .
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /opt/services:/opt/services
|
||||
- /opt/data/gitea-runner:/data
|
||||
environment:
|
||||
GITEA_INSTANCE_URL: https://git.ada-dev.ru
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN: ${TOKEN}
|
||||
GITEA_RUNNER_NAME: mini-pc-runner
|
||||
GITEA_RUNNER_LABELS: mini-pc
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
@@ -0,0 +1,13 @@
|
||||
services:
|
||||
memoir-bot:
|
||||
image: git.ada-dev.ru/ada/memoir_bot:0.1
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
OBSIDIAN_VAULT_PATH: /vault
|
||||
TZ: ${TIMEZONE:-Europe/Moscow}
|
||||
volumes:
|
||||
- /opt/data/memoir_bot:/vault
|
||||
- /home/ada/.ssh:/root/.ssh:ro
|
||||
network_mode: host
|
||||
@@ -0,0 +1,38 @@
|
||||
version = "1"
|
||||
|
||||
[global]
|
||||
scheduler = "systemd"
|
||||
|
||||
[default]
|
||||
password-file = "/etc/restic/password"
|
||||
initialize = true
|
||||
|
||||
[default.forget]
|
||||
keep-daily = 7
|
||||
keep-weekly = 4
|
||||
keep-monthly = 6
|
||||
schedule-permission = "system"
|
||||
schedule-after-backup = true
|
||||
prune = true
|
||||
|
||||
[default.check]
|
||||
schedule = "Sun 04:00"
|
||||
schedule-permission = "system"
|
||||
schedule-after-network-online = true
|
||||
|
||||
[services]
|
||||
inherit = "default"
|
||||
repository = "rclone:yadisk:System/Backups/mini-pc/restic/services"
|
||||
|
||||
[services.backup]
|
||||
source = ["/opt/data", "/opt/configs"]
|
||||
tag = ["mini-pc", "services"]
|
||||
exclude-caches = true
|
||||
one-file-system = true
|
||||
schedule = "03:00"
|
||||
schedule-permission = "system"
|
||||
schedule-after-network-online = true
|
||||
schedule-log = "/var/log/resticprofile/services.log"
|
||||
|
||||
[services.forget]
|
||||
schedule = "03:30"
|
||||
@@ -0,0 +1,13 @@
|
||||
services:
|
||||
uptime-kuma:
|
||||
image: louislam/uptime-kuma:2
|
||||
container_name: uptime-kuma
|
||||
hostname: uptime-kuma
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3001:3001"
|
||||
environment:
|
||||
TZ: Europe/Moscow
|
||||
volumes:
|
||||
- /opt/data/uptime-kuma:/app/data
|
||||
networks: {}
|
||||
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
v2raya:
|
||||
image: mzz2017/v2raya:latest
|
||||
container_name: v2raya
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
privileged: true
|
||||
volumes:
|
||||
- /opt/data/v2raya:/etc/v2raya
|
||||
- /opt/data/v2raya-extra:/etc/v2raya-extra
|
||||
environment:
|
||||
- V2RAYA_LOG_FILE=/tmp/v2raya.log
|
||||
- V2RAYA_V2RAY_BIN_PATH=/usr/local/bin/v2ray
|
||||
- V2RAYA_V2RAY_CONFDIR=/etc/v2raya-extra
|
||||
@@ -0,0 +1,8 @@
|
||||
ADMIN_TOKEN=changeme
|
||||
SMTP_HOST=smtp.yandex.ru
|
||||
SMTP_PORT=465
|
||||
SMTP_SECURITY=force_tls
|
||||
SMTP_USERNAME=info.ada-dev@yandex.ru
|
||||
SMTP_PASSWORD=changeme
|
||||
SMTP_FROM=info.ada-dev@yandex.ru
|
||||
SMTP_FROM_NAME=Vaultwarden
|
||||
+1
-1
@@ -2,12 +2,12 @@ services:
|
||||
vaultwarden:
|
||||
hostname: vaultwarden
|
||||
image: vaultwarden/server:latest
|
||||
network_mode: bridge
|
||||
ports:
|
||||
- target: 80
|
||||
published: "10380"
|
||||
protocol: tcp
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
volumes:
|
||||
- type: bind
|
||||
source: /opt/data/vaultwarden
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
description = "Homelab NixOS hosts";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, ... }: {
|
||||
nixosConfigurations.cloud-pc = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
./hosts/cloud-pc
|
||||
];
|
||||
};
|
||||
|
||||
nixosConfigurations.mini-pc = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
./hosts/mini-pc
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
../../modules/base.nix
|
||||
../../modules/docker.nix
|
||||
../../modules/libvirtd.nix
|
||||
../../modules/zerotier.nix
|
||||
./firewall.nix
|
||||
./hardware.nix
|
||||
];
|
||||
|
||||
networking = {
|
||||
hostName = "cloud-pc";
|
||||
useDHCP = false;
|
||||
defaultGateway = "192.168.1.1";
|
||||
nameservers = [
|
||||
"192.168.1.1"
|
||||
"1.1.1.1"
|
||||
];
|
||||
|
||||
interfaces.enp2s0.ipv4.addresses = [
|
||||
{
|
||||
address = "192.168.1.5";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
fileSystems."/opt/data" = {
|
||||
device = "/dev/disk/by-uuid/ae278333-4116-4225-9b95-595496fadd26";
|
||||
fsType = "ext4";
|
||||
options = [ "nofail" ];
|
||||
};
|
||||
|
||||
fileSystems."/opt/samba" = {
|
||||
device = "/dev/disk/by-uuid/89024d30-1ab6-4b2e-b23b-62837455b24a";
|
||||
fsType = "ext4";
|
||||
options = [ "nofail" ];
|
||||
};
|
||||
|
||||
fileSystems."/mnt/backup" = {
|
||||
device = "/dev/disk/by-uuid/940a9a4e-10cf-4380-9d22-a71549066561";
|
||||
fsType = "ext4";
|
||||
options = [ "nofail" ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /opt/data/postgres 0755 ada users - -"
|
||||
"d /opt/data/nextcloud 0755 ada users - -"
|
||||
"d /opt/data/gitea 0755 ada users - -"
|
||||
"d /opt/data/gitea-runner 0755 ada users - -"
|
||||
"d /opt/data/syncthing 0755 ada users - -"
|
||||
"d /opt/data/zerotier 0755 ada users - -"
|
||||
"d /opt/configs/nextcloud 0755 ada users - -"
|
||||
"d /opt/configs/nextcloud/app 0755 ada users - -"
|
||||
"d /opt/configs/syncthing 0755 ada users - -"
|
||||
"d /opt/samba 0755 ada users - -"
|
||||
"d /opt/samba/media 0755 ada users - -"
|
||||
"d /opt/samba/documents 0755 ada users - -"
|
||||
"d /opt/samba/backups_win 0755 ada users - -"
|
||||
"d /mnt/backup 0755 ada users - -"
|
||||
"d /mnt/backup/restic 0755 ada users - -"
|
||||
"d /mnt/backup/restic/services 0755 ada users - -"
|
||||
"d /mnt/backup/restic/samba 0755 ada users - -"
|
||||
"d /mnt/backup/devices 0755 ada users - -"
|
||||
"d /mnt/backup/devices/proxmox 0755 ada users - -"
|
||||
"d /mnt/backup/devices/ada-x1 0755 ada users - -"
|
||||
"d /mnt/yandex 0755 ada users - -"
|
||||
];
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
networking.firewall = {
|
||||
enable = true;
|
||||
allowedTCPPorts = [
|
||||
22 # SSH
|
||||
139 # Samba (NetBIOS session)
|
||||
445 # Samba (SMB)
|
||||
2222 # Gitea SSH
|
||||
3002
|
||||
4567
|
||||
5432 # PostgreSQL
|
||||
7080
|
||||
8081
|
||||
8384 # Syncthing web UI
|
||||
22000 # Syncthing data transfer
|
||||
];
|
||||
allowedUDPPorts = [
|
||||
137 # Samba (NetBIOS name)
|
||||
138 # Samba (NetBIOS datagram)
|
||||
9993 # ZeroTier
|
||||
21027 # Syncthing discovery
|
||||
22000 # Syncthing data transfer
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ (modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/" =
|
||||
{ device = "/dev/disk/by-uuid/f729e8cb-5c37-45fe-bd0a-e511d79bb49c";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
fileSystems."/boot" =
|
||||
{ device = "/dev/disk/by-uuid/8BA8-38FE";
|
||||
fsType = "vfat";
|
||||
options = [ "fmask=0022" "dmask=0022" ];
|
||||
};
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
../../modules/base.nix
|
||||
../../modules/docker.nix
|
||||
../../modules/libvirtd.nix
|
||||
../../modules/zerotier.nix
|
||||
./firewall.nix
|
||||
./hardware.nix
|
||||
];
|
||||
|
||||
networking = {
|
||||
hostName = "mini-pc";
|
||||
useDHCP = false;
|
||||
};
|
||||
|
||||
systemd.network = {
|
||||
enable = true;
|
||||
networks."10-lan" = {
|
||||
matchConfig.Name = "en*";
|
||||
networkConfig = {
|
||||
Address = "192.168.1.10/24";
|
||||
Gateway = "192.168.1.1";
|
||||
DNS = [
|
||||
"192.168.1.1"
|
||||
"1.1.1.1"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.resolved = {
|
||||
enable = true;
|
||||
fallbackDns = [ "1.1.1.1" "8.8.8.8" ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /opt/data/adguard 0755 ada users - -"
|
||||
"d /opt/data/adguard/work 0755 ada users - -"
|
||||
"d /opt/data/caddy 0755 ada users - -"
|
||||
"d /opt/data/gitea-runner 0755 ada users - -"
|
||||
"d /opt/data/uptime-kuma 0755 ada users - -"
|
||||
"d /opt/data/vaultwarden 0755 ada users - -"
|
||||
"d /opt/data/zerotier 0755 ada users - -"
|
||||
"d /opt/configs/adguard 0755 ada users - -"
|
||||
"d /opt/configs/caddy 0755 ada users - -"
|
||||
"d /opt/configs/caddy/lego 0755 ada users - -"
|
||||
];
|
||||
|
||||
programs.bash.interactiveShellInit = ''
|
||||
proxy_on() {
|
||||
export http_proxy=http://192.168.1.10:10172
|
||||
export https_proxy=http://192.168.1.10:10172
|
||||
export HTTP_PROXY=http://192.168.1.10:10172
|
||||
export HTTPS_PROXY=http://192.168.1.10:10172
|
||||
export all_proxy=socks5://192.168.1.10:10171
|
||||
export ALL_PROXY=socks5://192.168.1.10:10171
|
||||
export ftp_proxy=http://192.168.1.10:10172
|
||||
export FTP_PROXY=http://192.168.1.10:10172
|
||||
export no_proxy=localhost,127.0.0.0/8,::1,192.168.1.0/24
|
||||
export NO_PROXY=localhost,127.0.0.0/8,::1,192.168.1.0/24
|
||||
}
|
||||
proxy_off() {
|
||||
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
|
||||
unset all_proxy ALL_PROXY ftp_proxy FTP_PROXY
|
||||
unset no_proxy NO_PROXY
|
||||
}
|
||||
'';
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
networking.firewall = {
|
||||
enable = true;
|
||||
allowedTCPPorts = [
|
||||
22 # SSH
|
||||
53 # DNS (AdGuard)
|
||||
80 # HTTP
|
||||
443 # HTTPS
|
||||
784 # DNS-over-QUIC
|
||||
853 # DNS-over-TLS
|
||||
3001 # Uptime Kuma
|
||||
3015
|
||||
4438
|
||||
5443
|
||||
8088
|
||||
10171 # v2raya SOCKS5 LAN
|
||||
10172 # v2raya HTTP proxy LAN
|
||||
10380
|
||||
];
|
||||
allowedUDPPorts = [
|
||||
53 # DNS
|
||||
443 # QUIC
|
||||
784 # DNS-over-QUIC
|
||||
9993 # ZeroTier
|
||||
5443
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ (modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" "sdhci_pci" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/" =
|
||||
{ device = "/dev/disk/by-uuid/a2fc1626-3301-44dd-9d00-fa7e8d216d25";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
fileSystems."/boot" =
|
||||
{ device = "/dev/disk/by-uuid/73C2-3F45";
|
||||
fsType = "vfat";
|
||||
options = [ "fmask=0022" "dmask=0022" ];
|
||||
};
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
nix = {
|
||||
settings = {
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
auto-optimise-store = true;
|
||||
};
|
||||
gc = {
|
||||
automatic = true;
|
||||
dates = "weekly";
|
||||
options = "--delete-older-than 30d";
|
||||
};
|
||||
};
|
||||
|
||||
boot = {
|
||||
tmp.cleanOnBoot = true;
|
||||
loader.systemd-boot.enable = true;
|
||||
loader.efi.canTouchEfiVariables = true;
|
||||
};
|
||||
|
||||
systemd.network.wait-online.anyInterface = true;
|
||||
|
||||
systemd.services.nix-daemon.environment = {
|
||||
HTTPS_PROXY = "http://192.168.1.10:10172";
|
||||
HTTP_PROXY = "http://192.168.1.10:10172";
|
||||
NO_PROXY = "localhost,127.0.0.0/8,::1,192.168.1.0/24";
|
||||
};
|
||||
|
||||
time.timeZone = "Europe/Moscow";
|
||||
|
||||
i18n.defaultLocale = "ru_RU.UTF-8";
|
||||
i18n.supportedLocales = [
|
||||
"ru_RU.UTF-8/UTF-8"
|
||||
"en_US.UTF-8/UTF-8"
|
||||
];
|
||||
|
||||
users.users.ada = {
|
||||
isNormalUser = true;
|
||||
shell = pkgs.bashInteractive;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"docker"
|
||||
];
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIWNsUQmppB6cQccXX1ZaBbFIcmM6RmghsTVbG9TgoZB ada@ada-x1"
|
||||
];
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
PermitRootLogin = "no";
|
||||
PubkeyAuthentication = true;
|
||||
};
|
||||
};
|
||||
|
||||
security.sudo.extraRules = [
|
||||
{
|
||||
users = [ "ada" ];
|
||||
commands = [
|
||||
{
|
||||
command = "/run/current-system/sw/bin/nixos-rebuild";
|
||||
options = [ "NOPASSWD" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
git
|
||||
restic
|
||||
resticprofile
|
||||
htop
|
||||
];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /opt/services 0755 ada users - -"
|
||||
"Z /opt/services - ada users - -"
|
||||
"d /opt/data 0755 ada users - -"
|
||||
"d /opt/configs 0755 ada users - -"
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
virtualisation.docker = {
|
||||
enable = true;
|
||||
daemon.settings = {
|
||||
registry-mirrors = [
|
||||
"https://mirror.gcr.io"
|
||||
"https://dockerhub.timeweb.cloud"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
docker-compose
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
virtualisation.libvirtd = {
|
||||
enable = true;
|
||||
qemu.package = pkgs.qemu_kvm;
|
||||
};
|
||||
|
||||
users.users.ada.extraGroups = [ "libvirtd" ];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
services.zerotierone = {
|
||||
enable = true;
|
||||
joinNetworks = [ "743993800fa5a34c" ];
|
||||
};
|
||||
}
|
||||
@@ -14,6 +14,13 @@ pass.ada-dev.ru {
|
||||
reverse_proxy 10.122.62.95:10380
|
||||
}
|
||||
|
||||
# projects.ada-dev.ru {
|
||||
# reverse_proxy 10.122.62.51:4567
|
||||
# }
|
||||
uptime.ada-dev.ru {
|
||||
reverse_proxy 10.122.62.95:3001
|
||||
}
|
||||
|
||||
|
||||
projects.ada-dev.ru {
|
||||
handle_path /neural* {
|
||||
reverse_proxy 10.122.62.51:4567
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
FROM gitea/act_runner:latest
|
||||
|
||||
USER root
|
||||
RUN apk add --no-cache docker-cli docker-cli-compose
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
services:
|
||||
gitea-runner:
|
||||
image: gitea/act_runner:latest
|
||||
build: .
|
||||
restart: always
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /opt/services:/opt/services
|
||||
- /opt/data/gitea-runner:/data
|
||||
environment:
|
||||
GITEA_INSTANCE_URL: https://git.ada-dev.ru
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN: ${TOKEN}
|
||||
GITEA_RUNNER_NAME: ru-vps-runner
|
||||
GITEA_RUNNER_LABELS: ru-vps
|
||||
@@ -0,0 +1,56 @@
|
||||
mixed-port: 7890
|
||||
http-port: 7892
|
||||
socks-port: 7891
|
||||
allow-lan: true # Меняем с false на true
|
||||
bind-address: "0.0.0.0" # Добавляем - слушаем все интерфейсы
|
||||
mode: rule
|
||||
|
||||
external-controller: 127.0.0.1:9090
|
||||
external-ui: /root/.config/mihomo/ui
|
||||
# secret: "changeme"
|
||||
|
||||
authentication:
|
||||
- "ada:Holivarr588856"
|
||||
|
||||
proxy-providers:
|
||||
main:
|
||||
type: http
|
||||
url: "https://491b5056.withblancvpn.online/s/c1b50e857b4e41a59ea80bcf6d1953d3"
|
||||
interval: 3600
|
||||
health-check:
|
||||
enable: true
|
||||
url: https://www.gstatic.com/generate_204
|
||||
interval: 300
|
||||
|
||||
proxy-groups:
|
||||
- name: "URL-Test"
|
||||
type: url-test
|
||||
use: [main]
|
||||
url: "https://www.gstatic.com/generate_204"
|
||||
interval: 300 # Тестировать каждые 5 минут
|
||||
tolerance: 50
|
||||
|
||||
rules:
|
||||
- DOMAIN-SUFFIX,local,DIRECT
|
||||
- DOMAIN-SUFFIX,lan,DIRECT
|
||||
- DOMAIN-SUFFIX,ru,DIRECT
|
||||
- DOMAIN-SUFFIX,su,DIRECT
|
||||
- DOMAIN-SUFFIX,xn--p1ai,DIRECT
|
||||
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
|
||||
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
|
||||
- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
|
||||
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
|
||||
- IP-CIDR,169.254.0.0/16,DIRECT,no-resolve
|
||||
- IP-CIDR6,::1/128,DIRECT,no-resolve
|
||||
- IP-CIDR6,fc00::/7,DIRECT,no-resolve
|
||||
- IP-CIDR6,fe80::/10,DIRECT,no-resolve
|
||||
- GEOIP,RU,DIRECT
|
||||
- MATCH,URL-Test
|
||||
|
||||
# tun:
|
||||
# enable: true
|
||||
# stack: mixed
|
||||
# auto-route: true
|
||||
# auto-detect-interface: true
|
||||
# dns-hijack:
|
||||
# - "any:53"
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"><script type="importmap">{"imports":{"#entry":"./_nuxt/Bmqvg1eR.js"}}</script><title>MetaCubeXD</title><script src="config.js" onerror="window.__METACUBEXD_CONFIG__={defaultBackendURL:''}"></script><script>window.__METACUBEXD_CONFIG__ = window.__METACUBEXD_CONFIG__ || { defaultBackendURL: '' }</script><link rel="stylesheet" href="./_nuxt/entry.B_k74qUh.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="./_nuxt/Bmqvg1eR.js"><link rel="modulepreload" as="script" crossorigin href="./_nuxt/CaGtQlYh.js"><link rel="modulepreload" as="script" crossorigin href="./_nuxt/QTnfLwEv.js"><script type="module" src="./_nuxt/Bmqvg1eR.js" crossorigin></script><meta name="theme-color" content="#000000"><link rel="icon" type="image/svg+xml" href="favicon.svg"><link rel="apple-touch-icon" href="pwa-192x192.png"><link rel="manifest" href="manifest.webmanifest"><script id="unhead:payload" type="application/json">{"title":"MetaCubeXD"}</script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{appVersion:"1.251.3",mockMode:false,defaultBackendURL:"",i18n:{baseUrl:"",defaultLocale:"en",rootRedirect:"",redirectStatusCode:302,skipSettingLocaleOnNavigate:false,locales:[{code:"en",name:"English",language:""},{code:"zh",name:"简体中文",language:""},{code:"ru",name:"Русский",language:""}],detectBrowserLanguage:{alwaysRedirect:false,cookieCrossOrigin:false,cookieDomain:"",cookieKey:"metacubexd_lang",cookieSecure:false,fallbackLocale:"en",redirectOn:"root",useCookie:true},experimental:{localeDetector:"",typedPages:true,typedOptionsAndMessages:false,alternateLinkCanonicalQueries:true,devCache:false,cacheLifetime:"",stripMessagesPayload:false,preload:false,strictSeo:false,nitroContextDetection:true,httpCacheDuration:10,compactRoutes:false,prerenderMessages:false},domainLocales:{en:{domain:""},zh:{domain:""},ru:{domain:""}}}},app:{baseURL:"./",buildId:"cdc84ee9-d7c0-44a7-96d3-b0595bcf2138",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1780682638349,false]</script></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"><script type="importmap">{"imports":{"#entry":"./_nuxt/Bmqvg1eR.js"}}</script><title>MetaCubeXD</title><script src="config.js" onerror="window.__METACUBEXD_CONFIG__={defaultBackendURL:''}"></script><script>window.__METACUBEXD_CONFIG__ = window.__METACUBEXD_CONFIG__ || { defaultBackendURL: '' }</script><link rel="stylesheet" href="./_nuxt/entry.B_k74qUh.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="./_nuxt/Bmqvg1eR.js"><link rel="modulepreload" as="script" crossorigin href="./_nuxt/CaGtQlYh.js"><link rel="modulepreload" as="script" crossorigin href="./_nuxt/QTnfLwEv.js"><script type="module" src="./_nuxt/Bmqvg1eR.js" crossorigin></script><meta name="theme-color" content="#000000"><link rel="icon" type="image/svg+xml" href="favicon.svg"><link rel="apple-touch-icon" href="pwa-192x192.png"><link rel="manifest" href="manifest.webmanifest"><script id="unhead:payload" type="application/json">{"title":"MetaCubeXD"}</script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{appVersion:"1.251.3",mockMode:false,defaultBackendURL:"",i18n:{baseUrl:"",defaultLocale:"en",rootRedirect:"",redirectStatusCode:302,skipSettingLocaleOnNavigate:false,locales:[{code:"en",name:"English",language:""},{code:"zh",name:"简体中文",language:""},{code:"ru",name:"Русский",language:""}],detectBrowserLanguage:{alwaysRedirect:false,cookieCrossOrigin:false,cookieDomain:"",cookieKey:"metacubexd_lang",cookieSecure:false,fallbackLocale:"en",redirectOn:"root",useCookie:true},experimental:{localeDetector:"",typedPages:true,typedOptionsAndMessages:false,alternateLinkCanonicalQueries:true,devCache:false,cacheLifetime:"",stripMessagesPayload:false,preload:false,strictSeo:false,nitroContextDetection:true,httpCacheDuration:10,compactRoutes:false,prerenderMessages:false},domainLocales:{en:{domain:""},zh:{domain:""},ru:{domain:""}}}},app:{baseURL:"./",buildId:"cdc84ee9-d7c0-44a7-96d3-b0595bcf2138",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1780682638350,false]</script></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
d.metacubex.one
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user