forked from Deuxfleurs/infrastructure
Compare commits
166 commits
Author | SHA1 | Date | |
---|---|---|---|
24dcc09695 | |||
d286da23d8 | |||
9a263b762b | |||
8f0cb24246 | |||
Quentin | 982efd1b49 | ||
Quentin | 5b53cf1673 | ||
Quentin | 47bcdaaf0d | ||
Quentin | 0e848bb2d0 | ||
Quentin | 4809e27220 | ||
Quentin | 7b57ff72a9 | ||
Quentin | ebb772e5ba | ||
Quentin | 07765e8456 | ||
Quentin | 6adb551db4 | ||
Quentin | 3e7dc8b49d | ||
Quentin | 031f31e91e | ||
Quentin | 5dfca7a713 | ||
Quentin | bd9c854a12 | ||
Quentin | d3a3867180 | ||
Quentin | b879be2156 | ||
Quentin | 46dce5d917 | ||
Quentin | 6b91db048d | ||
Quentin | 8eaa7914d0 | ||
Quentin | 2a0e9720b7 | ||
Alex | 2e25e150d4 | ||
Alex | a2eec38de4 | ||
Alex | 1c814f002a | ||
Alex | 9560f80852 | ||
Alex | a847a9683f | ||
6e1940061a | |||
Quentin | af2b8b06ba | ||
98280c8628 | |||
2a346f5430 | |||
65421d947e | |||
Quentin | eb925049ac | ||
Quentin | 0be20b22a6 | ||
Quentin | 7e637a070c | ||
Quentin | 2c2efdc276 | ||
Quentin | 6c8c861dd5 | ||
Quentin | ad6017eea0 | ||
Quentin | c642370def | ||
Quentin | cffd902815 | ||
Quentin | 79b7273ff2 | ||
Alex | 850ccbf1c7 | ||
Alex | d4d0b100ad | ||
Alex | c74dc92feb | ||
Alex | 0c4ee40e01 | ||
Alex | a6b23f5713 | ||
Quentin | 52c141e5fc | ||
Quentin | 464b990e19 | ||
Quentin | 969ee58b7d | ||
Quentin | 4456fb56c1 | ||
Quentin | ba3d84a1de | ||
a5a56b6f70 | |||
Quentin | 7508a10a71 | ||
Quentin | c4c4d6f8a6 | ||
Quentin | fc518df1c1 | ||
Quentin | a2f8e11d06 | ||
Quentin | 48db0185a4 | ||
Quentin | 4f23adfbb9 | ||
Quentin | 1624b348df | ||
Quentin | 8625a9af75 | ||
Quentin | f75497af11 | ||
Quentin | 6913655316 | ||
Quentin | 80dc6ec803 | ||
Quentin | 9117616f02 | ||
Quentin | b29028405d | ||
Quentin | 9f6f0fb53c | ||
Quentin | a2adaa2101 | ||
Quentin | bb5a82b056 | ||
Quentin | e628dc44ba | ||
Quentin | 846449b238 | ||
Quentin | b6ccf06d8a | ||
Quentin | 685bc45802 | ||
Quentin | 55f93cc5ad | ||
Quentin | 41e33f40ad | ||
94ee5d3e5c | |||
91ffdc732c | |||
Quentin | 3ff113ceab | ||
bcb3964417 | |||
ad064dddbc | |||
2b3df5b6ee | |||
Quentin | 9c947a458f | ||
Quentin | e370380a3f | ||
Quentin | d1332a2d42 | ||
Quentin | 6402119511 | ||
Quentin | 365849760d | ||
adrien | de3e21101d | ||
da1d381068 | |||
fd38cbf744 | |||
Quentin | d241948034 | ||
Quentin | e2bb0e1b4e | ||
Quentin | cfab2346cf | ||
Quentin | f544c202be | ||
Quentin | 804078b3f4 | ||
Quentin | 9f41d95dcf | ||
Quentin | 33f769c747 | ||
Quentin | c19cadf353 | ||
Alex | 1bb9c7ce19 | ||
Alex | f931dd939c | ||
Alex | e2a0c40e6b | ||
Alex | 2051a21662 | ||
Quentin | f14777e1b6 | ||
Alex | 7e111783fe | ||
Alex | e1f171e19c | ||
Quentin | 9981ea0286 | ||
Quentin | 0191926455 | ||
Quentin | 2452e87509 | ||
Quentin | bf58bd2a2c | ||
Quentin | ed3ed5e2e4 | ||
Quentin | c32bd6df1d | ||
Quentin | 03680a992b | ||
Quentin | aba3ba723c | ||
Quentin | f9013d9ca5 | ||
Quentin | 9fef7ae777 | ||
Quentin | e74737e6e3 | ||
Quentin | 1f53e2061e | ||
Quentin | d8d0d74920 | ||
Quentin | 2ef6ab1881 | ||
Quentin | f4a88fa565 | ||
Quentin | 2557793cee | ||
Quentin | bf9a9128b8 | ||
Quentin | 5902805ac9 | ||
Quentin | e465d65a27 | ||
Quentin | 3b75213d40 | ||
Quentin | b53b71f750 | ||
Quentin | 6858f17766 | ||
Quentin | 5c31fbf0b1 | ||
Quentin | 3ef7b6775b | ||
Quentin | e4c15e9d71 | ||
Quentin | 6b667af32b | ||
Quentin | 4af75bd8b8 | ||
Quentin | 25ec221248 | ||
Quentin | fcbb788de6 | ||
Quentin | 948e4fb94e | ||
Alex | 8fb283d502 | ||
Alex | cc57e0b353 | ||
Quentin | c5eee91b12 | ||
Quentin | 3afe80b158 | ||
Quentin | 9460862c18 | ||
Quentin | 6467a5ab31 | ||
Quentin | 9e4e2f7b99 | ||
Quentin | f8682668c2 | ||
Quentin | 09fc30214d | ||
Quentin | e9bc6fe7f1 | ||
Adrien | 6b1a7127ce | ||
Quentin | 484c3fe667 | ||
Quentin | d5bfc38fe8 | ||
Quentin | d76d82fccb | ||
Quentin | a1be6b31ed | ||
Quentin | da034dabfc | ||
Quentin | 744fa8b8c9 | ||
Quentin | c40095d02c | ||
Quentin | 7951d35035 | ||
Quentin | 1af6eabc81 | ||
Quentin | 51e4af08c0 | ||
Quentin | a23e08ce20 | ||
Quentin | fb4ffbc7fa | ||
Quentin | b00fc0eaf1 | ||
Quentin | 0c05730a5d | ||
Quentin | 5337be94df | ||
Quentin | c4a6cf1534 | ||
Quentin | 0550647b93 | ||
Quentin | 30fe6d2e3c | ||
Quentin | 72b84fbe18 | ||
Quentin | bdff5571f1 | ||
Quentin | c29d660700 |
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,5 +1,5 @@
|
|||
[submodule "docker/static/goStatic"]
|
||||
path = docker/static/goStatic
|
||||
path = app/build/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,42 +5,53 @@ 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 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:
|
||||
|
||||
* 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)
|
||||
* **[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
|
||||
|
||||
Some services we provide:
|
||||
|
||||
* Chat (Matrix/Riot)
|
||||
* Email (Postfix/Dovecot/Sogo)
|
||||
* Storage (Seafile)
|
||||
* **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
|
||||
|
||||
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.*
|
||||
*The following instructions are provided for ops that already have access to the servers (meaning: their SSH public key is known by the cluster).*
|
||||
|
||||
Deploy Nomad on your machine:
|
||||
|
||||
```bash
|
||||
export NOMAD_VER=0.9.1
|
||||
export NOMAD_VER=1.0.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
|
||||
|
@ -50,7 +61,7 @@ rm nomad_${NOMAD_VER}_linux_amd64.zip
|
|||
Deploy Consul on your machine:
|
||||
|
||||
```bash
|
||||
export CONSUL_VER=1.5.1
|
||||
export CONSUL_VER=1.9.0
|
||||
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
|
||||
|
@ -63,14 +74,37 @@ 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:
|
||||
|
||||
```
|
||||
bind_df
|
||||
# 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
|
||||
```
|
||||
|
||||
Now, to connect, do the following:
|
||||
|
||||
ssh deuxfleurs -N
|
||||
|
|
5
administratif/.gitignore
vendored
5
administratif/.gitignore
vendored
|
@ -1,5 +0,0 @@
|
|||
*.aux
|
||||
*.fdb_latexmk
|
||||
*.fls
|
||||
*.log
|
||||
*.pdf
|
|
@ -1,68 +0,0 @@
|
|||
\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}
|
|
@ -1,104 +0,0 @@
|
|||
\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}
|
|
@ -1,3 +0,0 @@
|
|||
# Documents administatifs
|
||||
|
||||
__Statuts__ : Pour compiler les statuts, faites `latexmk -pdf statuts.tex`
|
|
@ -1,15 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[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
|
|
@ -1,2 +0,0 @@
|
|||
nameserver {{ private_ip }}
|
||||
nameserver {{ dns_server }}
|
|
@ -1,12 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
- 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
|
2
app/.gitignore
vendored
Normal file
2
app/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
env/
|
||||
__pycache__
|
66
app/README.md
Normal file
66
app/README.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
# 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
|
28
app/backup/build/backup-consul/Dockerfile
Normal file
28
app/backup/build/backup-consul/Dockerfile
Normal file
|
@ -0,0 +1,28 @@
|
|||
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"
|
||||
|
20
app/backup/build/backup-consul/do_backup.sh
Executable file
20
app/backup/build/backup-consul/do_backup.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/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"
|
||||
|
67
app/backup/deploy/backup.hcl
Normal file
67
app/backup/deploy/backup.hcl
Normal file
|
@ -0,0 +1,67 @@
|
|||
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
app/backup/secrets/backup/id_ed25519
Normal file
1
app/backup/secrets/backup/id_ed25519
Normal file
|
@ -0,0 +1 @@
|
|||
USER_LONG Private ed25519 key of the container doing the backup
|
1
app/backup/secrets/backup/id_ed25519.pub
Normal file
1
app/backup/secrets/backup/id_ed25519.pub
Normal file
|
@ -0,0 +1 @@
|
|||
USER Public ed25519 key of the container doing the backup (this key must be in authorized_keys on the backup target host)
|
1
app/backup/secrets/backup/target_ssh_dir
Normal file
1
app/backup/secrets/backup/target_ssh_dir
Normal file
|
@ -0,0 +1 @@
|
|||
USER Directory where to store backups on target host
|
1
app/backup/secrets/backup/target_ssh_fingerprint
Normal file
1
app/backup/secrets/backup/target_ssh_fingerprint
Normal file
|
@ -0,0 +1 @@
|
|||
USER SSH fingerprint of the target machine (format: copy here the corresponding line from your known_hosts file)
|
1
app/backup/secrets/backup/target_ssh_host
Normal file
1
app/backup/secrets/backup/target_ssh_host
Normal file
|
@ -0,0 +1 @@
|
|||
USER Hostname of the backup target host
|
1
app/backup/secrets/backup/target_ssh_port
Normal file
1
app/backup/secrets/backup/target_ssh_port
Normal file
|
@ -0,0 +1 @@
|
|||
USER SSH port number to connect to the target host
|
1
app/backup/secrets/backup/target_ssh_user
Normal file
1
app/backup/secrets/backup/target_ssh_user
Normal file
|
@ -0,0 +1 @@
|
|||
USER SSH username to log in as on the target host
|
|
@ -1,6 +1,7 @@
|
|||
job "core" {
|
||||
datacenters = ["dc1"]
|
||||
type = "system"
|
||||
priority = 90
|
||||
|
||||
constraint {
|
||||
attribute = "${attr.cpu.arch}"
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"suffix": "dc=deuxfleurs,dc=fr",
|
||||
"bind": "0.0.0.0:1389",
|
||||
"bind": "0.0.0.0:389",
|
||||
"consul_host": "http://consul.service.2.cluster.deuxfleurs.fr:8500",
|
||||
"log_level": "debug",
|
||||
"acl": [
|
|
@ -1,6 +1,7 @@
|
|||
job "directory2" {
|
||||
job "directory" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
priority = 90
|
||||
|
||||
constraint {
|
||||
attribute = "${attr.cpu.arch}"
|
||||
|
@ -9,14 +10,21 @@ job "directory2" {
|
|||
|
||||
group "bottin" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
port "ldap_port" {
|
||||
static = 389
|
||||
to = 389
|
||||
}
|
||||
}
|
||||
|
||||
task "bottin" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "lxpz/bottin_amd64:14"
|
||||
image = "lxpz/bottin_amd64:20"
|
||||
network_mode = "host"
|
||||
readonly_rootfs = true
|
||||
port_map {
|
||||
ldap_port = 1389
|
||||
}
|
||||
ports = [ "ldap_port" ]
|
||||
volumes = [
|
||||
"secrets/config.json:/config.json"
|
||||
]
|
||||
|
@ -24,15 +32,10 @@ job "directory2" {
|
|||
|
||||
resources {
|
||||
memory = 100
|
||||
network {
|
||||
port "ldap_port" {
|
||||
static = "389"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
data = "{{ key \"configuration/directory/bottin/config.json\" }}"
|
||||
data = file("../config/bottin/config.json")
|
||||
destination = "secrets/config.json"
|
||||
}
|
||||
|
||||
|
@ -58,34 +61,29 @@ job "directory2" {
|
|||
|
||||
group "guichet" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
port "web_port" { to = 9991 }
|
||||
}
|
||||
|
||||
task "guichet" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "lxpz/guichet_amd64:10"
|
||||
readonly_rootfs = true
|
||||
port_map {
|
||||
web_port = 9991
|
||||
}
|
||||
ports = [ "web_port" ]
|
||||
volumes = [
|
||||
"secrets/config.json:/config.json"
|
||||
]
|
||||
}
|
||||
|
||||
artifact {
|
||||
source = "http://127.0.0.1:8500/v1/kv/configuration/directory/guichet/config.json.tpl?raw"
|
||||
destination = "secrets/config.json.tpl"
|
||||
mode = "file"
|
||||
}
|
||||
template {
|
||||
source = "secrets/config.json.tpl"
|
||||
data = file("../config/guichet/config.json.tpl")
|
||||
destination = "secrets/config.json"
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 200
|
||||
network {
|
||||
port "web_port" {}
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
94
app/docker-compose.yml
Normal file
94
app/docker-compose.yml
Normal file
|
@ -0,0 +1,94 @@
|
|||
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
app/dummy/secrets/dummy/test_cmd
Normal file
1
app/dummy/secrets/dummy/test_cmd
Normal file
|
@ -0,0 +1 @@
|
|||
CMD head -c 10 /dev/urandom | base64
|
1
app/dummy/secrets/dummy/test_const
Normal file
1
app/dummy/secrets/dummy/test_const
Normal file
|
@ -0,0 +1 @@
|
|||
CONST this is a constant
|
5
app/dummy/secrets/dummy/test_const_long
Normal file
5
app/dummy/secrets/dummy/test_const_long
Normal file
|
@ -0,0 +1,5 @@
|
|||
CONST_LONG
|
||||
this is a
|
||||
constant
|
||||
on several
|
||||
lines
|
1
app/dummy/secrets/dummy/test_service_dn
Normal file
1
app/dummy/secrets/dummy/test_service_dn
Normal file
|
@ -0,0 +1 @@
|
|||
SERVICE_DN dummy Dummy service for testing secretmgr.py
|
1
app/dummy/secrets/dummy/test_service_password
Normal file
1
app/dummy/secrets/dummy/test_service_password
Normal file
|
@ -0,0 +1 @@
|
|||
SERVICE_PASSWORD dummy
|
1
app/dummy/secrets/dummy/test_user
Normal file
1
app/dummy/secrets/dummy/test_user
Normal file
|
@ -0,0 +1 @@
|
|||
USER Test user value
|
21
app/email/build/alps/Dockerfile
Normal file
21
app/email/build/alps/Dockerfile
Normal file
|
@ -0,0 +1,21 @@
|
|||
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"]
|
55
app/email/build/alps/skipverify.patch
Normal file
55
app/email/build/alps/skipverify.patch
Normal file
|
@ -0,0 +1,55 @@
|
|||
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
|
||||
|
5
app/email/build/dovecot/conf/all_before.sieve
Normal file
5
app/email/build/dovecot/conf/all_before.sieve
Normal file
|
@ -0,0 +1,5 @@
|
|||
require ["fileinto", "mailbox"];
|
||||
if header :contains "X-Spam-Flag" "YES" {
|
||||
fileinto :create "Junk";
|
||||
}
|
||||
|
8
app/email/build/dovecot/conf/dovecot-ldap.sample.conf
Normal file
8
app/email/build/dovecot/conf/dovecot-ldap.sample.conf
Normal file
|
@ -0,0 +1,8 @@
|
|||
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}
|
79
app/email/build/dovecot/conf/dovecot.conf
Normal file
79
app/email/build/dovecot/conf/dovecot.conf
Normal file
|
@ -0,0 +1,79 @@
|
|||
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
|
||||
|
||||
}
|
||||
|
17
app/email/build/dovecot/conf/report-ham.sieve
Normal file
17
app/email/build/dovecot/conf/report-ham.sieve
Normal file
|
@ -0,0 +1,17 @@
|
|||
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}";
|
||||
|
9
app/email/build/dovecot/conf/report-spam.sieve
Normal file
9
app/email/build/dovecot/conf/report-spam.sieve
Normal file
|
@ -0,0 +1,9 @@
|
|||
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,8 +1,10 @@
|
|||
FROM amd64/debian:buster
|
||||
|
||||
ARG VERSION
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
postfix \
|
||||
postfix=$VERSION \
|
||||
postfix-ldap
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint
|
|
@ -26,5 +26,6 @@ for file in $(ls /etc/postfix-conf); do
|
|||
done
|
||||
|
||||
echo ${MAILNAME} > /etc/mailname
|
||||
postmap /etc/postfix/transport
|
||||
|
||||
exec "$@"
|
|
@ -1,6 +1,6 @@
|
|||
#FROM amd64/debian:stretch as builder
|
||||
|
||||
FROM amd64/debian:stretch
|
||||
FROM amd64/debian:buster
|
||||
|
||||
RUN mkdir ~/.gnupg && echo "disable-ipv6" >> ~/.gnupg/dirmngr.conf
|
||||
|
||||
|
@ -8,7 +8,7 @@ RUN apt-get update && \
|
|||
apt-get install -y apt-transport-https gnupg2 sudo nginx && \
|
||||
rm -rf /etc/nginx/sites-enabled/* && \
|
||||
apt-key adv --keyserver keys.gnupg.net --recv-key 0x810273C4 && \
|
||||
echo "deb https://packages.inverse.ca/SOGo/nightly/4/debian stretch stretch" > /etc/apt/sources.list.d/sogo.list && \
|
||||
echo "deb http://packages.inverse.ca/SOGo/nightly/5/debian/ buster buster" > /etc/apt/sources.list.d/sogo.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y sogo sogo-activesync sope4.9-gdl1-postgresql postgresql-client
|
||||
|
83
app/email/build/sogo/sogo.nginx.conf
Normal file
83
app/email/build/sogo/sogo.nginx.conf
Normal file
|
@ -0,0 +1,83 @@
|
|||
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,6 +1,7 @@
|
|||
{
|
||||
WONoDetach = NO;
|
||||
WOWorkersCount = 10;
|
||||
WOWorkersCount = 3;
|
||||
SxVMemLimit = 300;
|
||||
WOPort = "127.0.0.1:20000";
|
||||
SOGoProfileURL = "postgresql://{{ key "secrets/email/sogo/postgre_auth" | trimSpace }}@psql-proxy.service.2.cluster.deuxfleurs.fr:5432/sogo/sogo_user_profile";
|
||||
OCSFolderInfoURL = "postgresql://{{ key "secrets/email/sogo/postgre_auth" | trimSpace }}@psql-proxy.service.2.cluster.deuxfleurs.fr:5432/sogo/sogo_folder_info";
|
||||
|
@ -23,7 +24,7 @@
|
|||
SOGoSentFolderName = Sent;
|
||||
SOGoTrashFolderName = Trash;
|
||||
SOGoDraftsFolderName = Drafts;
|
||||
SOGoIMAPServer = "imaps://dovecot-imaps.service.2.cluster.deuxfleurs.fr:993/";
|
||||
SOGoIMAPServer = "imaps://dovecot-imaps.service.2.cluster.deuxfleurs.fr:993/?tlsVerifyMode=none";
|
||||
SOGoSieveServer = "sieve://sieve.service.2.cluster.deuxfleurs.fr:4190/?tls=YES";
|
||||
SOGoIMAPAclConformsToIMAPExt = YES;
|
||||
SOGoVacationEnabled = NO;
|
|
@ -1,27 +1,43 @@
|
|||
job "email" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
priority = 65
|
||||
|
||||
group "dovecot" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
port "zauthentication_port" {
|
||||
static = 1337
|
||||
to = 1337
|
||||
}
|
||||
port "imaps_port" {
|
||||
static = 993
|
||||
to = 993
|
||||
}
|
||||
port "imap_port" {
|
||||
static = 143
|
||||
to = 143
|
||||
}
|
||||
port "lmtp_port" {
|
||||
static = 24
|
||||
to = 24
|
||||
}
|
||||
}
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "superboum/amd64_dovecot:v2"
|
||||
readonly_rootfs = false
|
||||
port_map {
|
||||
auth_port = 1337
|
||||
imaps_port = 993
|
||||
imap_port = 143
|
||||
lmtp_port = 24
|
||||
}
|
||||
ports = [ "zauthentication_port", "imaps_port", "imap_port", "lmtp_port" ]
|
||||
command = "dovecot"
|
||||
args = [ "-F" ]
|
||||
volumes = [
|
||||
"secrets/ssl/certs:/etc/ssl/certs",
|
||||
"secrets/ssl/private:/etc/ssl/private",
|
||||
"secrets/conf/dovecot-ldap.conf:/etc/dovecot/dovecot-ldap.conf",
|
||||
"secrets/conf/dovecot-ldap.conf:/etc/dovecot/dovecot-ldap.conf",
|
||||
"/mnt/glusterfs/email/mail:/var/mail/",
|
||||
]
|
||||
}
|
||||
|
@ -33,47 +49,11 @@ job "email" {
|
|||
resources {
|
||||
cpu = 100
|
||||
memory = 200
|
||||
network {
|
||||
mbits = 1
|
||||
port "auth_port" {
|
||||
static = "1337"
|
||||
}
|
||||
port "imap_port" {
|
||||
static = "143"
|
||||
}
|
||||
port "imaps_port" {
|
||||
static = "993"
|
||||
}
|
||||
port "lmtp_port" {
|
||||
static = "24"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
name = "dovecot-auth"
|
||||
port = "auth_port"
|
||||
address_mode = "host"
|
||||
tags = [
|
||||
"dovecot",
|
||||
]
|
||||
check {
|
||||
type = "tcp"
|
||||
port = "auth_port"
|
||||
interval = "60s"
|
||||
timeout = "5s"
|
||||
check_restart {
|
||||
limit = 3
|
||||
grace = "90s"
|
||||
ignore_warnings = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
name = "dovecot-imap"
|
||||
port = "imap_port"
|
||||
address_mode = "host"
|
||||
tags = [
|
||||
"dovecot"
|
||||
]
|
||||
|
@ -93,7 +73,6 @@ job "email" {
|
|||
service {
|
||||
name = "dovecot-imaps"
|
||||
port = "imaps_port"
|
||||
address_mode = "host"
|
||||
tags = [
|
||||
"dovecot",
|
||||
"(diplonat (tcp_port 993))"
|
||||
|
@ -115,7 +94,6 @@ job "email" {
|
|||
service {
|
||||
name = "dovecot-lmtp"
|
||||
port = "lmtp_port"
|
||||
address_mode = "host"
|
||||
tags = [
|
||||
"dovecot",
|
||||
]
|
||||
|
@ -133,24 +111,39 @@ job "email" {
|
|||
}
|
||||
}
|
||||
|
||||
artifact {
|
||||
source = "http://127.0.0.1:8500/v1/kv/configuration/email/dovecot/dovecot-ldap.conf.tpl?raw"
|
||||
destination = "secrets/conf/dovecot-ldap.conf.tpl"
|
||||
mode = "file"
|
||||
service {
|
||||
name = "dovecot-auth"
|
||||
port = "zauthentication_port"
|
||||
tags = [
|
||||
"dovecot",
|
||||
]
|
||||
check {
|
||||
type = "tcp"
|
||||
port = "zauthentication_port"
|
||||
interval = "60s"
|
||||
timeout = "5s"
|
||||
check_restart {
|
||||
limit = 3
|
||||
grace = "90s"
|
||||
ignore_warnings = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template {
|
||||
source = "secrets/conf/dovecot-ldap.conf.tpl"
|
||||
data = file("../config/dovecot/dovecot-ldap.conf.tpl")
|
||||
destination = "secrets/conf/dovecot-ldap.conf"
|
||||
perms = "400"
|
||||
}
|
||||
|
||||
# ----- secrets ------
|
||||
template {
|
||||
data = "{{ key \"configuration/email/dovecot/dovecot.crt\" }}"
|
||||
data = "{{ key \"secrets/email/dovecot/dovecot.crt\" }}"
|
||||
destination = "secrets/ssl/certs/dovecot.crt"
|
||||
perms = "400"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/dovecot/dovecot.key\" }}"
|
||||
data = "{{ key \"secrets/email/dovecot/dovecot.key\" }}"
|
||||
destination = "secrets/ssl/private/dovecot.key"
|
||||
perms = "400"
|
||||
}
|
||||
|
@ -159,15 +152,20 @@ job "email" {
|
|||
|
||||
group "opendkim" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
port "dkim_port" {
|
||||
static = 8999
|
||||
to = 8999
|
||||
}
|
||||
}
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "superboum/amd64_opendkim:v1"
|
||||
readonly_rootfs = false
|
||||
port_map {
|
||||
dkim_port = 8999
|
||||
}
|
||||
ports = [ "dkim_port" ]
|
||||
command = "opendkim"
|
||||
args = [ "-f", "-v", "-x", "/etc/opendkim.conf" ]
|
||||
volumes = [
|
||||
|
@ -179,12 +177,6 @@ job "email" {
|
|||
resources {
|
||||
cpu = 100
|
||||
memory = 50
|
||||
network {
|
||||
mbits = 1
|
||||
port "dkim_port" {
|
||||
static = "8999"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
|
@ -208,72 +200,69 @@ job "email" {
|
|||
}
|
||||
|
||||
template {
|
||||
data = "{{ key \"configuration/email/dkim/keytable\" }}"
|
||||
data = file("../config/dkim/keytable")
|
||||
destination = "secrets/dkim/keytable"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/dkim/signingtable\" }}"
|
||||
data = file("../config/dkim/signingtable")
|
||||
destination = "secrets/dkim/signingtable"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/dkim/smtp.private\" }}"
|
||||
data = file("../config/dkim/trusted")
|
||||
destination = "secrets/dkim/trusted"
|
||||
}
|
||||
|
||||
# --- secrets ---
|
||||
template {
|
||||
data = "{{ key \"secrets/email/dkim/smtp.private\" }}"
|
||||
destination = "secrets/dkim/smtp.private"
|
||||
perms = "600"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/dkim/smtp.txt\" }}"
|
||||
destination = "secrets/dkim/smtp.txt"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/dkim/trusted\" }}"
|
||||
destination = "secrets/dkim/trusted"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group "postfix" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
port "smtp_port" {
|
||||
static = 25
|
||||
to = 25
|
||||
}
|
||||
port "smtps_port" {
|
||||
static = 465
|
||||
to = 465
|
||||
}
|
||||
port "submission_port" {
|
||||
static = 587
|
||||
to = 587
|
||||
}
|
||||
}
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "superboum/amd64_postfix:v1"
|
||||
image = "superboum/amd64_postfix:v3"
|
||||
readonly_rootfs = false
|
||||
port_map {
|
||||
smtp_port = 25
|
||||
smtps_port = 465
|
||||
submission_port = 587
|
||||
}
|
||||
ports = [ "smtp_port", "smtps_port", "submission_port" ]
|
||||
command = "postfix"
|
||||
args = [ "start-fg" ]
|
||||
volumes = [
|
||||
"secrets/ssl/certs:/etc/ssl/certs",
|
||||
"secrets/ssl/private:/etc/ssl/private",
|
||||
"secrets/postfix:/etc/postfix-conf",
|
||||
"secrets/postfix:/etc/postfix-conf",
|
||||
"/dev/log:/dev/log"
|
||||
]
|
||||
}
|
||||
|
||||
env {
|
||||
TLSINFO = "/C=FR/ST=Bretagne/L=Rennes/O=Deuxfleurs/CN=smtp.deuxfleurs.fr"
|
||||
MAILNAME = "smtp.deuxfleurs.fr",
|
||||
MAILNAME = "smtp.deuxfleurs.fr"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 100
|
||||
memory = 200
|
||||
network {
|
||||
mbits = 1
|
||||
port "smtp_port" {
|
||||
static = "25"
|
||||
}
|
||||
port "smtps_port" {
|
||||
static = "465"
|
||||
}
|
||||
port "submission_port" {
|
||||
static = "587"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
|
@ -339,109 +328,135 @@ job "email" {
|
|||
}
|
||||
}
|
||||
|
||||
artifact {
|
||||
source = "http://127.0.0.1:8500/v1/kv/configuration/email/postfix/ldap-account.cf.tpl?raw"
|
||||
destination = "secrets/postfix/ldap-account.cf.tpl"
|
||||
mode = "file"
|
||||
}
|
||||
template {
|
||||
source = "secrets/postfix/ldap-account.cf.tpl"
|
||||
data = file("../config/postfix/ldap-account.cf.tpl")
|
||||
destination = "secrets/postfix/ldap-account.cf"
|
||||
}
|
||||
|
||||
artifact {
|
||||
source = "http://127.0.0.1:8500/v1/kv/configuration/email/postfix/ldap-alias.cf.tpl?raw"
|
||||
destination = "secrets/postfix/ldap-alias.cf.tpl"
|
||||
mode = "file"
|
||||
}
|
||||
template {
|
||||
source = "secrets/postfix/ldap-alias.cf.tpl"
|
||||
data = file("../config/postfix/ldap-alias.cf.tpl")
|
||||
destination = "secrets/postfix/ldap-alias.cf"
|
||||
}
|
||||
|
||||
artifact {
|
||||
source = "http://127.0.0.1:8500/v1/kv/configuration/email/postfix/ldap-virtual-domains.cf.tpl?raw"
|
||||
destination = "secrets/postfix/ldap-virtual-domains.cf.tpl"
|
||||
mode = "file"
|
||||
}
|
||||
template {
|
||||
source = "secrets/postfix/ldap-virtual-domains.cf.tpl"
|
||||
data = file("../config/postfix/ldap-virtual-domains.cf.tpl")
|
||||
destination = "secrets/postfix/ldap-virtual-domains.cf"
|
||||
}
|
||||
|
||||
|
||||
template {
|
||||
data = file("../config/postfix/dynamicmaps.cf")
|
||||
destination = "secrets/postfix/dynamicmaps.cf"
|
||||
}
|
||||
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/postfix.crt\" }}"
|
||||
data = file("../config/postfix/header_checks")
|
||||
destination = "secrets/postfix/header_checks"
|
||||
}
|
||||
|
||||
template {
|
||||
data = file("../config/postfix/main.cf")
|
||||
destination = "secrets/postfix/main.cf"
|
||||
}
|
||||
|
||||
template {
|
||||
data = file("../config/postfix/master.cf")
|
||||
destination = "secrets/postfix/master.cf"
|
||||
}
|
||||
|
||||
template {
|
||||
data = file("../config/postfix/transport")
|
||||
destination = "secrets/postfix/transport"
|
||||
}
|
||||
|
||||
# --- secrets ---
|
||||
template {
|
||||
data = "{{ key \"secrets/email/postfix/postfix.crt\" }}"
|
||||
destination = "secrets/ssl/certs/postfix.crt"
|
||||
perms = "400"
|
||||
}
|
||||
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/postfix.key\" }}"
|
||||
data = "{{ key \"secrets/email/postfix/postfix.key\" }}"
|
||||
destination = "secrets/ssl/private/postfix.key"
|
||||
perms = "400"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/dynamicmaps.cf\" }}"
|
||||
destination = "secrets/postfix/dynamicmaps.cf"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/header_checks\" }}"
|
||||
destination = "secrets/postfix/header_checks"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/main.cf\" }}"
|
||||
destination = "secrets/postfix/main.cf"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/master.cf\" }}"
|
||||
destination = "secrets/postfix/master.cf"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/transport\" }}"
|
||||
destination = "secrets/postfix/transport"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"configuration/email/postfix/transport.db\" }}"
|
||||
destination = "secrets/postfix/transport.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group "alps" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
port "alps_web_port" { to = 1323 }
|
||||
}
|
||||
|
||||
task "main" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_alps:v1"
|
||||
readonly_rootfs = true
|
||||
ports = [ "alps_web_port" ]
|
||||
command = "-theme"
|
||||
args = [ "alps", "imaps://imap.deuxfleurs.fr:993", "smtps://smtp.deuxfleurs.fr:465" ]
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 50
|
||||
memory = 40
|
||||
}
|
||||
|
||||
service {
|
||||
name = "alps"
|
||||
port = "alps_web_port"
|
||||
address_mode = "host"
|
||||
tags = [
|
||||
"alps",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:alps.deuxfleurs.fr"
|
||||
]
|
||||
check {
|
||||
type = "tcp"
|
||||
port = "alps_web_port"
|
||||
interval = "60s"
|
||||
timeout = "5s"
|
||||
check_restart {
|
||||
limit = 3
|
||||
grace = "5m"
|
||||
ignore_warnings = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
group "sogo" {
|
||||
count = 1
|
||||
task "bundle" {
|
||||
|
||||
driver = "docker"
|
||||
|
||||
network {
|
||||
port "sogo_web_port" { to = 8080 }
|
||||
}
|
||||
|
||||
task "bundle" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_sogo:v6"
|
||||
image = "superboum/amd64_sogo:v7"
|
||||
readonly_rootfs = false
|
||||
port_map {
|
||||
sogo_web_port = 8080
|
||||
}
|
||||
ports = [ "sogo_web_port" ]
|
||||
volumes = [
|
||||
"secrets/sogo.conf:/etc/sogo/sogo.conf",
|
||||
"secrets/sogo.conf:/etc/sogo/sogo.conf",
|
||||
]
|
||||
}
|
||||
|
||||
/* Workaround as there is no consul source and no way to template recursively... */
|
||||
artifact {
|
||||
source = "http://127.0.0.1:8500/v1/kv/configuration/email/sogo/sogo.conf.tpl?raw"
|
||||
destination = "secrets/tpl/sogo.conf.tpl"
|
||||
mode = "file"
|
||||
}
|
||||
template {
|
||||
source = "secrets/tpl/sogo.conf.tpl"
|
||||
data = file("../config/sogo/sogo.conf.tpl")
|
||||
destination = "secrets/sogo.conf"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 200
|
||||
memory = 1000
|
||||
network {
|
||||
mbits = 1
|
||||
port "sogo_web_port" {}
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
1
app/email/secrets/email/dkim/smtp.private
Normal file
1
app/email/secrets/email/dkim/smtp.private
Normal file
|
@ -0,0 +1 @@
|
|||
RSA_PRIVATE_KEY dkim
|
1
app/email/secrets/email/dovecot/dovecot.crt
Normal file
1
app/email/secrets/email/dovecot/dovecot.crt
Normal file
|
@ -0,0 +1 @@
|
|||
SSL_CERT dovecot deuxfleurs.fr
|
1
app/email/secrets/email/dovecot/dovecot.key
Normal file
1
app/email/secrets/email/dovecot/dovecot.key
Normal file
|
@ -0,0 +1 @@
|
|||
SSL_KEY dovecot
|
1
app/email/secrets/email/dovecot/ldap_binddn
Normal file
1
app/email/secrets/email/dovecot/ldap_binddn
Normal file
|
@ -0,0 +1 @@
|
|||
SERVICE_DN dovecot Dovecot IMAP server
|
1
app/email/secrets/email/dovecot/ldap_bindpwd
Normal file
1
app/email/secrets/email/dovecot/ldap_bindpwd
Normal file
|
@ -0,0 +1 @@
|
|||
SERVICE_PASSWORD dovecot
|
1
app/email/secrets/email/postfix/postfix.crt
Normal file
1
app/email/secrets/email/postfix/postfix.crt
Normal file
|
@ -0,0 +1 @@
|
|||
SSL_CERT postfix deuxfleurs.fr
|
1
app/email/secrets/email/postfix/postfix.key
Normal file
1
app/email/secrets/email/postfix/postfix.key
Normal file
|
@ -0,0 +1 @@
|
|||
SSL_KEY postfix
|
1
app/email/secrets/email/sogo/ldap_binddn
Normal file
1
app/email/secrets/email/sogo/ldap_binddn
Normal file
|
@ -0,0 +1 @@
|
|||
SERVICE_DN sogo SoGo email frontend
|
1
app/email/secrets/email/sogo/ldap_bindpw
Normal file
1
app/email/secrets/email/sogo/ldap_bindpw
Normal file
|
@ -0,0 +1 @@
|
|||
SERVICE_PASSWORD sogo
|
1
app/email/secrets/email/sogo/postgre_auth
Normal file
1
app/email/secrets/email/sogo/postgre_auth
Normal file
|
@ -0,0 +1 @@
|
|||
USER SoGo postgres auth (format: sogo:<password>) (TODO: replace this with two separate files and change template)
|
30
app/garage/config/garage.toml
Normal file
30
app/garage/config/garage.toml
Normal file
|
@ -0,0 +1,30 @@
|
|||
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"
|
102
app/garage/deploy/garage.hcl
Normal file
102
app/garage/deploy/garage.hcl
Normal file
|
@ -0,0 +1,102 @@
|
|||
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
app/garage/secrets/garage/garage-ca.crt
Normal file
1
app/garage/secrets/garage/garage-ca.crt
Normal file
|
@ -0,0 +1 @@
|
|||
USER_LONG garage-ca.crt (generated with Garage's genkeys.sh script)
|
1
app/garage/secrets/garage/garage-ca.key
Normal file
1
app/garage/secrets/garage/garage-ca.key
Normal file
|
@ -0,0 +1 @@
|
|||
USER_LONG garage-ca.key (generated with Garage's genkeys.sh script)
|
1
app/garage/secrets/garage/garage.crt
Normal file
1
app/garage/secrets/garage/garage.crt
Normal file
|
@ -0,0 +1 @@
|
|||
USER_LONG garage.crt (generated with Garage's genkeys.sh script)
|
1
app/garage/secrets/garage/garage.key
Normal file
1
app/garage/secrets/garage/garage.key
Normal file
|
@ -0,0 +1 @@
|
|||
USER_LONG garage.key (generated with Garage's genkeys.sh script)
|
|
@ -5,9 +5,9 @@ WORKDIR /root
|
|||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y wget && \
|
||||
wget https://github.com/vector-im/riot-web/releases/download/v${VERSION}/riot-v${VERSION}.tar.gz && \
|
||||
tar xf riot-v${VERSION}.tar.gz && \
|
||||
mv riot-v${VERSION}/ riot/
|
||||
wget https://github.com/vector-im/element-web/releases/download/v${VERSION}/element-v${VERSION}.tar.gz && \
|
||||
tar xf element-v${VERSION}.tar.gz && \
|
||||
mv element-v${VERSION}/ riot/
|
||||
|
||||
FROM superboum/amd64_webserver:v3
|
||||
COPY --from=builder /root/riot /srv/http
|
49
app/im/config/riot_web/config.json
Normal file
49
app/im/config/riot_web/config.json
Normal file
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue