Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
oxodao committed Apr 7, 2024
0 parents commit 92746d4
Show file tree
Hide file tree
Showing 34 changed files with 1,848 additions and 0 deletions.
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Oxodao's Homelab

Documentation de mon homelab

## Architecture

Mon homelab est composé de trois machines:
- Un NAS Synology pour le stockage (Actuellement 2x2TB WDC WD20EFRX-68EUZN0)
- Un PC "multimédia" qui est utilisé sur la TV (Thinkcentre m75q / Athlon 300GE)
- Un PC serveur (i3-10100 / 24Gb DDR4 / 1to nvme)
- Un router TP Link Archer C6 (AC1200) sous OpenWRT (Pas encore mis en place)

Le serveur est un host XCP-NG (hostname `rubeus`) avec deux VM principales, une dédiée aux services accessible en publique, l'autre avec les services accessible uniquement sur le réseau interne.

Note: le serveur n'a pas de VM pour XenOrchestra, j'utilise sur mon PC une VM sur laquelle j'ai installé XOA via [XenOrchestraInstallerUpdater](https://github.com/ronivay/XenOrchestraInstallerUpdater), cela permet d'économiser de la RAM / du CPU utilisé pour autre chose.

Le NAS Synology possède toutes les data et expose plusieurs montage samba:
- sauvegarde (Mon espace de stockage générique ou je met un peu tout)
- iso (Mon SR d'iso pour xcp-ng)
- shares (Espace pour stocker mes films / séries / musiques / ...)
- documents (Montage utilisé pour Paperless)
- images (Montage utilisé pour Immich)

Pour la gestion des droits, j'ai mon utilisateur perso pour la connexion depuis mes machines, et des utilisateurs scopés en RO sur iso et shares pour les différents services ansi qu'un utilisateur RW pour Paperless, chacun n'ayant accès qu'aux shares qu'ils ont besoin.

Enfin le PC multimédia est un simple debian 12 qui accèdes aux services hébergés sur le serveur.

Ce guide note particulièrement le setup du serveur puisque le reste est basique et ne nécessite rien de spécial.

## Sommaire

1. [Setup XCP-NG](docs/setup_xcp.md)
2. [Setup basique des VMs](docs/setup_vm.md)

> A partir de ce point la, les divers ansible
> vont setup tout ça.
>
> Il faut tout de même suivre chaque page pour s'assurer
> la config des logiciels est complète car tout n'est
> pas fait dans ansible pour l'instant (e.g. config
> interne de Jellyfin, création de compte utilisateur, etc...)
3. [Setup des utilitaires communs](docs/setup_common.md)
4. [Setup serveur DNS](docs/setup_dns.md)
5. [Setup reverse-proxy](docs/setup_reverseproxy.md)

-- Setup des apps --

6. [Setup Jellyfin](docs/setup_jellyfin.md)
7. [Setup Navidrome](docs/setup_navidrome.md)
8. [Setup Paperless](docs/setup_paperless.md)
9. [Setup Gitea](docs/setup_gitea.md)
10. [Setup Immich](docs/setup_immich.md)
11. [Setup JDownloader](docs/setup_jdownloader.md)
12. [Setup HomeAssistant](docs/setup_ha.md)

-- Setup sécu --

13. [Setup serveurs VPN](docs/setup_vpn.md)
14. [Setup firewall](docs/setup_firewall.md)
15. [Setup cloudflared](docs/setup_cloudflared.md)
16. [Setup backups](docs/setup_backups.md)

> A partir de ce point la, il s'agît d'informations sur
> l'utilisation usuelle des VMs et du serveur ainsi que
> comment faire du disaster recovery.
17. [Ajouter un utilisateur sur le VPN](docs/add_user_vpn.md)
18. [Renouveller les certificats SSL](docs/renew_ssl.md)
19. [Restorer un backup](docs/disaster_recovery.md)
20. [Backup day](docs/backup_day.md)

## Roadmap

Chose que je vais potentiellement ajouter après que tout soit fonctionnel

- Authentik / Authelia: Active Directory / OAuth
- [Gitea](https://docs.gitea.com/usage/authentication): LDAP
- [Jellyfin](https://github.com/jellyfin/jellyfin-plugin-ldapauth): LDAP
- [Immich](https://www.reddit.com/r/selfhosted/comments/zrkokx/immich_and_ldap/): OAuth
- [Paperless](https://github.com/paperless-ngx/paperless-ngx/pull/100): Ils ont pas l'air de vouloir ni ldap ni oauth
- [Navidrome](https://github.com/navidrome/navidrome/pull/590): huuuh ça à pas l'air très fun non plus
- JDownloader: Accès externe donc via LEUR login, à voir pour dev un client self-hosted mais API non documentée
4 changes: 4 additions & 0 deletions ansible/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
inventory.yaml
files/*.key
files/*.crt
.envrc
29 changes: 29 additions & 0 deletions ansible/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Homelab ansible

Pour utiliser cet ansible, il faut copier-coller le `inventory.yaml.dist` en `inventory.yaml` et le compléter en suivant les commentaires.

Installation des roles:
```sh
$ ansible-galaxy install -r requirements.yaml --force
$ ansible-galaxy collection install -r requirements.yaml --force
```

Exécution du playbook:
```sh
$ export ANSIBLE_BECOME_PASSWORD="MON MOT DE PASSE SUDO" # A exécuter une seule fois
$ ansible-playbook -i inventory.yaml setup.yaml
```

Si on souhaite exécuter seulement certaines parties, on peut limiter avec les tags, ou les hosts:
```sh
$ export ANSIBLE_BECOME_PASSWORD="MON MOT DE PASSE SUDO" # A exécuter une seule fois
$ ansible-playbook -i inventory.yaml setup.yaml --limit public -t dns
```

Pour une réinstallation future, chiffrer le `inventory.yaml` et le stocker dans un endroit sécurisé. Il suffira alors de le reprendre et modifier en cas de mise à jour sur la version git.

Pour cela, on peut utiliser `ansible-vault` pour utiliser un utilitaire tout-en-un:
```sh
$ ansible-vault encrypt --vault-id homelab@prompt inventory.yaml # Chiffrer
$ ansible-vault decrypt inventory.yaml # Déchiffrer
```
167 changes: 167 additions & 0 deletions ansible/files/mattboll.zsh-theme
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#-------------------------------------------------------------------------------
# Sunrise theme for oh-my-zsh by Adam Lindberg ([email protected])
# Intended to be used with Solarized: http://ethanschoonover.com/solarized
# (Needs Git plugin for current_branch method)
#
# Probably modified by mattboll
# https://raw.githubusercontent.com/mattboll/zshrc/master/mattboll.zsh-theme
#-------------------------------------------------------------------------------

# Color shortcuts
R=$fg_no_bold[red]
G=$fg_no_bold[green]
M=$fg_no_bold[magenta]
Y=$fg_no_bold[yellow]
B=$fg_no_bold[blue]
RESET=$reset_color

if [ "$(whoami)" = "root" ]; then
PREPROMPT="%{$R%}ROOT";
else
if [[ -z "$SSH_CLIENT" ]]; then
PREPROMPT="";
else
PREPROMPT="$(whoami)";
fi
fi
if [[ -z "$SSH_CLIENT" ]]; then
prompt_host=""
else
prompt_host=%{$fg_bold[white]%}@%{$reset_color$fg[yellow]%}$(hostname -s)
fi

local return_code="%(?..%{$R%}%? ☢ %{$RESET%})"

git_remote_status() {
remote=${$(command git rev-parse --verify ${hook_com[branch]}@{upstream} --symbolic-full-name 2>/dev/null)/refs\/remotes\/}
if [[ -n ${remote} ]] ; then
ahead=$(command git rev-list ${hook_com[branch]}@{upstream}..HEAD 2>/dev/null | wc -l)
behind=$(command git rev-list HEAD..${hook_com[branch]}@{upstream} 2>/dev/null | wc -l)

if [ $ahead -eq 0 ] && [ $behind -gt 0 ]
then
echo "$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE"
elif [ $ahead -gt 0 ] && [ $behind -eq 0 ]
then
echo "$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE"
elif [ $ahead -gt 0 ] && [ $behind -gt 0 ]
then
echo "$ZSH_THEME_GIT_PROMPT_DIVERGED_REMOTE"
fi
fi
}

# Checks if there are commits ahead from remote
function git_prompt_ahead_number() {
nbcommits=${$(command git log origin/$(current_branch)..HEAD 2> /dev/null | grep '^commit' | wc -l )// /}
if [[ -n ${nbcommits} ]] ; then
if [ $nbcommits -gt 0 ]
then
echo %{$R%}\($nbcommits\)
else
echo %{$G%}\($nbcommits\)
fi
fi
}

# ZSH function that shortens
# a very long path for display by removing
# the left most parts and replacing them
# with a leading ...
#
# + keep some left part of the path if asked
# thanks to liquidprompt for that
_lp_shorten_path()
{
# the character that will replace the part of the path that is masked
local mask=""
# index of the directory to keep from the root (starts at 0 whith bash, 1 with zsh)
local keep=2

local p="${PWD/$HOME/~}"
local len="${#p}"

local max_len=$((${COLUMNS:-80}*25/100))

if [[ "$len" -gt "$max_len" ]]; then
echo "%-${keep}~%${max_len}<${mask}<%~%<<"
else
echo "%~"
fi
}


# Get the status of the working tree (copied and modified from git.zsh)
custom_git_prompt_status() {
INDEX=$(git status --porcelain 2> /dev/null)
STATUS=""
# Non-staged
if $(echo "$INDEX" | grep '^?? ' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_UNTRACKED$STATUS"
fi
if $(echo "$INDEX" | grep '^UU ' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_UNMERGED$STATUS"
fi
if $(echo "$INDEX" | grep '^ D ' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS"
fi
if $(echo "$INDEX" | grep '^.M ' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
elif $(echo "$INDEX" | grep '^AM ' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
elif $(echo "$INDEX" | grep '^ T ' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
fi
# Staged
if $(echo "$INDEX" | grep '^D ' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_STAGED_DELETED$STATUS"
fi
if $(echo "$INDEX" | grep '^R' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_STAGED_RENAMED$STATUS"
fi
if $(echo "$INDEX" | grep '^M' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_STAGED_MODIFIED$STATUS"
fi
if $(echo "$INDEX" | grep '^A' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_PROMPT_STAGED_ADDED$STATUS"
fi

if $(echo -n "$STATUS" | grep '.*' &> /dev/null); then
STATUS="$ZSH_THEME_GIT_STATUS_PREFIX$STATUS"
fi

echo $STATUS
}

# get the name of the branch we are on (copied and modified from git.zsh)
function custom_git_prompt() {
ref=$(git symbolic-ref HEAD 2> /dev/null) || return
echo "$ZSH_THEME_GIT_PROMPT_PREFIX${ref#refs/heads/}$(parse_git_dirty)$(git_prompt_ahead_number)$(custom_git_prompt_status)$ZSH_THEME_GIT_PROMPT_SUFFIX"
}

# %B sets bold text
PROMPT='%B$PREPROMPT$prompt_host %{$G%} $(_lp_shorten_path) $(custom_git_prompt)%{$M%}%B»%b%{$RESET%} '
RPS1="${return_code} %D{%a %b %d, %I:%M}"

ZSH_THEME_GIT_PROMPT_PREFIX="%{$Y%}‹"
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$Y%}›%{$RESET%} "

ZSH_THEME_GIT_PROMPT_DIRTY="%{$R%}*"
ZSH_THEME_GIT_PROMPT_CLEAN=""

ZSH_THEME_GIT_PROMPT_AHEAD="%{$B%}➔"


ZSH_THEME_GIT_STATUS_PREFIX=" "

# Staged
ZSH_THEME_GIT_PROMPT_STAGED_ADDED="%{$G%}A"
ZSH_THEME_GIT_PROMPT_STAGED_MODIFIED="%{$G%}M"
ZSH_THEME_GIT_PROMPT_STAGED_RENAMED="%{$G%}R"
ZSH_THEME_GIT_PROMPT_STAGED_DELETED="%{$G%}D"

# Not-staged
ZSH_THEME_GIT_PROMPT_UNTRACKED="%{$R%}?"
ZSH_THEME_GIT_PROMPT_MODIFIED="%{$R%}M"
ZSH_THEME_GIT_PROMPT_DELETED="%{$R%}D"
ZSH_THEME_GIT_PROMPT_UNMERGED="%{$R%}UU"
15 changes: 15 additions & 0 deletions ansible/files/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
worker_processes 1;

events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

include sites-enabled/*;
}
17 changes: 17 additions & 0 deletions ansible/files/ssl.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;

# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
ssl_dhparam /opt/ssl/dhparam.pem;

# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;

# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;

ssl_certificate /opt/ssl/cert.crt;
ssl_certificate_key /opt/ssl/cert.key;
32 changes: 32 additions & 0 deletions ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
ntp_timezone: 'Europe/Paris'
ssh_port: '57982'

tld: 'lan'

hostname: '{{domain}}.{{tld}}'
base_ip: '{{ ansible_default_ipv4.address|default(ansible_all_ipv4_addresses[0]) }}'

smtp_hostname: "smtp.mailgun.org"
smtp_port: "587"
smtp_tls: yes
smtp_starttls: yes

lan_ip_prefix: "192.168.14."

# https://www.opennic.org/
forwarded_dns_servers:
- 51.158.108.203
- 152.53.15.127
- 195.10.195.195

predefined_machines:
- { hostname: 'home', short_name: 'home', ip: '2', description: 'My local homelab VM' }
- { hostname: 'severusdesk', short_name: 'severusdesk', ip: '10', description: 'Main desktop computer' }
- { hostname: 'lockhart', short_name: 'lockhart', ip: '11', description: 'Main laptop (Huawei)' }
- { hostname: 'tablet', short_name: 'tablet', ip: '12', description: 'Main tablet' }
- { hostname: 'op6', short_name: 'op6', ip: '13', description: 'Main phone' }
- { hostname: 'switch', short_name: 'switch', ip: '14', description: 'Main Nintendo Switch' }

# Lets me append machines from the inventory
# without having to add them all again
machines: '{{ predefined_machines }}'
Loading

0 comments on commit 92746d4

Please sign in to comment.