Compare commits

..

10 commits

Author SHA1 Message Date
Alex edb0a3737a WIP NextCloud using Garage backend, fix app download urls 2020-07-15 16:06:28 +02:00
Alex 24118ab426 Make things work on cluster devx.adnab.me 2020-07-15 16:06:08 +02:00
Alex 65af077d5a Fix iptables not liking comment on same line 2020-07-15 16:03:51 +02:00
Alex d3ada90d83 Fix nomad ip address
Remove the networ_interface parameter in nomad config
This means that nomad will now autodetect its own ip address
by looking at the default route.
Thus nodes in a LAN behind a NAT will get their LAN address,
and internet nodes will get their public address.
They won't get their VPN addresses.
This seems not to break Consul's use of VPN addresses to address
services, and fixes attr.unique.network.ip-address for DiploNAT.
2020-07-15 16:03:51 +02:00
Alex 3bf830713f don't retrieve wireguard privkeys in ansible 2020-07-15 16:03:51 +02:00
Alex 207d1fa278 Allow external VPN nodes, make multi-DC deployment work 2020-07-15 16:03:42 +02:00
Alex bee7e10256 Document Wireguard config 2020-07-15 16:03:42 +02:00
Alex a4f9aa2d98 Set up wireguard in dev cluster 2020-07-15 16:03:33 +02:00
Alex 1a16fc7f9e Add gitea config example 2020-07-15 15:49:52 +02:00
Alex 3174179100 Achieve a working install on my VMs 2020-07-15 15:49:52 +02:00
332 changed files with 2434 additions and 3586 deletions

2
.gitmodules vendored
View file

@ -1,5 +1,5 @@
[submodule "docker/static/goStatic"]
path = app/build/static/goStatic
path = docker/static/goStatic
url = https://github.com/PierreZ/goStatic
[submodule "docker/blog/quentin.dufour.io"]
path = docker/blog-quentin/quentin.dufour.io

View file

@ -5,53 +5,42 @@ deuxfleurs.fr
## Our abstraction stack
We try to build a generic abstraction stack between our different resources (CPU, RAM, disk, etc.) and our services (Chat, Storage, etc.), we develop our own tools when needed:
We try to build a generic abstraction stack between our different resources (CPU, RAM, disk, etc.) and our services (Chat, Storage, etc.):
* **[garage](https://git.deuxfleurs.fr/Deuxfleurs/garage/):** S3-compatible lightweight object store for self-hosted geo-distributed deployments (we also have a legacy glusterfs cluster)
* **[diplonat](https://git.deuxfleurs.fr/Deuxfleurs/diplonat):** network automation (firewalling, upnp igd)
* **[bottin](https://git.deuxfleurs.fr/Deuxfleurs/bottin):** authentication and authorization (LDAP protocol, consul backend)
* **[guichet](https://git.deuxfleurs.fr/Deuxfleurs/guichet):** a dashboard for our users and administrators
* **ansible:** physical node configuration
* **nomad:** schedule containers and handle their lifecycle
* **consul:** distributed key value store + lock + service discovery
* **stolon + postgresql:** distributed relational database
* **docker:** package, distribute and isolate applications
* ansible (physical node conf)
* nomad (schedule containers)
* consul (distributed key value store / lock / service discovery)
* glusterfs (file storage)
* stolon + postgresql (distributed relational database)
* docker (container tool)
* bottin (LDAP server, auth)
Some services we provide:
* **Websites:** garage (static) + fediverse blog (plume)
* **Chat:** Synapse + Element Web (Matrix protocol)
* **Email:** Postfix SMTP + Dovecot IMAP + opendkim DKIM + Sogo webmail (legacy) | Alps webmail (experimental)
* **Storage:** Seafile (legacy) | Nextcloud (experimental)
* **Visio:** Jitsi
* Chat (Matrix/Riot)
* Email (Postfix/Dovecot/Sogo)
* Storage (Seafile)
As a generic abstraction is provided, deploying new services should be easy.
## I am lost, how this repo works?
To ease the development, we make the choice of a fully integrated environment
1. `os` the base os for the cluster
1. `build`: where you will build our OS image based on Debian that you will install on your server
2. `config`: our Ansible recipes to configure and update your freshly installed server
2. `apps` apps we deploy on the cluster
1. `build`: our Docker files to build immutable images of our applications
2. `integration`: Our Docker compose files to test locally how our built images interact together
3. `config`: Files containing application configurations to be deployed on Consul Key Value Store
4. `deployment`: Files containing application definitions to be deployed on Nomad Scheduler
3. `op_guide`: Guides to explain you operations you can do cluster wide (like configuring postgres)
## Start hacking
### Clone the repository
```
git clone https://gitlab.com/superboum/deuxfleurs.fr.git
git submodule init
git submodule update
```
### Deploying/Updating new services is done from your machine
*The following instructions are provided for ops that already have access to the servers (meaning: their SSH public key is known by the cluster).*
*The following instructions are provided for ops that already have access to the servers.*
Deploy Nomad on your machine:
```bash
export NOMAD_VER=1.0.1
export NOMAD_VER=0.9.1
wget https://releases.hashicorp.com/nomad/${NOMAD_VER}/nomad_${NOMAD_VER}_linux_amd64.zip
unzip nomad_${NOMAD_VER}_linux_amd64.zip
sudo mv nomad /usr/local/bin
@ -61,7 +50,7 @@ rm nomad_${NOMAD_VER}_linux_amd64.zip
Deploy Consul on your machine:
```bash
export CONSUL_VER=1.9.0
export CONSUL_VER=1.5.1
wget https://releases.hashicorp.com/consul/${CONSUL_VER}/consul_${CONSUL_VER}_linux_amd64.zip
unzip consul_${CONSUL_VER}_linux_amd64.zip
sudo mv consul /usr/local/bin
@ -74,37 +63,14 @@ Create an alias (and put it in your `.bashrc`) to bind APIs on your machine:
alias bind_df="ssh \
-p110 \
-N \
-L 1389:bottin2.service.2.cluster.deuxfleurs.fr:389 \
-L 4646:127.0.0.1:4646 \
-L 5432:psql-proxy.service.2.cluster.deuxfleurs.fr:5432 \
-L 8082:traefik-admin.service.2.cluster.deuxfleurs.fr:8082 \
-L 8500:127.0.0.1:8500 \
-L 8082:traefik.service.2.cluster.deuxfleurs.fr:8082 \
<a server from the cluster>"
```
and run:
bind_df
Adrien uses `.ssh/config` configuration instead. I works basically the same. Here it goes:
```
# in ~/.ssh/config
Host deuxfleurs
User adrien
Hostname deuxfleurs.fr
# If you don't use the default ~/.ssh/id_rsa to connect to Deuxfleurs
IdentityFile <some_key_path>
PubKeyAuthentication yes
ForwardAgent No
LocalForward 1389 bottin2.service.2.cluster.deuxfleurs.fr:389
LocalForward 4646 127.0.0.1:4646
LocalForward 5432 psql-proxy.service.2.cluster.deuxfleurs.fr:5432
LocalForward 8082 traefik-admin.service.2.cluster.deuxfleurs.fr:8082
LocalForward 8500 127.0.0.1:8500
bind_df
```
Now, to connect, do the following:
ssh deuxfleurs -N

5
administratif/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
*.aux
*.fdb_latexmk
*.fls
*.log
*.pdf

View file

@ -0,0 +1,68 @@
\documentclass[a4paper,DIV=12]{scrartcl}
\usepackage[french]{babel}
% On abuse komafont pour réduire la place prise par le titre
\addtokomafont{title}{\vspace*{-3em}}
\addtokomafont{author}{\vspace*{-1em}}
\addtokomafont{date}{\vspace*{-0.5em}}
% On ajoute "Article" devant les sections
\renewcommand\sectionformat{Article\enskip\thesection~:\hspace{1em}}
% On réduit la taille des sections
\addtokomafont{section}{\large}
% On rajoute un peu d'espace entre les paragraphes
\setlength{\parskip}{.8em}
% On enlève de la place après les titres
% (je n'ai pas pu utiliser le paquet dédié titlesec car il cause plein d'erreurs)
%\titlespacing\section{1pt}{*4}{*1.5}
\let\oldsection\section
\renewcommand{\section}[1]{\oldsection{#1}\vspace{-1em}}
\title{Procès-verbal de lassemblée générale constitutive de l'association Deuxfleurs}
\date{13 janvier 2020}
\author{Association Deuxfleurs\\10A Allée de Lanvaux, 35700 Rennes}
\begin{document}
\maketitle
Le 13 janvier 2020 à 19 heures, les fondateurs de lassociation Deuxfleurs se sont réunis en assemblée générale constitutive au 24 rue des Tanneurs à Rennes. Sont présents Adrien, Alex, Anaïs, Axelle, Louison, Maximilien, Quentin, Rémi et Vincent.
Lassemblée générale désigne Adrien Luxey en qualité de président de séance et Quentin Dufour en qualité de secrétaire de séance.
Le président de séance met à la disposition des présents le projet de statuts de lassociation et létat des actes passés pour le compte de lassociation en formation.
Puis il rappelle que lassemblée générale constitutive est appelée à statuer sur lordre du jour suivant :
\begin{itemize}
\item présentation du projet de constitution de lassociation ;
\item présentation du projet de statuts ;
\item adoption des statuts ;
\item désignation des premiers membres du conseil ;
\item pouvoirs en vue des formalités de déclaration et publication.
\end{itemize}
Enfin, le président de séance expose les motifs du projet de création de lassociation et commente le projet de statuts.
Il ouvre la discussion. Un débat sinstaure entre les membres de lassemblée.
Après quoi, personne ne demandant plus la parole, le président met successivement aux voix les délibérations suivantes.
\paragraph{1\iere~délibération} Lassemblée générale adopte les statuts dont le projet lui a été soumis.
Cette délibération est adoptée à lunanimité.
\paragraph{2\ieme~délibération} Lassemblée générale constitutive désigne en qualité de premiers membres du conseil d'administration :
\begin{itemize}
\item Adrien Luxey
\item Alex Auvolat
\item Maximilien Richer
\item Quentin Dufour
\item Vincent Giraud
\end{itemize}
Conformément aux statuts, cette désignation est faite pour une durée expirant lors de lassemblée générale qui sera appelée à statuer sur les comptes de lexercice clos le 13 janvier 2021.
Les membres du conseil ainsi désignés acceptent leurs fonctions
Nom, prénom et signature du président et du secrétaire de séance
\end{document}

View file

@ -0,0 +1,104 @@
\documentclass[a4paper,DIV=12]{scrartcl}
\usepackage[frenchb]{babel}
% On abuse komafont pour réduire la place prise par le titre
\addtokomafont{title}{\vspace*{-3em}}
\addtokomafont{author}{\vspace*{-1em}}
\addtokomafont{date}{\vspace*{-2em}}
% On ajoute "Article" devant les sections
\renewcommand\sectionformat{Article\enskip\thesection~:\hspace{1em}}
% On réduit la taille des sections
\addtokomafont{section}{\large}
% On rajoute un peu d'espace entre les paragraphes
\setlength{\parskip}{.8em}
% On enlève de la place après les titres
% (je n'ai pas pu utiliser le paquet dédié titlesec car il cause plein d'erreurs)
%\titlespacing\section{1pt}{*4}{*1.5}
\let\oldsection\section
\renewcommand{\section}[1]{\oldsection{#1}\vspace{-1em}}
\title{Statuts de l'association Deuxfleurs}
\date{13 janvier 2020}
\begin{document}
\maketitle
\section{Constitution et dénomination}
Il est fondé entre les adhérents aux présents statuts une association régie par la loi 1901, ayant pour titre Deuxfleurs.
\section{Buts}
Cette association a pour but de défendre et promouvoir les libertés individuelles et collectives à travers la mise en place d'infrastuctures numériques libres.
\section{Siège social}
Le siège social est fixé au 10A, Allée de Lanvaux, 35700 Rennes.
Il pourra être transféré suite à un vote par l'assemblée générale.
\section{Durée de l'association}
L'association perdure tant qu'elle possède au moins un membre, ou jusqu'à sa dissolution décidée en assemblée générale.
\section{Admission et adhésion}\label{article:admission}
Pour faire partie de l'association, il faut être coopté par un membre de l'association, adhérer aux présents statuts et s'acquitter de la cotisation annuelle dont le montant est de 10 euros.
\section{Composition de l'association}
L'association se compose exclusivement de membres admis selon les dispositions de l'article~\ref{article:admission} et à jour de leur cotisation.
Tout membre actif possède une voix lors des votes en assemblée générale.
Est considéré actif tout membre présent à l'assemblée générale (physiquement, par visioconférence ou par procuration écrite donnée à un autre membre de l'association).
\section{Perte de la qualité de membre}
La qualité de membre se perd par :
\begin{itemize}
\item la démission,
\item le non-renouvelement de la cotisation dans un délai de deux mois après le 1er Janvier de l'année courante,
\item le décès,
\item la radiation prononcée aux deux tiers des votes exprimés, lors d'un vote extraordinaire ou de l'assemblée générale.
\end{itemize}
\section{L'assemblée générale}\label{article:ag}
L'assemblée générale ordinaire se réunit au moins une fois par an, convoquée par le conseil d'administration.
Lassemblée générale extraordinaire est convoquée par le conseil dadministration, à la demande de celui-ci ou à la demande du quart au moins des membres de l'association.
L'assemblée générale (ordinaire ou extraordinaire) comprend tous les membres de l'association à jour de leur cotisation.
Quinze jours au moins avant la date fixée, les membres de l'association sont convoqués via la liste de diffusion de l'association et l'ordre du jour est inscrit sur les convocations.
Le conseil dadministration anime lassemblée générale.
Lassemblée générale, après avoir délibéré, se prononce sur le rapport moral et/ou d'activités.
Le conseil dadministration rend compte de l'exercice financier clos et soumet le bilan de lexercice clos à lapprobation de lassemblée dans un délai de six mois après la clôture des comptes.
Lassemblée générale délibère sur les orientations à venir et se prononce sur le budget prévisionnel de lannée en cours.
Elle pourvoit, au scrutin secret, à la nomination ou au renouvellement des membres du conseil d'administration via un scrutin de Condorcet Randomisé.
Elle fixe le montant de la cotisation annuelle.
Les décisions de l'assemblée sont prises à la majorité des membres présents ou représentés.
Chaque membre présent ne peut détenir plus d'une procuration.
\section{Membres mineurs}
Les mineurs peuvent adhérer à lassociation sous réserve dun accord tacite ou dune autorisation écrite de leurs parents ou tuteurs légaux.
Ils sont membres à part entière de lassociation.
Seuls les membres âgés de 16 ans au moins au jour dune élection sont autorisés à y voter, notamment au cours d'une assemblée générale.
Pour les autres, leur droit de vote est transmis à leur représentant légal.
\section{Le conseil d'administration}
L'association est administrée par un conseil d'administration composé de 3 à 6 membres, élus pour 1 an dans les conditions fixées à larticle~\ref{article:ag}.
Tous les membres de lassociation à jour de leur cotisation sont éligibles.
En cas de vacance de poste, le conseil d'administration peut pourvoir provisoirement au remplacement de ses membres. Ce remplacement est obligatoire quand le conseil d'administration compte moins de 3 membres.
Il est procédé à leur remplacement définitif à la plus prochaine assemblée générale.
Les pouvoirs des membres ainsi élus prennent fin à l'époque où devrait normalement expirer le mandat des membres remplacés.
Le conseil dadministration met en œuvre les décisions de lassemblée générale, organise et anime la vie de lassociation, dans le cadre fixé par les statuts.
Chacun de ses membres peut être habilité par le conseil à remplir toutes les formalités de déclaration et de publication prescrites par la législation et tout autre acte nécessaire au fonctionnement de lassociation et décidé par le conseil dadministration.
Tous les membres du conseil dadministration sont responsables des engagements contractés par lassociation.
Tout contrat ou convention passé entre lassociation d'une part, et un membre du conseil d'administration, son conjoint ou un proche, d'autre part, est soumis pour autorisation au conseil d'administration et présenté pour information à la plus prochaine assemblée générale.
Le conseil dadministration se réunit au moins 4 fois par an et toutes les fois qu'il est convoqué par le tiers de ses membres.
La présence de la moitié au moins des membres du conseil est nécessaire pour que le conseil d'administration puisse délibérer valablement.
Les décisions sont prises au consensus et, à défaut, à la majorité des voix des présents. Le vote par procuration n'est pas autorisé.
\section{Modification des statuts de l'association}
Sur demande d'un tiers des membres actifs, ou sur demande du conseil d'administration, des amendements aux statuts de l'association peuvent être discutés et soumis au vote lors d'une assemblée générale, selon les modalités de l'article~\ref{article:ag}.
\end{document}

3
administratif/README.md Normal file
View file

@ -0,0 +1,3 @@
# Documents administatifs
__Statuts__ : Pour compiler les statuts, faites `latexmk -pdf statuts.tex`

71
ansible/README.md Normal file
View file

@ -0,0 +1,71 @@
# ANSIBLE
## How to proceed
For each machine, **one by one** do:
- Check that cluster is healthy
- `sudo gluster peer status`
- `sudo gluster volume status all` (check Online Col, only `Y` must appear)
- Check that Nomad is healthy
- Check that Consul is healthy
- Check that Postgres is healthy
- Run `ansible-playbook -i production --limit <machine> site.yml`
- Reboot
- Check that cluster is healthy
## New configuration with Wireguard
This configuration is used to make all of the cluster nodes appear in a single
virtual private network, enable them to communicate on all ports even if they
are behind NATs at different locations. The VPN also provides a layer of
security, encrypting all comunications that occur over the internet.
### Prerequisites
Nodes must all have two publicly accessible ports (potentially routed through a NAT):
- A port that maps to the SSH port (port 22) of the machine, allowing TCP connections
- A port that maps to the Wireguard port (port 51820) of the machine, allowing UDP connections
### Configuration
The network role sets up a Wireguard interface, called `wgdeuxfleurs`, and
establishes a full mesh between all cluster machines. The following
configuration variables are necessary in the node list:
- `ansible_host`: hostname to which Ansible connects to, usually the same as `public_ip`
- `ansible_user`: username to connect as for Ansible to run commands through SSH
- `ansible_port`: if SSH is not bound publicly on port 22, set the port here
- `public_ip`: the public IP for the machine or the NATting router behind which the machine is
- `public_vpn_port`: the public port number on `public_ip` that maps to port 51820 of the machine
- `vpn_ip`: the IP address to affect to the node on the VPN (each node must have a different one)
- `dns_server`: any DNS resolver, typically your ISP's DNS or a public one such as OpenDNS
The new iptables configuration now prevents direct communication between
cluster machines, except on port 51820 which is used to transmit VPN packets.
All intra-cluster communications must now go through the VPN interface (thus
machines refer to one another using their VPN IP addresses and never their
public or LAN addresses).
### Restarting Nomad
When switching to the Wireguard configuration, machines will stop using their
LAN addresses and switch to using their VPN addresses. Consul seems to handle
this correctly, however Nomad does not. To make Nomad able to restart
correctly, its Raft protocol module must be informed of the new IP addresses of
the cluster members. This is done by creating on all nodes the file
`/var/lib/nomad/server/raft/peers.json` that contains the list of IP addresses
of the cluster. Here is an example for such a file:
```
["10.68.70.11:4647","10.68.70.12:4647","10.68.70.13:4647"]
```
Once this file is created and is the same on all nodes, restart Nomad on all
nodes. The cluster should resume operation normally.
The same procedure can also be applied to fix Consul, however my tests showed
that it didn't break when IP addresses changed (it just took a bit long to come
back up).

6
ansible/lxvm Normal file
View file

@ -0,0 +1,6 @@
[cluster_nodes]
#ubuntu1 ansible_host=192.168.42.10
debian1 ansible_host=192.168.42.20 ansible_user=root public_ip=192.168.42.20 dns_server=208.67.222.222 vpn_ip=10.68.70.11 public_vpn_port=51820 datacenter=belair interface=enp1s0
debian2 ansible_host=192.168.42.21 ansible_user=root public_ip=192.168.42.21 dns_server=208.67.222.222 vpn_ip=10.68.70.12 public_vpn_port=51820 datacenter=belair interface=enp1s0
debian3 ansible_host=192.168.42.22 ansible_user=root public_ip=192.168.42.22 dns_server=208.67.222.222 vpn_ip=10.68.70.13 public_vpn_port=51820 datacenter=belair interface=enp1s0
ovh1 ansible_host=51.75.4.20 ansible_user=debian ansible_become=yes public_ip=51.75.4.20 dns_server=208.67.222.222 vpn_ip=10.68.70.20 public_vpn_port=51820 datacenter=saturne interface=eth0

4
ansible/production Normal file
View file

@ -0,0 +1,4 @@
[cluster_nodes]
veterini ansible_host=fbx-rennes2.machine.deuxfleurs.fr ansible_port=110 ansible_user=root public_ip=192.168.1.2 private_ip=192.168.1.2 interface=eno1 dns_server=80.67.169.40
silicareux ansible_host=fbx-rennes2.machine.deuxfleurs.fr ansible_port=111 ansible_user=root public_ip=192.168.1.3 private_ip=192.168.1.3 interface=eno1 dns_server=80.67.169.40
wonse ansible_host=fbx-rennes2.machine.deuxfleurs.fr ansible_port=112 ansible_user=root public_ip=192.168.1.4 private_ip=192.168.1.4 interface=eno1 dns_server=80.67.169.40

View file

@ -3,6 +3,7 @@
that:
- "ansible_architecture == 'aarch64' or ansible_architecture == 'armv7l' or ansible_architecture == 'x86_64'"
- "ansible_os_family == 'Debian'"
- "ansible_distribution_version == '10'"
- name: "Upgrade system"
apt:
@ -15,6 +16,7 @@
- name: "Install base tools"
apt:
name:
- sudo
- vim
- htop
- screen

View file

@ -1,6 +1,6 @@
- name: "Set consul version"
set_fact:
consul_version: 1.9.1
consul_version: 1.8.0
- name: "Download and install Consul for x86_64"
unarchive:
@ -21,3 +21,6 @@
- name: "Enable consul systemd service at boot"
service: name=consul state=started enabled=yes daemon_reload=yes
- name: "Deploy resolv.conf to use Consul"
template: src=resolv.conf.j2 dest=/etc/resolv.conf

View file

@ -0,0 +1,31 @@
{
"datacenter": "deuxfleurs",
"data_dir": "/var/lib/consul",
"bind_addr": "0.0.0.0",
"advertise_addr": "{{ vpn_ip }}",
"addresses": {
"dns": "0.0.0.0",
"http": "0.0.0.0"
},
"retry_join": [
{% for selected_host in groups['cluster_nodes']|difference([inventory_hostname]) %}{# @FIXME: Reject doesn't work #}
"{{ hostvars[selected_host]['vpn_ip'] }}" {{ "," if not loop.last else "" }}
{% endfor %}
],
"bootstrap_expect": {{ groups['cluster_nodes']|length }},
"server": true,
"ui": true,
"ports": {
"dns": 53
},
"recursors": [
"{{ dns_server }}"
],
"encrypt": "{{ consul_gossip_encrypt }}",
"domain": "2.cluster.deuxfleurs.fr",
"performance": {
"raft_multiplier": 10,
"rpc_hold_timeout": "30s",
"leave_drain_time": "30s"
}
}

View file

@ -0,0 +1,2 @@
nameserver {{ vpn_ip }}
nameserver {{ dns_server }}

View file

@ -0,0 +1,12 @@
# WARNING!! When rules.{v4,v6} are changed, the whole iptables configuration is reloaded.
# This creates issues with Docker, which injects its own configuration in iptables when it starts.
# In practice, most (all?) containers will break if rules.{v4,v6} are changed,
# and docker will have to be restared.
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

View file

@ -0,0 +1,5 @@
---
- name: reload wireguard
service:
name: wg-quick@wgdeuxfleurs
state: restarted

View file

@ -0,0 +1,59 @@
- name: "Create iptables configuration direcetory"
file: path=/etc/iptables/ state=directory
- name: "Deploy iptablesv4 configuration"
template: src=rules.v4.j2 dest=/etc/iptables/rules.v4
- name: "Deploy iptablesv6 configuration"
copy: src=rules.v6 dest=/etc/iptables/rules.v6
- name: "Activate IP forwarding"
sysctl:
name: net.ipv4.ip_forward
value: "1"
sysctl_set: yes
# Wireguard configuration
- name: "Enable backports repository"
apt_repository:
repo: deb http://deb.debian.org/debian buster-backports main
state: present
- name: "Install wireguard"
apt:
name:
- wireguard
- wireguard-tools
- "linux-headers-{{ ansible_kernel }}"
state: present
- name: "Create wireguard configuration direcetory"
file: path=/etc/wireguard/ state=directory
- name: "Check if wireguard private key exists"
stat: path=/etc/wireguard/privkey
register: wireguard_privkey
- name: "Create wireguard private key"
shell: wg genkey > /etc/wireguard/privkey
when: wireguard_privkey.stat.exists == false
notify:
- reload wireguard
- name: "Secure wireguard private key"
file: path=/etc/wireguard/privkey mode=0600
- name: "Retrieve wireguard public key"
shell: wg pubkey < /etc/wireguard/privkey
register: wireguard_pubkey
- name: "Deploy wireguard configuration"
template: src=wireguard.conf.j2 dest=/etc/wireguard/wgdeuxfleurs.conf mode=0600
notify:
- reload wireguard
- name: "Enable Wireguard systemd service at boot"
service: name=wg-quick@wgdeuxfleurs state=started enabled=yes daemon_reload=yes
- name: "Create /tmp/wgdeuxfleurs.template.conf example configuration file for external nodes"
local_action: template src=wireguard_external.conf.j2 dest=/tmp/wgdeuxfleurs.template.conf

View file

@ -3,21 +3,25 @@
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Internet Control Message Protocol
-A INPUT -p icmp -j ACCEPT
# Administration
-A INPUT -p tcp --dport 22 -j ACCEPT
# Diplonat needs everything open to communicate with IGD with the router
-A INPUT -s 192.168.1.254 -j ACCEPT
# Cluster
{% for selected_host in groups['cluster_nodes'] %}
-A INPUT -s {{ hostvars[selected_host]['ipv4'] }} -j ACCEPT
-A INPUT -s {{ hostvars[selected_host]['public_ip'] }} -p udp --dport 51820 -j ACCEPT
-A INPUT -s {{ hostvars[selected_host]['vpn_ip'] }} -j ACCEPT
{% endfor %}
{% for host in other_vpn_nodes %}
-A INPUT -s {{ host.public_ip }} -p udp --dport 51820 -j ACCEPT
-A INPUT -s {{ host.vpn_ip }} -j ACCEPT
{% endfor %}
# Local
# Rennes
-A INPUT -s 93.2.173.168 -j ACCEPT
-A INPUT -s 82.253.205.190 -j ACCEPT
# router
-A INPUT -s 192.168.1.254 -j ACCEPT
-A INPUT -i docker0 -j ACCEPT
-A INPUT -s 127.0.0.1/8 -j ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

View file

@ -0,0 +1,20 @@
[Interface]
Address = {{ vpn_ip }}
PostUp = wg set %i private-key <(cat /etc/wireguard/privkey)
ListenPort = 51820
{% for selected_host in groups['cluster_nodes']|difference([inventory_hostname]) %}
[Peer]
PublicKey = {{ hostvars[selected_host].wireguard_pubkey.stdout }}
Endpoint = {{ hostvars[selected_host].public_ip }}:{{ hostvars[selected_host].public_vpn_port }}
AllowedIPs = {{ hostvars[selected_host].vpn_ip }}/32
PersistentKeepalive = 25
{% endfor %}
{% for host in other_vpn_nodes %}
[Peer]
PublicKey = {{ host.pubkey }}
Endpoint = {{ host.public_ip }}:{{ host.public_vpn_port }}
AllowedIPs = {{ host.vpn_ip }}/32
PersistentKeepalive = 25
{% endfor %}

View file

@ -0,0 +1,27 @@
# Template configuration file for VPN nodes that are non in the cluster
# The private key should be stored as /etc/wireguard/privkey
# External nodes should be registered in network/vars/main.yml
[Interface]
Address = <INSERT YOUR IP HERE, IT SHOULD MATCH THE ONE IN vars/main.yml>
PostUp = wg set %i private-key <(cat /etc/wireguard/privkey)
ListenPort = 51820
# Cluster nodes
{% for selected_host in groups['cluster_nodes'] %}
[Peer]
PublicKey = {{ hostvars[selected_host].wireguard_pubkey.stdout }}
Endpoint = {{ hostvars[selected_host].public_ip }}:{{ hostvars[selected_host].public_vpn_port }}
AllowedIPs = {{ hostvars[selected_host].vpn_ip }}/32
PersistentKeepalive = 25
{% endfor %}
# External nodes
# TODO: remove yourself from here
{% for host in other_vpn_nodes %}
[Peer]
PublicKey = {{ host.pubkey }}
Endpoint = {{ host.public_ip }}:{{ host.public_vpn_port }}
AllowedIPs = {{ host.vpn_ip }}/32
PersistentKeepalive = 25
{% endfor %}

View file

@ -0,0 +1,6 @@
---
other_vpn_nodes:
- pubkey: "QUiUNMk70TEQ75Ut7Uqikr5uGVSXmx8EGNkGM6tANlg="
public_ip: "37.187.118.206"
public_vpn_port: "51820"
vpn_ip: "10.68.70.101"

View file

@ -1,6 +1,10 @@
- name: "Set nomad version"
- name: "Set Nomad version"
set_fact:
nomad_version: 1.0.2
nomad_version: 0.12.0-beta2
- name: "Set CNI version"
set_fact:
cni_plugins_version: 0.8.6
- name: "Download and install Nomad for x86_64"
unarchive:
@ -10,6 +14,19 @@
when:
- "ansible_architecture == 'x86_64'"
- name: "Create /opt/cni/bin"
file: path=/opt/cni/bin state=directory
- name: "Download and install CNI plugins for x86_64"
unarchive:
src: "https://github.com/containernetworking/plugins/releases/download/v{{ cni_plugins_version }}/cni-plugins-linux-amd64-v{{ cni_plugins_version }}.tgz"
dest: /opt/cni/bin
remote_src: yes
when:
- "ansible_architecture == 'x86_64'"
notify:
- restart nomad
- name: "Create Nomad configuration directory"
file: path=/etc/nomad/ state=directory

View file

@ -0,0 +1,46 @@
datacenter = "{{ datacenter }}"
addresses {
http = "0.0.0.0"
rpc = "0.0.0.0"
serf = "0.0.0.0"
}
advertise {
http = "{{ vpn_ip }}"
rpc = "{{ vpn_ip }}"
serf = "{{ vpn_ip }}"
}
data_dir = "/var/lib/nomad"
server {
enabled = true
bootstrap_expect = {{ groups['cluster_nodes']|length }}
}
consul {
address="127.0.0.1:8500"
}
client {
enabled = true
#cpu_total_compute = 4000
servers = ["127.0.0.1:4648"]
options {
docker.privileged.enabled = "true"
docker.volumes.enabled = "true"
}
network_interface = "wgdeuxfleurs"
host_network "default" {
#cidr = "{{ vpn_ip }}/24"
interface = "wgdeuxfleurs"
}
host_network "public" {
#cidr = "{{ public_ip }}/32"
interface = "{{ interface }}"
}
}

View file

@ -48,7 +48,7 @@
nfs.export-volumes: "off"
cluster.lookup-optimize: "on"
cluster: "{% for selected_host in groups['cluster_nodes'] %}{{ hostvars[selected_host]['ipv4'] }}{{ ',' if not loop.last else '' }}{% endfor %}"
cluster: "{% for selected_host in groups['cluster_nodes'] %}{{ hostvars[selected_host]['vpn_ip'] }}{{ ',' if not loop.last else '' }}{% endfor %}"
run_once: true
- name: "Create mountpoint"
@ -61,7 +61,7 @@
tags: gluster-fstab
mount:
path: /mnt/glusterfs
src: "{{ ipv4 }}:/donnees"
src: "{{ vpn_ip }}:/donnees"
fstype: glusterfs
opts: "defaults,_netdev,noauto,x-systemd.automount"
state: present

View file

@ -10,8 +10,7 @@ active_users:
is_admin: true
ssh_keys:
- 'alex-key1.pub'
#- 'alex-key2.pub'
- 'alex-key3.pub'
- 'alex-key2.pub'
- username: 'maximilien'
is_admin: true
@ -21,13 +20,9 @@ active_users:
- username: 'florian'
is_admin: false
ssh_keys:
- 'florian-key1.pub'
- 'florian-key2.pub'
- username: 'adrien'
is_admin: false
ssh_keys:
- 'adrien-key1.pub'
- 'quentin-key1.pub'
#- 'florian-key1.pub'
#- 'florian-key2.pub'
disabled_users:
- 'john.doe'

2
app/.gitignore vendored
View file

@ -1,2 +0,0 @@
env/
__pycache__

View file

@ -1,66 +0,0 @@
# Folder hierarchy
- `<module>/build/<image_name>/`: folders with dockerfiles and other necessary resources for building container images
- `<module>/config/`: folder containing configuration files, referenced by deployment file
- `<module>/secrets/`: folder containing secrets, which can be synchronized with Consul using `secretmgr.py`
- `<module>/deploy/`: folder containing the HCL file(s) necessary for deploying the module
- `<module>/integration/`: folder containing files for integration testing using docker-compose
# Secret Manager `secretmgr.py`
The Secret Manager ensures that all secrets are present where they should in the cluster.
**You need access to the cluster** (SSH port forwarding) for it to find any secret on the cluster. Refer to the previous directory's [README](../README.md), at the bottom of the file.
## How to install `secretmgr.py` dependencies
```bash
### Install system dependencies first:
## On fedora
dnf install -y openldap-devel cyrus-sasl-devel
## On ubuntu
apt-get install -y libldap2-dev libsasl2-dev
### Now install the Python dependencies from requirements.txt:
## Either using a virtual environment
# (requires virtualenv python module)
python3 -m virtualenv env
# Must be done everytime you create a new terminal window in this folder:
. env/bin/activate
# Install the deps
pip install -r requirements.txt
## Either by installing the dependencies for your system user:
pip3 install --user -r requirements.txt
```
## How to use `secretmgr.py`
Check that all secrets are correctly deployed for app `dummy`:
```bash
./secretmgr.py check dummy
```
Generate secrets for app `dummy` if they don't already exist:
```bash
./secretmgr.py gen dummy
```
Rotate secrets for app `dummy`, overwriting existing ones (be careful, this is dangerous!):
```bash
./secretmgr.py regen dummy
```
# Upgrading one of our packaged apps to a new version
1. Edit `docker-compose.yml`
2. Change the `VERSION` variable to the desired version
3. Increment the docker image tag by 1 (eg: superboum/riot:v13 -> superboum/riot:v14)
4. Run `docker-compose build`
5. Run `docker-compose push`
6. Done

View file

@ -1,28 +0,0 @@
FROM golang:buster as builder
WORKDIR /root
RUN git clone https://filippo.io/age && cd age/cmd/age && go build -o age .
FROM amd64/debian:buster
COPY --from=builder /root/age/cmd/age/age /usr/local/bin/age
RUN apt-get update && \
apt-get -qq -y full-upgrade && \
apt-get install -y rsync wget openssh-client unzip && \
apt-get clean && \
rm -f /var/lib/apt/lists/*_*
RUN mkdir -p /root/.ssh
WORKDIR /root
RUN wget https://releases.hashicorp.com/consul/1.8.5/consul_1.8.5_linux_amd64.zip && \
unzip consul_1.8.5_linux_amd64.zip && \
chmod +x consul && \
mv consul /usr/local/bin && \
rm consul_1.8.5_linux_amd64.zip
COPY do_backup.sh /root/do_backup.sh
CMD "/root/do_backup.sh"

View file

@ -1,20 +0,0 @@
#!/bin/sh
set -x -e
cd /root
chmod 0600 .ssh/id_ed25519
cat > .ssh/config <<EOF
Host backuphost
HostName $TARGET_SSH_HOST
Port $TARGET_SSH_PORT
User $TARGET_SSH_USER
EOF
consul kv export | \
gzip | \
age -r "$(cat /root/.ssh/id_ed25519.pub)" | \
ssh backuphost "cat > $TARGET_SSH_DIR/consul/$(date --iso-8601=minute)_consul_kv_export.gz.age"

View file

@ -1,67 +0,0 @@
job "backup_periodic" {
datacenters = ["dc1"]
type = "batch"
periodic {
// Launch every hour
cron = "0 * * * * *"
// Do not allow overlapping runs.
prohibit_overlap = true
}
task "backup-consul" {
driver = "docker"
config {
image = "lxpz/backup_consul:12"
volumes = [
"secrets/id_ed25519:/root/.ssh/id_ed25519",
"secrets/id_ed25519.pub:/root/.ssh/id_ed25519.pub",
"secrets/known_hosts:/root/.ssh/known_hosts"
]
network_mode = "host"
}
env {
CONSUL_HTTP_ADDR = "http://consul.service.2.cluster.deuxfleurs.fr:8500"
}
template {
data = <<EOH
TARGET_SSH_USER={{ key "secrets/backup/target_ssh_user" }}
TARGET_SSH_PORT={{ key "secrets/backup/target_ssh_port" }}
TARGET_SSH_HOST={{ key "secrets/backup/target_ssh_host" }}
TARGET_SSH_DIR={{ key "secrets/backup/target_ssh_dir" }}
EOH
destination = "secrets/env_vars"
env = true
}
template {
data = "{{ key \"secrets/backup/id_ed25519\" }}"
destination = "secrets/id_ed25519"
}
template {
data = "{{ key \"secrets/backup/id_ed25519.pub\" }}"
destination = "secrets/id_ed25519.pub"
}
template {
data = "{{ key \"secrets/backup/target_ssh_fingerprint\" }}"
destination = "secrets/known_hosts"
}
resources {
memory = 200
}
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
}
}

View file

@ -1 +0,0 @@
USER_LONG Private ed25519 key of the container doing the backup

View file

@ -1 +0,0 @@
USER Public ed25519 key of the container doing the backup (this key must be in authorized_keys on the backup target host)

View file

@ -1 +0,0 @@
USER Directory where to store backups on target host

View file

@ -1 +0,0 @@
USER SSH fingerprint of the target machine (format: copy here the corresponding line from your known_hosts file)

View file

@ -1 +0,0 @@
USER Hostname of the backup target host

View file

@ -1 +0,0 @@
USER SSH port number to connect to the target host

View file

@ -1 +0,0 @@
USER SSH username to log in as on the target host

View file

@ -1,94 +0,0 @@
version: '3.4'
services:
mariadb:
build:
context: ./seafile/build/mariadb
args:
VERSION: 4 # fake for now
image: superboum/amd64_mariadb:v4
# Instant Messaging
riot:
build:
context: ./im/build/riotweb
args:
# https://github.com/vector-im/riot-web/releases
VERSION: 1.7.18
image: particallydone/amd64_riotweb:v20
synapse:
build:
context: ./im/build/matrix-synapse
args:
# https://github.com/matrix-org/synapse/releases
VERSION: 1.26.0
image: particallydone/amd64_synapse:v41
# Email
sogo:
build:
context: ./email/build/sogo
args:
# fake for now
VERSION: 5.0.0
image: superboum/amd64_sogo:v7
alps:
build:
context: ./email/build/alps
args:
VERSION: 5cef0aaff2b8b6ee3e00b566123517e241d8cfb8
image: superboum/amd64_alps:v1
# VoIP
jitsi-meet:
build:
context: ./jitsi/build/jitsi-meet
args:
# https://github.com/jitsi/jitsi-meet
PREFIXV: stable/jitsi-meet_
VERSION: 5463
image: superboum/amd64_jitsi_meet:v4
jitsi-conference-focus:
build:
context: ./jitsi/build/jitsi-conference-focus
args:
# https://github.com/jitsi/jicofo
PREFIXV: jitsi-meet_
VERSION: 5463
image: superboum/amd64_jitsi_conference_focus:v7
jitsi-videobridge:
build:
context: ./jitsi/build/jitsi-videobridge
args:
# https://github.com/jitsi/jitsi-videobridge
PREFIXV: jitsi-meet_
VERSION: 5463
image: superboum/amd64_jitsi_videobridge:v17
jitsi-xmpp:
build:
context: ./jitsi/build/jitsi-xmpp
args:
PREFIXV: jitsi-meet_
MEET_VERSION: 5463
PROSODY_VERSION: 0.11.7-1~buster4
image: superboum/amd64_jitsi_xmpp:v9
plume:
build:
context: ./plume/build/plume
args:
VERSION: 0.6.0
image: superboum/plume:v2
postfix:
build:
context: ./email/build/postfix
args:
# https://packages.debian.org/fr/buster/postfix
VERSION: 3.4.14-0+deb10u1
image: superboum/amd64_postfix:v3

View file

@ -1 +0,0 @@
CMD head -c 10 /dev/urandom | base64

View file

@ -1 +0,0 @@
CONST this is a constant

View file

@ -1,5 +0,0 @@
CONST_LONG
this is a
constant
on several
lines

View file

@ -1 +0,0 @@
SERVICE_DN dummy Dummy service for testing secretmgr.py

View file

@ -1 +0,0 @@
SERVICE_PASSWORD dummy

View file

@ -1 +0,0 @@
USER Test user value

View file

@ -1,21 +0,0 @@
FROM golang:1.15.6-buster as builder
ARG VERSION
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
WORKDIR /tmp/alps
RUN git init && \
git remote add origin https://git.sr.ht/~migadu/alps && \
git fetch --depth 1 origin ${VERSION} && \
git checkout FETCH_HEAD
COPY skipverify.patch skipverify.patch
RUN git apply skipverify.patch && \
go build -a -o /usr/local/bin/alps ./cmd/alps
FROM scratch
COPY --from=builder /usr/local/bin/alps /alps
COPY --from=builder /tmp/alps/themes /themes
ENTRYPOINT ["/alps"]

View file

@ -1,55 +0,0 @@
From 47765c10f1af2013556f76dc63dfa056167ae5e8 Mon Sep 17 00:00:00 2001
From: Quentin <quentin@deuxfleurs.fr>
Date: Fri, 4 Dec 2020 13:19:24 +0100
Subject: [PATCH] Skip CA verification
---
imap.go | 3 ++-
smtp.go | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap.go b/imap.go
index 7554331..1a4931d 100644
--- a/imap.go
+++ b/imap.go
@@ -3,6 +3,7 @@ package alps
import (
"fmt"
+ "crypto/tls"
"github.com/emersion/go-imap"
imapclient "github.com/emersion/go-imap/client"
"github.com/emersion/go-message/charset"
@@ -16,7 +17,7 @@ func (s *Server) dialIMAP() (*imapclient.Client, error) {
var c *imapclient.Client
var err error
if s.imap.tls {
- c, err = imapclient.DialTLS(s.imap.host, nil)
+ c, err = imapclient.DialTLS(s.imap.host, &tls.Config{InsecureSkipVerify: true})
if err != nil {
return nil, fmt.Errorf("failed to connect to IMAPS server: %v", err)
}
diff --git a/smtp.go b/smtp.go
index 5e178f2..8d22f1d 100644
--- a/smtp.go
+++ b/smtp.go
@@ -3,6 +3,7 @@ package alps
import (
"fmt"
+ "crypto/tls"
"github.com/emersion/go-smtp"
)
@@ -14,7 +15,7 @@ func (s *Server) dialSMTP() (*smtp.Client, error) {
var c *smtp.Client
var err error
if s.smtp.tls {
- c, err = smtp.DialTLS(s.smtp.host, nil)
+ c, err = smtp.DialTLS(s.smtp.host, &tls.Config{InsecureSkipVerify: true})
if err != nil {
return nil, fmt.Errorf("failed to connect to SMTPS server: %v", err)
}
--
2.28.0

View file

@ -1,5 +0,0 @@
require ["fileinto", "mailbox"];
if header :contains "X-Spam-Flag" "YES" {
fileinto :create "Junk";
}

View file

@ -1,8 +0,0 @@
hosts = ldap.example.com
dn = cn=admin,dc=example,dc=com
dnpass = s3cr3t
base = dc=example,dc=com
scope = subtree
user_filter = (&(mail=%u)(&(objectClass=inetOrgPerson)(memberOf=cn=email,ou=groups,dc=example,dc=com)))
pass_filter = (&(mail=%u)(&(objectClass=inetOrgPerson)(memberOf=cn=email,ou=groups,dc=example,dc=com)))
user_attrs = mail=/var/mail/%{ldap:mail}

View file

@ -1,79 +0,0 @@
auth_mechanisms = plain login
auth_username_format = %u
log_timestamp = "%Y-%m-%d %H:%M:%S "
mail_location = maildir:/var/mail/%u
mail_privileged_group = mail
log_path = /dev/stderr
info_log_path = /dev/stdout
debug_log_path = /dev/stdout
protocols = imap sieve lmtp
ssl_cert = < /etc/ssl/certs/dovecot.crt
ssl_key = < /etc/ssl/private/dovecot.key
service auth {
inet_listener {
port = 1337
}
}
passdb {
args = /etc/dovecot/dovecot-ldap.conf
driver = ldap
}
service lmtp {
inet_listener lmtp {
address = 0.0.0.0
port = 24
}
}
service imap-login {
inet_listener imap {
port = 143
}
inet_listener imaps {
port = 993
}
}
userdb {
args = uid=mailstore gid=mailstore home=/var/mail/%u
driver = static
}
protocol imap {
mail_plugins = $mail_plugins imap_sieve
}
protocol lda {
auth_socket_path = /var/run/dovecot/auth-master
info_log_path = /var/log/dovecot-deliver.log
log_path = /var/log/dovecot-deliver-errors.log
postmaster_address = postmaster@deuxfleurs.fr
mail_plugins = $mail_plugins sieve
}
plugin {
sieve = file:~/sieve;active=~/dovecot.sieve
sieve_before = /etc/dovecot/all_before.sieve
# antispam learn
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment +vnd.dovecot.debug
sieve_pipe_bin_dir = /usr/bin
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY FLAG APPEND
imapsieve_mailbox1_before = file:/etc/dovecot/report-spam.sieve
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Spam
imapsieve_mailbox2_causes = COPY APPEND
imapsieve_mailbox2_before = file:/etc/dovecot/report-ham.sieve
}

View file

@ -1,17 +0,0 @@
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables", "vnd.dovecot.debug"];
if environment :matches "imap.mailbox" "*" {
set "mailbox" "${1}";
}
if string "${mailbox}" "Trash" {
stop;
}
if environment :matches "imap.user" "*" {
set "username" "${1}";
}
pipe :copy "sa-learn" [ "--ham", "-u", "debian-spamd" ];
debug_log "ham reported by ${username}";

View file

@ -1,9 +0,0 @@
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables", "vnd.dovecot.debug"];
if environment :matches "imap.user" "*" {
set "username" "${1}";
}
pipe :copy "sa-learn" [ "--spam", "-u", "debian-spamd"];
debug_log "spam reported by ${username}";

View file

@ -1,83 +0,0 @@
server {
listen 8080;
server_name default_server;
root /usr/lib/GNUstep/SOGo/WebServerResources/;
## requirement to create new calendars in Thunderbird ##
proxy_http_version 1.1;
# Message size limit
client_max_body_size 50m;
client_body_buffer_size 128k;
location = / {
rewrite ^ '/SOGo';
allow all;
}
location = /principals/ {
rewrite ^ '/SOGo/dav';
allow all;
}
location ^~/SOGo {
proxy_pass 'http://127.0.0.1:20000';
proxy_redirect 'http://127.0.0.1:20000' default;
# forward user's IP address
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
proxy_set_header x-webobjects-remote-host 127.0.0.1;
proxy_set_header x-webobjects-server-name $server_name;
proxy_set_header x-webobjects-server-url $scheme://$host;
proxy_set_header x-webobjects-server-port $server_port;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
break;
}
location /SOGo.woa/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
allow all;
expires max;
}
location /SOGo/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
allow all;
expires max;
}
location (^/SOGo/so/ControlPanel/Products/([^/]*)/Resources/(.*)$) {
alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
expires max;
}
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) {
alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
expires max;
}
location ^~ /Microsoft-Server-ActiveSync {
access_log /var/log/nginx/activesync.log;
error_log /var/log/nginx/activesync-error.log;
proxy_connect_timeout 75;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
proxy_buffers 64 256k;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync;
proxy_redirect http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync /;
}
}

View file

@ -1 +0,0 @@
RSA_PRIVATE_KEY dkim

View file

@ -1 +0,0 @@
SSL_CERT dovecot deuxfleurs.fr

View file

@ -1 +0,0 @@
SSL_KEY dovecot

View file

@ -1 +0,0 @@
SERVICE_DN dovecot Dovecot IMAP server

View file

@ -1 +0,0 @@
SERVICE_PASSWORD dovecot

View file

@ -1 +0,0 @@
SSL_CERT postfix deuxfleurs.fr

View file

@ -1 +0,0 @@
SSL_KEY postfix

View file

@ -1 +0,0 @@
SERVICE_DN sogo SoGo email frontend

View file

@ -1 +0,0 @@
SERVICE_PASSWORD sogo

View file

@ -1 +0,0 @@
USER SoGo postgres auth (format: sogo:<password>) (TODO: replace this with two separate files and change template)

View file

@ -1,30 +0,0 @@
block_size = 1048576
metadata_dir = "/garage/meta"
data_dir = "/garage/data"
rpc_bind_addr = "[::]:3901"
consul_host = "consul.service.2.cluster.deuxfleurs.fr:8500"
consul_service_name = "garage-rpc"
bootstrap_peers = []
max_concurrent_rpc_requests = 12
data_replication_factor = 3
meta_replication_factor = 3
meta_epidemic_fanout = 3
[rpc_tls]
ca_cert = "/garage/garage-ca.crt"
node_cert = "/garage/garage.crt"
node_key = "/garage/garage.key"
[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
[s3_web]
bind_addr = "[::]:3902"
root_domain = ".web.deuxfleurs.fr"
index = "index.html"

View file

@ -1,102 +0,0 @@
job "garage" {
datacenters = ["dc1", "belair", "saturne"]
type = "system"
priority = 40
constraint {
attribute = "${attr.cpu.arch}"
value = "amd64"
}
group "garage" {
network {
port "s3" { static = 3900 }
port "rpc" { static = 3901 }
port "web" { static = 3902 }
}
task "server" {
driver = "docker"
config {
advertise_ipv6_address = true
image = "lxpz/garage_amd64:v0.1.1b"
network_mode = "host"
volumes = [
"/mnt/storage/garage/data:/garage/data",
"/mnt/ssd/garage/meta:/garage/meta",
"secrets/garage.toml:/garage/config.toml",
"secrets/garage-ca.crt:/garage/garage-ca.crt",
"secrets/garage.crt:/garage/garage.crt",
"secrets/garage.key:/garage/garage.key",
]
}
template {
data = file("../config/garage.toml")
destination = "secrets/garage.toml"
}
# --- secrets ---
template {
data = "{{ key \"secrets/garage/garage-ca.crt\" }}"
destination = "secrets/garage-ca.crt"
}
template {
data = "{{ key \"secrets/garage/garage.crt\" }}"
destination = "secrets/garage.crt"
}
template {
data = "{{ key \"secrets/garage/garage.key\" }}"
destination = "secrets/garage.key"
}
resources {
memory = 500
cpu = 1000
}
service {
tags = [
"garage_api",
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:garage.deuxfleurs.fr"
]
port = 3900
address_mode = "driver"
name = "garage-api"
check {
type = "tcp"
port = 3900
address_mode = "driver"
interval = "60s"
timeout = "5s"
check_restart {
limit = 3
grace = "90s"
ignore_warnings = false
}
}
}
service {
tags = ["garage-rpc"]
port = 3901
address_mode = "driver"
name = "garage-rpc"
check {
type = "tcp"
port = 3901
address_mode = "driver"
interval = "60s"
timeout = "5s"
check_restart {
limit = 3
grace = "90s"
ignore_warnings = false
}
}
}
}
}
}

View file

@ -1 +0,0 @@
USER_LONG garage-ca.crt (generated with Garage's genkeys.sh script)

View file

@ -1 +0,0 @@
USER_LONG garage-ca.key (generated with Garage's genkeys.sh script)

View file

@ -1 +0,0 @@
USER_LONG garage.crt (generated with Garage's genkeys.sh script)

View file

@ -1 +0,0 @@
USER_LONG garage.key (generated with Garage's genkeys.sh script)

View file

@ -1,49 +0,0 @@
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://im.deuxfleurs.fr",
"server_name": "deuxfleurs.fr"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"disable_custom_urls": false,
"disable_guests": false,
"disable_login_language_selector": false,
"disable_3pid_login": false,
"brand": "Deuxfleurs",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
"defaultCountryCode": "FR",
"showLabsSettings": true,
"features": {
"feature_new_spinner": true,
"feature_groups": "labs",
"feature_pinning": "labs"
},
"default_federate": true,
"default_theme": "light",
"roomDirectory": {
"servers": [
"deuxfleurs.fr",
"matrix.org",
"tedomum.net",
"zinz.dev"
]
},
"settingDefaults": {
"breadcrumbs": true
},
"jitsi": {
"preferredDomain": "jitsi.deuxfleurs.fr"
}
}

View file

@ -1 +0,0 @@
USER coturn static-auth (what is this?)

View file

@ -1 +0,0 @@
USER Serveur coturn (TURN/STUN) d'Adrien, c'est un jeton d'identification.

View file

@ -1 +0,0 @@
CMD openssl rand -hex 32

View file

@ -1 +0,0 @@
SERVICE_PASSWORD easybridge

View file

@ -1 +0,0 @@
CONST easybridge

View file

@ -1 +0,0 @@
CMD openssl rand -hex 32

View file

@ -1,2 +0,0 @@
CMD openssl rand -hex 32

View file

@ -1 +0,0 @@
CMD openssl rand -hex 32

View file

@ -1 +0,0 @@
USER fb2mx database URL, format: postgres://username:password@hostname/dbname

View file

@ -1 +0,0 @@
CMD openssl rand -hex 32

View file

@ -1 +0,0 @@
USER Synapse homeserver ed25519 signing key

View file

@ -1 +0,0 @@
SSL_CERT synapse im.deuxfleurs.fr

View file

@ -1 +0,0 @@
USER_LONG DH parameters for matrix ssl key? how does this work?

View file

@ -1 +0,0 @@
SSL_KEY synapse im.deuxfleurs.fr

Some files were not shown because too many files have changed in this diff Show more