forked from Deuxfleurs/infrastructure
Compare commits
10 commits
Author | SHA1 | Date | |
---|---|---|---|
Alex | edb0a3737a | ||
Alex | 24118ab426 | ||
Alex | 65af077d5a | ||
Alex | d3ada90d83 | ||
Alex | 3bf830713f | ||
Alex | 207d1fa278 | ||
Alex | bee7e10256 | ||
Alex | a4f9aa2d98 | ||
Alex | 1a16fc7f9e | ||
Alex | 3174179100 |
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -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
|
||||
|
|
82
README.md
82
README.md
|
@ -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
5
administratif/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
*.aux
|
||||
*.fdb_latexmk
|
||||
*.fls
|
||||
*.log
|
||||
*.pdf
|
68
administratif/2020-01-13-pv-ag.tex
Normal file
68
administratif/2020-01-13-pv-ag.tex
Normal 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 l’assemblé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 l’association 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.
|
||||
|
||||
L’assemblé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 l’association et l’état des actes passés pour le compte de l’association en formation.
|
||||
|
||||
Puis il rappelle que l’assemblée générale constitutive est appelée à statuer sur l’ordre du jour suivant :
|
||||
|
||||
\begin{itemize}
|
||||
\item présentation du projet de constitution de l’association ;
|
||||
\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 l’association et commente le projet de statuts.
|
||||
Il ouvre la discussion. Un débat s’instaure entre les membres de l’assemblé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} L’assemblée générale adopte les statuts dont le projet lui a été soumis.
|
||||
Cette délibération est adoptée à l’unanimité.
|
||||
|
||||
\paragraph{2\ieme~délibération} L’assemblé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 l’assemblée générale qui sera appelée à statuer sur les comptes de l’exercice 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}
|
104
administratif/2020-01-13-statuts.tex
Normal file
104
administratif/2020-01-13-statuts.tex
Normal 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.
|
||||
L’assemblée générale extraordinaire est convoquée par le conseil d’administration, à 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 d’administration anime l’assemblée générale.
|
||||
L’assemblée générale, après avoir délibéré, se prononce sur le rapport moral et/ou d'activités.
|
||||
Le conseil d’administration rend compte de l'exercice financier clos et soumet le bilan de l’exercice clos à l’approbation de l’assemblée dans un délai de six mois après la clôture des comptes.
|
||||
L’assemblée générale délibère sur les orientations à venir et se prononce sur le budget prévisionnel de l’anné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 à l’association sous réserve d’un accord tacite ou d’une autorisation écrite de leurs parents ou tuteurs légaux.
|
||||
Ils sont membres à part entière de l’association.
|
||||
Seuls les membres âgés de 16 ans au moins au jour d’une é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 à l’article~\ref{article:ag}.
|
||||
Tous les membres de l’association à 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 d’administration met en œuvre les décisions de l’assemblée générale, organise et anime la vie de l’association, 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 l’association et décidé par le conseil d’administration.
|
||||
Tous les membres du conseil d’administration sont responsables des engagements contractés par l’association.
|
||||
Tout contrat ou convention passé entre l’association 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 d’administration 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
3
administratif/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Documents administatifs
|
||||
|
||||
__Statuts__ : Pour compiler les statuts, faites `latexmk -pdf statuts.tex`
|
71
ansible/README.md
Normal file
71
ansible/README.md
Normal 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
6
ansible/lxvm
Normal 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
4
ansible/production
Normal 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
|
|
@ -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
|
|
@ -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
|
31
ansible/roles/consul/templates/consul.json.j2
Normal file
31
ansible/roles/consul/templates/consul.json.j2
Normal 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"
|
||||
}
|
||||
}
|
2
ansible/roles/consul/templates/resolv.conf.j2
Normal file
2
ansible/roles/consul/templates/resolv.conf.j2
Normal file
|
@ -0,0 +1,2 @@
|
|||
nameserver {{ vpn_ip }}
|
||||
nameserver {{ dns_server }}
|
12
ansible/roles/network/files/rules.v6
Normal file
12
ansible/roles/network/files/rules.v6
Normal 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
|
||||
|
5
ansible/roles/network/handlers/main.yml
Normal file
5
ansible/roles/network/handlers/main.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
- name: reload wireguard
|
||||
service:
|
||||
name: wg-quick@wgdeuxfleurs
|
||||
state: restarted
|
59
ansible/roles/network/tasks/main.yml
Normal file
59
ansible/roles/network/tasks/main.yml
Normal 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
|
|
@ -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
|
20
ansible/roles/network/templates/wireguard.conf.j2
Normal file
20
ansible/roles/network/templates/wireguard.conf.j2
Normal 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 %}
|
27
ansible/roles/network/templates/wireguard_external.conf.j2
Normal file
27
ansible/roles/network/templates/wireguard_external.conf.j2
Normal 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 %}
|
6
ansible/roles/network/vars/main.yml
Normal file
6
ansible/roles/network/vars/main.yml
Normal 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"
|
|
@ -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
|
||||
|
46
ansible/roles/nomad/templates/nomad.hcl.j2
Normal file
46
ansible/roles/nomad/templates/nomad.hcl.j2
Normal 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 }}"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
2
app/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
env/
|
||||
__pycache__
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
USER_LONG Private ed25519 key of the container doing the backup
|
|
@ -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)
|
|
@ -1 +0,0 @@
|
|||
USER Directory where to store backups on target host
|
|
@ -1 +0,0 @@
|
|||
USER SSH fingerprint of the target machine (format: copy here the corresponding line from your known_hosts file)
|
|
@ -1 +0,0 @@
|
|||
USER Hostname of the backup target host
|
|
@ -1 +0,0 @@
|
|||
USER SSH port number to connect to the target host
|
|
@ -1 +0,0 @@
|
|||
USER SSH username to log in as on the target host
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
CMD head -c 10 /dev/urandom | base64
|
|
@ -1 +0,0 @@
|
|||
CONST this is a constant
|
|
@ -1,5 +0,0 @@
|
|||
CONST_LONG
|
||||
this is a
|
||||
constant
|
||||
on several
|
||||
lines
|
|
@ -1 +0,0 @@
|
|||
SERVICE_DN dummy Dummy service for testing secretmgr.py
|
|
@ -1 +0,0 @@
|
|||
SERVICE_PASSWORD dummy
|
|
@ -1 +0,0 @@
|
|||
USER Test user value
|
|
@ -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"]
|
|
@ -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
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
require ["fileinto", "mailbox"];
|
||||
if header :contains "X-Spam-Flag" "YES" {
|
||||
fileinto :create "Junk";
|
||||
}
|
||||
|
|
@ -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}
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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}";
|
||||
|
|
@ -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}";
|
||||
|
|
@ -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 /;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
RSA_PRIVATE_KEY dkim
|
|
@ -1 +0,0 @@
|
|||
SSL_CERT dovecot deuxfleurs.fr
|
|
@ -1 +0,0 @@
|
|||
SSL_KEY dovecot
|
|
@ -1 +0,0 @@
|
|||
SERVICE_DN dovecot Dovecot IMAP server
|
|
@ -1 +0,0 @@
|
|||
SERVICE_PASSWORD dovecot
|
|
@ -1 +0,0 @@
|
|||
SSL_CERT postfix deuxfleurs.fr
|
|
@ -1 +0,0 @@
|
|||
SSL_KEY postfix
|
|
@ -1 +0,0 @@
|
|||
SERVICE_DN sogo SoGo email frontend
|
|
@ -1 +0,0 @@
|
|||
SERVICE_PASSWORD sogo
|
|
@ -1 +0,0 @@
|
|||
USER SoGo postgres auth (format: sogo:<password>) (TODO: replace this with two separate files and change template)
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
USER_LONG garage-ca.crt (generated with Garage's genkeys.sh script)
|
|
@ -1 +0,0 @@
|
|||
USER_LONG garage-ca.key (generated with Garage's genkeys.sh script)
|
|
@ -1 +0,0 @@
|
|||
USER_LONG garage.crt (generated with Garage's genkeys.sh script)
|
|
@ -1 +0,0 @@
|
|||
USER_LONG garage.key (generated with Garage's genkeys.sh script)
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
USER coturn static-auth (what is this?)
|
|
@ -1 +0,0 @@
|
|||
USER Serveur coturn (TURN/STUN) d'Adrien, c'est un jeton d'identification.
|
|
@ -1 +0,0 @@
|
|||
CMD openssl rand -hex 32
|
|
@ -1 +0,0 @@
|
|||
SERVICE_PASSWORD easybridge
|
|
@ -1 +0,0 @@
|
|||
CONST easybridge
|
|
@ -1 +0,0 @@
|
|||
CMD openssl rand -hex 32
|
|
@ -1,2 +0,0 @@
|
|||
CMD openssl rand -hex 32
|
||||
|
|
@ -1 +0,0 @@
|
|||
CMD openssl rand -hex 32
|
|
@ -1 +0,0 @@
|
|||
USER fb2mx database URL, format: postgres://username:password@hostname/dbname
|
|
@ -1 +0,0 @@
|
|||
CMD openssl rand -hex 32
|
|
@ -1 +0,0 @@
|
|||
USER Synapse homeserver ed25519 signing key
|
|
@ -1 +0,0 @@
|
|||
SSL_CERT synapse im.deuxfleurs.fr
|
|
@ -1 +0,0 @@
|
|||
USER_LONG DH parameters for matrix ssl key? how does this work?
|
|
@ -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
Loading…
Reference in a new issue