Ansiblify my VPS setup

This commit is contained in:
Sam A. 2023-04-20 23:34:25 +02:00
parent c9fe87cd0e
commit 9c9ef9f4c7
Signed by: samsapti
GPG key ID: CBBBE7371E81C4EA
32 changed files with 1208 additions and 0 deletions

119
.ansible-lint Normal file
View file

@ -0,0 +1,119 @@
---
# .ansible-lint
profile: null # min, basic, moderate,safety, shared, production
# exclude_paths included in this file are parsed relative to this file's location
# and not relative to the CWD of execution. CLI arguments passed to the --exclude
# option are parsed relative to the CWD of execution.
exclude_paths:
- .cache/ # implicit unless exclude_paths is defined in config
- .github/
- test/fixtures/formatting-before/
- test/fixtures/formatting-prettier/
# parseable: true
# quiet: true
# strict: true
# verbosity: 1
# Mock modules or roles in order to pass ansible-playbook --syntax-check
mock_modules:
- zuul_return
# note the foo.bar is invalid as being neither a module or a collection
- fake_namespace.fake_collection.fake_module
- fake_namespace.fake_collection.fake_module.fake_submodule
mock_roles:
- mocked_role
- author.role_name # old standalone galaxy role
- fake_namespace.fake_collection.fake_role # role within a collection
# Enable checking of loop variable prefixes in roles
# loop_var_prefix: "{role}_"
# Enforce variable names to follow pattern below, in addition to Ansible own
# requirements, like avoiding python identifiers. To disable add `var-naming`
# to skip_list.
# var_naming_pattern: "^[a-z_][a-z0-9_]*$"
use_default_rules: true
# Load custom rules from this specific folder
# rulesdir:
# - ./rule/directory/
# Ansible-lint completely ignores rules or tags listed below
# skip_list:
# - skip_this_tag
# Ansible-lint does not automatically load rules that have the 'opt-in' tag.
# You must enable opt-in rules by listing each rule 'id' below.
enable_list:
- empty-string-compare # opt-in
- no-log-password # opt-in
- no-same-owner # opt-in
# add yaml here if you want to avoid ignoring yaml checks when yamllint
# library is missing. Normally its absence just skips using that rule.
- yaml
# Report only a subset of tags and fully ignore any others
# tags:
# - jinja[spacing]
# Ansible-lint does not fail on warnings from the rules or tags listed below
warn_list:
- skip_this_tag
- experimental # experimental is included in the implicit list
- no-changed-when
- latest[git]
# - role-name
# - yaml[document-start] # you can also use sub-rule matches
# skip_list:
# - fqcn[action-core]
# - fqcn[action]
# Some rules can transform files to fix (or make it easier to fix) identified
# errors. `ansible-lint --write` will reformat YAML files and run these transforms.
# By default it will run all transforms (effectively `write_list: ["all"]`).
# You can disable running transforms by setting `write_list: ["none"]`.
# Or only enable a subset of rule transforms by listing rules/tags here.
# write_list:
# - all
# Offline mode disables installation of requirements.yml
offline: false
# Return success if number of violations compared with previous git
# commit has not increased. This feature works only in git
# repositories.
progressive: false
# Define required Ansible's variables to satisfy syntax check
extra_vars:
foo: bar
multiline_string_variable: |
line1
line2
complex_variable: ":{;\t$()"
# Uncomment to enforce action validation with tasks, usually is not
# needed as Ansible syntax check also covers it.
# skip_action_validation: false
# List of additional kind:pattern to be added at the top of the default
# match list, first match determines the file kind.
kinds:
# - playbook: "**/examples/*.{yml,yaml}"
# - galaxy: "**/folder/galaxy.yml"
# - tasks: "**/tasks/*.yml"
# - vars: "**/vars/*.yml"
# - meta: "**/meta/main.yml"
- yaml: "**/*.yaml-too"
# List of additional collections to allow in only-builtins rule.
# only_builtins_allow_collections:
# - example_ns.example_collection
# List of additions modules to allow in only-builtins rule.
# only_builtins_allow_modules:
# - example_module
# vim:ft=yaml

5
ansible.cfg Normal file
View file

@ -0,0 +1,5 @@
[defaults]
remote_user = sysadm
remote_tmp = /tmp/.ansible
inventory = inventory
use_persistent_connections = True

View file

@ -0,0 +1,47 @@
$ANSIBLE_VAULT;1.1;AES256
30376133336133396666636131316435356262663762386235616334333965633165326338363065
6334346230333564663066353962643833613634663833630a383831393035343730653564373266
63363266653965626363656139316632326233623038333139343838326366663333623464666663
6131363263653164320a663930323534613661623965656164613431626266363834646665326434
66633538623733386335376432326462666265353864373734323833396132663539636363613765
32303535663436626464336636313964633764303163666135303333616261316438326462666261
31643736376561313865393838636433376232653666366631363866326432303231373838393262
61376137393861346231313735626235366337306633653961613330323364333164633835333639
39653161636132663661613838343861303364666537383237306632636131316334666263613934
64373630396232613436643235626262656630343130363866313334633466316266356231373035
36636161633233313661303162333165306331313633666131643333373765336637643562373833
34626261353161656135653033623036633231656362653537323361323763333937353631373032
66613939643161666535356139353139303734393237333135623166323331663137343737616630
65643962356466616261383638616337383932653066303836643738646237666138643566333263
62653335643436333161643562613837343166393732373262323362346162353164353230663730
30303730306235333039623839366466663565386266386262326333643536643334613665633266
63656666633137386235626531396535623030333834623337343862653432373035376630633264
39323263353864376537666565636631396563626266396131383265653262343664636463396565
31343437313731613130623131636234343263323836316232316362396636313931326531383266
36323133646365636635363264646232363233343366636563353163326361336531376438303163
37383234323765633039393164623035363935383333383537396639626636633262653561633734
64653766336439343963646264616362396264393765656632363233376437373864323837323466
65326532623033346331643130376632643538643939313961326465623636343332396533326565
31336665613431366439663765326239336365316539303164633333613965643162643362666363
65353339663436393462646338646535613166303833346330613265316266343935333339323865
37356164623632376532623635616239313735663337613139633332346634386535353036656136
38313262653366363933383632623831636566363765663737653637316661343061646238613062
36646630393164323363373635343762393334626232363864373266653031346435663033616564
66313534633236306533373832343136393334303364396130653763353530323936663263636239
34623664643061353362346535373965626133613737303632656336633639373338353837643233
36366233623837383461336365343136376134333433373037656533313431336136333234373134
33356362633665643337353339643632633439616663313564616165333761316262353933303235
37393738386165333362326535626336393339353863663831663363366137643638643938636362
31623165323561376665393965613332616365393732333365376566316364653837656238383565
61666238626564303239383036373437613630663366383931616661303338336363393264323566
61396639383735326532343139316134616636663939626463376639336434343238653333336337
32643331616537663362383932343735623861623534346235623337393535343761316463373234
39356363343730633437363131623538346534653839313031363936363131653937346131396136
30326331326261646664303530346133323134613466323536323935646164353162663836643166
31393761333838363834663832636335356138663064313431333836613735343064333262306239
62333365356134346433633335396565323136303435656132396434653937303032383938643466
62353563323036346431643663623938333333353665646361313237643861303562663062353362
61303539306164333935626561393963633631306265313665373262303636383664363034383639
64313832623038653266353432353662326433363033373466356461323330623139393632653933
36383865343933393531306638376635666337653234363437313962373938333139383637323837
6364356630343237313161353266323864616365336631353062

23
group_vars/all/vars.yml Normal file
View file

@ -0,0 +1,23 @@
# vim: ft=yaml.ansible
---
hostname: cpx
timezone: Europe/Berlin
users:
- name: sysadm
comment: System Administration
password: $6$Q2WHaFPAPgndQSz4$F6py94HemVpE/cgtTU0cRKkI/Si5Vjl/aSCniVr8WtbkViBOtt.dpDDWwhjbOOX/o6R22w1tFiciBmF.UA9HH0
groups:
- sudo
ssh_keys:
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIFWZGLov8wPBNxuvnaPK+8vv6wK5hHUVEFzXKsN9QeuBAAAADHNzaDpzYW1zYXB0aQ== ssh:samsapti
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPd/4fQV7CL8/KVwbo/phiV5UdXFBIDlkZ+ps8C7FeRf cardno:14 336 332
- name: cicd
comment: CI/CD user
password: '!'
groups: []
ssh_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBN7XhL364LZ4rnEnmpV59Qehw3ldqEb7XX5DWTAb/XH cicd
- sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIFWZGLov8wPBNxuvnaPK+8vv6wK5hHUVEFzXKsN9QeuBAAAADHNzaDpzYW1zYXB0aQ== ssh:samsapti
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPd/4fQV7CL8/KVwbo/phiV5UdXFBIDlkZ+ps8C7FeRf cardno:14 336 332

2
inventory Normal file
View file

@ -0,0 +1,2 @@
# CPX21
cpx.servers.sapti.me ansible_python_interface=/usr/bin/python3

13
playbook.yml Normal file
View file

@ -0,0 +1,13 @@
# vim: ft=yaml.ansible
---
- name: Deploy self-hosted services
hosts: all
gather_facts: true
become: true
roles:
- role: os_config
tags:
- os
- role: docker_services
tags:
- docker

57
provision.sh Executable file
View file

@ -0,0 +1,57 @@
#!/bin/sh
set -e
usage() {
printf '%s\n' "Usage:"
printf '$ %s\n' "$0 [-h|--help]"
printf '$ %s\n' "$0 os"
printf '$ %s\n' "$0 docker"
printf '$ %s\n' "$0 users [-i|--init]"
printf '$ %s\n' "$0 reboot [-f|--force]"
printf '$ %s\n' "$0 services [SINGLE_SERVICE]"
}
install_modules() {
if [ -z "$(ansible-galaxy collection list community.general 2>/dev/null)" ]; then
ansible-galaxy collection install community.general
fi
}
cd "$(dirname "$0")" || exit 255
BASE_CMD="ansible-playbook playbook.yml --ask-vault-pass --ask-become-pass"
case $1 in
"")
install_modules; $BASE_CMD ;;
os|docker)
install_modules; $BASE_CMD --tags "$1" ;;
users)
install_modules
if [ "$2" = "-i" ] || [ "$2" = "--init" ]; then
$BASE_CMD --user root --tags "$1"
else
$BASE_CMD --tags "$1"
fi
;;
reboot)
install_modules
if [ "$2" = "-f" ] || [ "$2" = "--force" ]; then
$BASE_CMD --tags "$1" --extra-vars "force_reboot=true"
else
$BASE_CMD --tags "$1" --extra-vars "reboot=true"
fi
;;
services)
install_modules
if [ -z "$2" ]; then
$BASE_CMD --tags "$1"
else
$BASE_CMD --tags "$1" --extra-vars "single_service=$2"
fi
;;
-h|--help)
usage ;;
*)
usage >&2; exit 1 ;;
esac

View file

@ -0,0 +1,44 @@
# vim: ft=yaml.ansible
---
base_domain: sapti.me
base_volume: /opt/docker
services:
caddy:
volume: "{{ base_volume }}/caddy"
version: 2-alpine
jitsi_meet:
domain: meet.{{ base_domain }}
volume: "{{ base_volume }}/jitsi-meet"
version: stable
lingva:
domain: translate.{{ base_domain }}
onion: 22qfd63ax4zt5arctpfh62kvjekap7yrdfzwq5kv5jvhew5hcpq6vgyd.onion
version: latest
samsapti_dev:
domain: samsapti.dev
onion: mldhltdackluvnqso7vk2azcg3ghjxbpw4im6alubymqkonb4kppqcqd.onion
volume: "{{ base_volume }}/samsapti.dev"
version: 1-alpine
searxng:
domain: search.{{ base_domain }}
onion: gbat2pbpg7ys3fi3pbp64667tt5x66mg45xok35bxdw7v55brm7a27yd.onion
volume: "{{ base_volume }}/searxng"
version: latest
redis_version: 7-alpine
wkd:
domain: "{{ base_domain }}"
volume: "{{ base_volume }}/wkd"
version: 1-alpine
tor:
volume: "{{ base_volume }}/tor"
version: latest
watchtower:
version: amd64-1.5.3

View file

@ -0,0 +1,8 @@
{
"default-address-pools": [
{
"base": "172.17.0.0/16",
"size": 24
}
]
}

View file

@ -0,0 +1,52 @@
user nginx;
worker_processes auto;
error_log /dev/null crit;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log off;
sendfile on;
tcp_nopush on;
gzip on;
server_tokens off;
include /etc/nginx/conf.d/*.conf;
keepalive_timeout 65;
### HTTP headers
# TCP Keep-Alive
add_header Connection "Keep-Alive" always;
# CSP
add_header Content-Security-Policy "default-src 'none'; child-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self'; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self'; img-src 'self'; manifest-src 'self'" always;
# Enable cross-site filter (XSS) and tell browser to block detected attacks
add_header X-XSS-Protection "1; mode=block" always;
# Prevent some browsers from MIME-sniffing a response away from the declared Content-Type
add_header X-Content-Type-Options "nosniff" always;
# Disallow the site to be rendered within a frame (clickjacking protection)
add_header X-Frame-Options "SAMEORIGIN" always;
# Disable some features
add_header Permissions-Policy "accelerometer=(),ambient-light-sensor=(),autoplay=(),camera=(),encrypted-media=(),focus-without-user-activation=(),geolocation=(),gyroscope=(),magnetometer=(),microphone=(),midi=(),payment=(),picture-in-picture=(),speaker=(),sync-xhr=(),usb=(),vr=(),interest-cohort=()" always;
# Disable referrer
add_header Referrer-Policy "no-referrer" always;
# Cache control policy
add_header Cache-Control "max-age=15552000" always;
}

View file

@ -0,0 +1,45 @@
# vim: ft=yaml.ansible
---
- name: Create base directory for Docker volumes
ansible.builtin.file:
path: "{{ base_volume }}"
owner: root
mode: u=rwx,g=rx,o=rx
state: directory
- name: Copy Docker daemon config file
ansible.builtin.copy:
src: dockerd/daemon.json
dest: /etc/docker/daemon.json
owner: root
mode: u=rw,g=r,o=r
register: daemon_config
- name: (Re)start Docker daemon
ansible.builtin.service:
name: "{{ item }}"
enabled: true
state: "{{ 'restarted' if daemon_config.changed else 'started' }}"
loop:
- docker.socket
- docker.service
- name: Create Docker network for services
community.docker.docker_network:
name: services
ipam_config:
- subnet: 172.16.0.0/16
gateway: 172.16.0.1
state: present
- name: Create Docker network for Tor
community.docker.docker_network:
name: tor
state: present
- name: Copy nginx.conf to shared location
ansible.builtin.copy:
src: shared/nginx.conf
dest: "{{ base_volume }}/nginx.conf"
owner: root
mode: u=rw,g=r,o=r

View file

@ -0,0 +1,50 @@
# vim: ft=yaml.ansible
---
- name: Add Docker PGP key
ansible.builtin.apt_key:
keyserver: keyserver.ubuntu.com
id: '0x8D81803C0EBFCD88'
state: present
- name: Add Docker apt repository
ansible.builtin.apt_repository:
repo: deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian bullseye stable
state: present
update_cache: true
- name: Install Docker
ansible.builtin.apt:
name: "{{ pkgs }}"
state: present
vars:
pkgs:
- docker-ce
- docker-compose-plugin
- name: Create docker-compose symlink
ansible.builtin.file:
path: /usr/local/bin/docker-compose
src: /usr/libexec/docker/cli-plugins/docker-compose
state: link
- name: Install Python bindings for Docker
ansible.builtin.pip:
name: "{{ pkgs }}"
state: present
executable: pip3
vars:
pkgs:
- docker
- docker-compose
- name: Configure Docker
ansible.builtin.import_tasks: config.yml
tags:
- services
- reboot
- name: Set up Docker services
ansible.builtin.import_tasks: services.yml
tags:
- services
- reboot

View file

@ -0,0 +1,11 @@
# vim: ft=yaml.ansible
---
- name: Deploy services
ansible.builtin.include_tasks: services/{{ item.key }}.yml
loop: "{{ services | dict2items }}"
when: single_service is not defined
- name: Deploy single service
ansible.builtin.include_tasks: services/{{ single_service }}.yml
when: single_service is defined and
single_service in services

View file

@ -0,0 +1,43 @@
# vim: ft=yaml.ansible
---
- name: Create Caddy volume directories
ansible.builtin.file:
path: "{{ services.caddy.volume }}/{{ dir }}"
owner: root
mode: u=rwx,g=rx,o=rx
state: directory
loop:
- config
- data
loop_control:
loop_var: dir
- name: Copy Caddyfile
ansible.builtin.template:
src: Caddyfile.j2
dest: "{{ services.caddy.volume }}/Caddyfile"
owner: root
mode: u=rw,g=r,o=r
- name: Deploy Caddy Docker container
community.docker.docker_container:
name: caddy
state: "{{ 'absent' if stop is defined and stop else 'started' }}"
restart: "{{ stop is undefined or not stop }}"
image: caddy:{{ services.caddy.version }}
restart_policy: always
networks:
- name: services
ipv4_address: 172.16.3.2
published_ports:
- 80:80/tcp
- 443:443/tcp
volumes:
- "{{ services.caddy.volume }}/Caddyfile:/etc/caddy/Caddyfile:ro"
- "{{ services.caddy.volume }}/config:/config:rw"
- "{{ services.caddy.volume }}/data:/data:rw"
capabilities:
- net_bind_service
- dac_override
cap_drop:
- all

View file

@ -0,0 +1,82 @@
# vim: ft=yaml.ansible
---
- name: Create Jitsi Meet volume directories
ansible.builtin.file:
path: "{{ services.jitsi_meet.volume }}/{{ dir }}"
owner: root
mode: u=rwx,g=rx,o=rx
state: directory
loop:
- prosody/plugins
- transcripts
loop_control:
loop_var: dir
- name: Deploy Jitsi Meet with Docker Compose
community.docker.docker_compose:
project_name: searxng
state: "{{ 'absent' if stop is defined and stop else 'present' }}"
restarted: "{{ stop is undefined or not stop }}"
pull: true
definition:
version: '3.8'
services:
frontend:
image: jitsi/web:{{ services.jitsi_meet.version }}
restart: always
environment:
DISABLE_HTTPS: 1
PUBLIC_URL: "{{ services.jitsi_meet.domain }}"
networks:
meet.jitsi:
services:
aliases:
- jitsi_meet
volumes:
- "{{ services.jitsi_meet.volume }}/transcripts:/usr/share/jitsi-meet/transcripts:rw"
prosody:
image: jitsi/prosody:{{ services.jitsi_meet.version }}
restart: always
environment:
JICOFO_AUTH_PASSWORD: "{{ secrets.jitsi_meet.jicofo_auth_password }}"
JVB_AUTH_PASSWORD: "{{ secrets.jitsi_meet.jvb_auth_password }}"
networks:
meet.jitsi:
aliases:
- xmpp.meet.jitsi
volumes:
- "{{ services.jitsi_meet.volume }}/prosody/plugins:/prosody-plugins-custom:rw"
expose:
- 5222
- 5280
- 5347
jicofo:
image: jitsi/jicofo:{{ services.jitsi_meet.version }}
restart: always
environment:
JICOFO_AUTH_PASSWORD: "{{ secrets.jitsi_meet.jicofo_auth_password }}"
networks:
meet.jitsi:
depends_on:
- prosody
jvb:
image: jitsi/jvb:{{ services.jitsi_meet.version }}
restart: always
environment:
JVB_AUTH_PASSWORD: "{{ secrets.jitsi_meet.jvb_auth_password }}"
JVB_WS_DOMAIN: "{{ services.jitsi_meet.domain }}"
networks:
meet.jitsi:
ports:
- 10000:10000/udp
depends_on:
- prosody
networks:
meet.jitsi:
services:
external: true

View file

@ -0,0 +1,18 @@
# vim: ft=yaml.ansible
---
- name: Deploy Lingva Docker container
community.docker.docker_container:
name: lingva
state: "{{ 'absent' if stop is defined and stop else 'started' }}"
restart: "{{ stop is undefined or not stop }}"
image: thedaviddelta/lingva-translate:{{ services.lingva.version }}
restart_policy: always
env:
site_domain: "{{ services.lingva.domain }}"
networks:
- name: services
aliases:
- lingva
- name: tor
aliases:
- lingva

View file

@ -0,0 +1,26 @@
# vim: ft=yaml.ansible
---
- name: Create samsapti.dev volume directories
ansible.builtin.file:
path: "{{ services.samsapti_dev.volume }}/public"
owner: cicd
mode: u=rwx,g=rx,o=rx
state: directory
- name: Deploy samsapti.dev Docker container
community.docker.docker_container:
name: samsapti.dev
state: "{{ 'absent' if stop is defined and stop else 'started' }}"
restart: "{{ stop is undefined or not stop }}"
image: nginx:{{ services.samsapti_dev.version }}
restart_policy: always
networks:
- name: services
aliases:
- samsapti
- name: tor
aliases:
- samsapti
volumes:
- "{{ services.samsapti_dev.volume }}/public:/usr/share/nginx/html:ro"
- "{{ base_volume }}/nginx.conf:/etc/nginx/nginx.conf:ro"

View file

@ -0,0 +1,72 @@
# vim: ft=yaml.ansible
---
- name: Create SearXNG volume directory
ansible.builtin.file:
path: "{{ services.searxng.volume }}"
owner: root
mode: u=rwx,g=rx,o=rx
state: directory
- name: Copy SearXNG settings.yml file
ansible.builtin.template:
src: settings.yml.j2
dest: "{{ services.searxng.volume }}/settings.yml"
owner: root
mode: u=rw,g=r,o=r
- name: Deploy SearXNG with Docker Compose
community.docker.docker_compose:
project_name: searxng
state: "{{ 'absent' if stop is defined and stop else 'present' }}"
restarted: "{{ stop is undefined or not stop }}"
pull: true
definition:
version: '3.8'
services:
searxng:
image: searxng/searxng:{{ services.searxng.version }}
restart: always
environment:
SEARXNG_BASE_URL: https://{{ services.searxng.domain }}
networks:
default:
services:
aliases:
- searxng
tor:
aliases:
- searxng
volumes:
- "{{ services.searxng.volume }}:/etc/searxng:rw"
cap_add:
- chown
- setuid
- setgid
- dac_override
cap_drop:
- all
logging:
driver: json-file
options:
max-size: 1m
max-file: '1'
redis:
image: redis:{{ services.searxng.redis_version }}
restart: always
command: redis-server --save "" --appendonly "no"
tmpfs:
- /var/lib/redis
cap_add:
- setuid
- setgid
- dac_override
cap_drop:
- all
networks:
services:
external: true
tor:
external: true

View file

@ -0,0 +1,33 @@
# vim: ft=yaml.ansible
---
- name: Create Tor volume directory
ansible.builtin.file:
path: "{{ services.tor.volume }}"
owner: '104'
group: '107'
mode: u=rwx,go=
state: directory
- name: Deploy Tor Docker container
community.docker.docker_container:
name: tor
state: "{{ 'absent' if stop is defined and stop else 'started' }}"
restart: "{{ stop is undefined or not stop }}"
image: goldy/tor-hidden-service:{{ services.tor.version }}
restart_policy: always
env:
LINGVA_TOR_SERVICE_HOSTS: 80:lingva:3000
LINGVA_TOR_SERVICE_VERSION: '3'
LINGVA_TOR_SERVICE_KEY: "{{ secrets.tor.lingva_key }}"
SAMSAPTI_TOR_SERVICE_HOSTS: 80:samsapti:80
SAMSAPTI_TOR_SERVICE_VERSION: '3'
SAMSAPTI_TOR_SERVICE_KEY: "{{ secrets.tor.samsapti_key }}"
SEARXNG_TOR_SERVICE_HOSTS: 80:searxng:8080
SEARXNG_TOR_SERVICE_VERSION: '3'
SEARXNG_TOR_SERVICE_KEY: "{{ secrets.tor.searxng_key }}"
networks:
- name: tor
volumes:
- "{{ services.tor.volume }}:/var/lib/tor/hidden_service:rw"

View file

@ -0,0 +1,20 @@
# vim: ft=yaml.ansible
---
- name: Create Docker network for Watchtower
community.docker.docker_network:
name: watchtower
state: present
- name: Deploy Watchtower Docker container
community.docker.docker_container:
name: watchtower
state: "{{ 'absent' if stop is defined and stop else 'started' }}"
restart: "{{ stop is undefined or not stop }}"
image: containrrr/watchtower:{{ services.watchtower.version }}
restart_policy: always
networks:
- name: watchtower
env:
WATCHTOWER_POLL_INTERVAL: '3600'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rw

View file

@ -0,0 +1,30 @@
# vim: ft=yaml.ansible
---
- name: Create WKD volume directories
ansible.builtin.file:
path: "{{ services.wkd.volume }}/.well-known/openpgpkey/hu"
owner: root
mode: u=rwx,g=rx,o=rx
state: directory
- name: Copy PGP key to WKD volume directory
ansible.builtin.copy:
src: wkd/6fi64ioaua1j93gkt5eow8skha8e34sy
dest: "{{ services.wkd.volume }}/.well-known/openpgpkey/hu/6fi64ioaua1j93gkt5eow8skha8e34sy"
owner: root
mode: u=rw,g=r,o=r
- name: Deploy WKD Docker container
community.docker.docker_container:
name: wkd
state: "{{ 'absent' if stop is defined and stop else 'started' }}"
restart: "{{ stop is undefined or not stop }}"
image: nginx:{{ services.wkd.version }}
restart_policy: always
networks:
- name: services
aliases:
- wkd
volumes:
- "{{ services.wkd.volume }}:/usr/share/nginx/html:ro"
- "{{ base_volume }}/nginx.conf:/etc/nginx/nginx.conf:ro"

View file

@ -0,0 +1,154 @@
{
admin off
}
www.{{ services.samsapti_dev.domain }} {
tls {{ secrets.tls_email }}
log {
output discard
}
redir https://{{ services.samsapti_dev.domain }}{uri}
}
{{ services.samsapti_dev.domain }} {
tls {{ secrets.tls_email }}
log {
output discard
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Onion-Location "http://{{ services.samsapti_dev.onion }}{uri}"
-Server
}
reverse_proxy samsapti:80
}
{{ services.wkd.domain }} {
tls {{ secrets.tls_email }}
log {
output discard
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
-Server
}
reverse_proxy wkd:80
}
{{ services.jitsi_meet.domain }} {
tls {{ secrets.tls_email }}
log {
output discard
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
-Server
}
reverse_proxy jitsi_meet:80
}
{{ services.lingva.domain }} {
tls {{ secrets.tls_email }}
log {
output discard
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Onion-Location "http://{{ services.lingva.onion }}{uri]"
-Server
}
reverse_proxy lingva:3000
}
{{ services.searxng.domain }} {
tls {{ secrets.tls_email }}
log {
output discard
}
@api {
path /config
path /healthz
path /stats/errors
path /stats/checker
}
@static {
path /static/*
}
@notstatic {
not path /static/*
}
@imageproxy {
path /image_proxy
}
@notimageproxy {
not path /image_proxy
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-XSS-Protection "1; mode=block"
X-Content-Type-Options "nosniff"
Permissions-Policy "accelerometer=(),ambient-light-sensor=(),autoplay=(),camera=(),encrypted-media=(),focus-without-user-activation=(),geolocation=(),gyroscope=(),magnetometer=(),microphone=(),midi=(),payment=(),picture-in-picture=(),speaker=(),sync-xhr=(),usb=(),vr=(),interest-cohort=()"
Referrer-Policy "no-referrer"
X-Robots-Tag "noindex, noarchive, nofollow"
Onion-Location "http://{{ services.searxng.onion }}{uri}"
-Server
}
header @api {
Access-Control-Allow-Methods "GET, OPTIONS"
Access-Control-Allow-Origin "*"
}
# Cache
header @static {
# Cache
Cache-Control "public, max-age=31536000"
defer
}
header @notstatic {
# No Cache
Cache-Control "no-cache, no-store"
Pragma "no-cache"
}
# CSP (see http://content-security-policy.com/ )
header @imageproxy {
Content-Security-Policy "default-src 'none'; img-src 'self' data:"
}
header @notimageproxy {
Content-Security-Policy "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self' https://github.com/searxng/searxng/issues/new; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self' https://overpass-api.de; img-src 'self' data: https://*.tile.openstreetmap.org; frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://www.dailymotion.com https://www.deezer.com https://www.mixcloud.com https://w.soundcloud.com https://embed.spotify.com"
}
# SearXNG
handle {
encode zstd gzip
reverse_proxy searxng:8080 {
header_up X-Forwarded-Port {http.request.port}
header_up X-Forwarded-Proto {http.request.scheme}
}
}
}

View file

@ -0,0 +1,85 @@
use_default_settings: true
general:
debug: false
instance_name: "Sam's SearXNG"
privacypolicy_url: "https://{{ services.samsapti_dev.domain }}/privacy/"
contact_url: "https://{{ services.samsapti_dev.domain }}/contact/"
enable_metrics: true
server:
secret_key: "{{ secrets.searxng.secret_key }}"
limiter: true
image_proxy: true
http_protocol_version: "1.1"
method: "GET"
ui:
results_on_new_tab: false
center_alignment: true
theme_args:
simple_style: auto
redis:
url: redis://redis:6379/0
search:
formats:
- html
- csv
- json
- rss
safe_search: 2
outgoing:
enable_http2: true
enabled_plugins:
- 'Hash plugin'
- 'Hostname replace'
- 'Search on category select'
- 'Self Informations'
- 'Tracker URL remover'
- 'Vim-like hotkeys'
hostname_replace:
'^(.*\.)?youtube\.com$': 'yewtu.be'
'^(.*\.)?youtu\.be$': 'yewtu.be'
'^(.*\.)?youtube-noocookie\.com$': 'yewtu.be'
'^(.*\.)?reddit\.com$': 'libreddit.de'
'^(.*\.)?redd\.it$': 'libreddit.de'
'^(www\.)?twitter\.com$': 'nitter.net'
'^(.*\.)?(m\.)?wiktionary\.org$': '\1m.wiktionary.org'
engines:
- name: bing
disabled: false
- name: yahoo
disabled: false
- name: ddg definitions
disabled: false
- name: duckduckgo images
disabled: false
- name: google
use_mobile_ui: true
disabled: false
- name: wikidata
disabled: true
- name: wikinews
disabled: true
- name: qwant
disabled: true
- name: qwant images
disabled: true
- name: lingva
url: https://translate.sapti.me
disabled: true

View file

@ -0,0 +1,19 @@
# vim: ft=yaml.ansible
---
- name: Create .env for apt-update-push
ansible.builtin.template:
src: env.j2
dest: /home/{{ ansible_user }}/apt-update-push/.env
owner: root
mode: u=rw,go=
listen: apt-update-push
- name: Install apt-update-push
ansible.builtin.command: /home/{{ ansible_user }}/apt-update-push/install.sh
listen: apt-update-push
- name: Restart sshd
ansible.builtin.service:
name: sshd
state: restarted
listen: sshd

View file

@ -0,0 +1,40 @@
# vim: ft=yaml.ansible
---
- name: Set hostname
ansible.builtin.hostname:
name: "{{ hostname }}"
- name: Set timezone
community.general.timezone:
name: "{{ timezone }}"
- name: Upgrade system packages
ansible.builtin.apt:
update_cache: true
upgrade: full
- name: Install packages via apt
ansible.builtin.apt:
name: "{{ pkgs }}"
state: present
vars:
pkgs:
- apparmor
- curl
- git
- haveged
- needrestart
- python3-pip
- ufw
- unattended-upgrades
- name: Clone apt-update-push
ansible.builtin.git:
dest: /home/{{ ansible_user }}/apt-update-push
repo: https://github.com/samsapti/apt-update-push.git
clone: true
update: true
single_branch: true
depth: 1
become: false
notify: apt-update-push

View file

@ -0,0 +1,18 @@
# vim: ft=yaml.ansible
---
- name: Allow necessary ports in UFW
community.general.ufw:
rule: allow
port: "{{ item.port }}"
proto: "{{ item.proto | default('tcp') }}"
loop:
- port: '22' # SSH
- port: '80' # HTTP
- port: '443' # HTTPS
- port: '10000' # Jitsi Videobridge
proto: udp
- name: Enable UFW
community.general.ufw:
state: enabled
policy: deny

View file

@ -0,0 +1,20 @@
# vim: ft=yaml.ansible
---
- name: Configure user accounts
ansible.builtin.import_tasks: users.yml
tags:
- users
- name: Configure system base
ansible.builtin.import_tasks: base.yml
- name: Reboot if needed
ansible.builtin.import_tasks: reboot.yml
tags:
- reboot
- name: Configure firewall
ansible.builtin.import_tasks: firewall.yml
- name: Configure SSH
ansible.builtin.import_tasks: ssh.yml

View file

@ -0,0 +1,28 @@
# vim: ft=yaml.ansible
---
- name: Check if a reboot is needed
ansible.builtin.stat:
path: /var/run/reboot-required
register: needs_reboot
- name: Include docker_services role for service shutdown
ansible.builtin.include_role:
name: docker_services
tasks_from: services.yml
apply:
ignore_errors: true
vars:
stop: true
when: needs_reboot.stat.exists or
(force_reboot is defined and force_reboot)
- name: Reboot host
ansible.builtin.reboot:
when: needs_reboot.stat.exists or
(force_reboot is defined and force_reboot)
register: rebooted
- name: End play if reboot is not needed
ansible.builtin.meta: end_play
when: reboot is defined and reboot and
(rebooted.rebooted is undefined or not rebooted.rebooted)

View file

@ -0,0 +1,25 @@
# vim: ft=yaml.ansible
---
- name: Allow SSH login with public keys
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PubkeyAuthentication '
line: PubkeyAuthentication yes
state: present
notify: sshd
- name: Disallow SSH login with password
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication '
line: PasswordAuthentication no
state: present
notify: sshd
- name: Disallow root login over SSH
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PermitRootLogin '
line: PermitRootLogin no
state: present
notify: sshd

View file

@ -0,0 +1,17 @@
# vim: ft=yaml.ansible
---
- name: Add users
ansible.builtin.user:
name: "{{ item.name }}"
comment: "{{ item.comment }}"
password: "{{ item.password }}"
groups: "{{ item.groups }}"
update_password: always
loop: "{{ users }}"
- name: Add ssh authorized_keys
ansible.posix.authorized_key:
user: "{{ item.name }}"
key: "{{ item.ssh_keys | join('\n') }}"
exclusive: true
loop: "{{ users }}"

View file

@ -0,0 +1,2 @@
topic={{ secrets.ntfy_topic }}
hour=20