Compare commits

..

No commits in common. "b1e7064488e8273b9d37646358a30460dd1e5496" and "7cbd9b00fcc224119e1083cf5fdaa8895a3ee870" have entirely different histories.

55 changed files with 178 additions and 700 deletions

View file

@ -1,11 +1,12 @@
#!/usr/bin/env python3
from os.path import join
from filecmp import dircmp
from difflib import unified_diff
from sys import stdout, argv
from os import walk
from os.path import join, islink
from os.path import join
from datetime import datetime, timezone
@ -86,9 +87,6 @@ for filename in old - new:
for filename in new - old:
if islink(join(NEW_DIR, filename)):
print(f'\n- lien {filename} ajouté\n')
else:
print(f'\n- fichier {filename} ajouté :\n')
with open(join(NEW_DIR, filename), 'r') as fh:
if WEBSITE:

View file

@ -23,39 +23,5 @@ for image in *; do
done
machinectl enable $MACHINES
machinectl start $MACHINES
STARTED=""
DEGRADED=""
found=true
idx=0
while [ $found = true ]; do
found=false
echo "tentative $idx"
for machine in $MACHINES; do
if ! echo $STARTED | grep -q " $machine "; then
status=$(machinectl -q shell $machine /usr/bin/systemctl is-system-running || true)
if echo "$status" | grep -q degraded; then
STARTED="$STARTED $machine "
DEGRADED="$DEGRADED $machine"
elif echo "$status" | grep -q running; then
STARTED="$STARTED $machine "
else
found=true
echo "status actuel de $machine : $status"
fi
fi
done
sleep 2
idx=$((idx+1))
if [ $idx = 60 ]; then
break
fi
done
retcode=0
for machine in $DEGRADED; do
echo
echo "========= $machine"
machinectl -q shell $machine /usr/bin/systemctl --state=failed --no-legend --no-pager
retcode=1
done
exit $retcode
exit 0

View file

@ -2,14 +2,12 @@ from os import fdopen
from dbus import SystemBus, Array
def run(host, cmd, user=None):
def run(host, cmd):
bus = SystemBus()
remote_object = bus.get_object('org.freedesktop.machine1',
'/org/freedesktop/machine1',
False,
)
if user is not None:
cmd = ['/bin/su', '-', user, '-s', '/bin/bash', '-c', ' '.join(cmd)]
res = remote_object.OpenMachineShell(host,
'',
cmd[0],

View file

@ -18,7 +18,7 @@
</service>
<service name='dovecot-init'>
<override/>
<file>/etc/nginx/default.d/autoconfig.conf</file>
<file>/etc/nginx/conf.d/autoconfig.conf</file>
</service>
<service name='nginx'>
<file source='config-v1.1.xml' file_type="variable" variable="mail_domains">well_known_filenames</file>
@ -90,8 +90,8 @@
<variable name="revprox_client_external_domainnames" redefine="True"/>
<variable name="revprox_client_web_address" redefine="True"/>
</family>
<variable name="nginx_root" redefine='True'>
<value>/var/www/html</value>
<variable name="nginx_default_https" redefine="True">
<value>False</value>
</variable>
</family>
</variables>

View file

@ -1,2 +1,12 @@
server {
listen 443 ssl;
server_name %%domain_name_eth0;
ssl_client_certificate %%revprox_ca_file;
ssl_certificate %%revprox_cert_file;
ssl_certificate_key %%revprox_key_file;
root /var/www/html/;
# To allow POST on static pages
error_page 405 =200 $uri;
}

View file

@ -1,5 +1,5 @@
[Unit]
After=risotto.target
After=network.target
[Service]
ExecStart=

View file

@ -1,7 +1,7 @@
#ORIGIN https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/systemd/gitea.service
[Unit]
Description=Gitea (Git with a cup of tea)
After=risotto.target
After=network.target postgresqlclient.service
[Service]
# Modify these two values and uncomment them if you have

View file

@ -2,7 +2,7 @@
<rougail version="0.10">
<services>
<service name="ldap-client" target="risotto" engine="creole">
<service name="ldap_client" manage="False">
<file source="ldap.conf" file_type="variable">ldap_client_file</file>
<file source="ca_LDAP.crt" file_type="variable">ldap_ca_file</file>
<file source="ldap_client.crt" file_type="variable">ldap_cert_file</file>

View file

@ -1,8 +0,0 @@
[Unit]
After=network-online.target
Before=risotto.target
[Service]
Type=oneshot
ExecStart=/usr/bin/timeout 90 bash -c 'while ! 3<> /dev/tcp/%%ldap_server_address/%%ldap_port; do sleep 1; done'

View file

@ -25,10 +25,10 @@
</services>
<variables>
<family name="nginx">
<variable name="oauth2_client_external_domain" type="domainname" hidden="True" supplier="OAuth2Client:external_domain"/>
<variable name="nginx_default_https" redefine="True">
<value>False</value>
</variable>
<variable name="oauth2_client_external_domain" type="domainname" hidden="True" supplier="OAuth2Client:external_domain"/>
</family>
<family name="lemonldap" description="LemonLDAP" help="Configuration de la solution d'authentification unique LemonLDAP::NG">
<variable name="lemon_proc" type="number" description="Nombre de processus dédié à LemonLdap (équivalent au nombre de processeurs)" mandatory="True">

View file

@ -1,6 +1,7 @@
[Unit]
After=risotto.target nginx.service
After=nginx.service
[Service]
ExecStartPre=/usr/bin/timeout 90 bash -c 'while ! 3<> /dev/tcp/%%ldap_server_address/%%ldap_port; do sleep 1; done'
ExecStartPost=-/usr/bin/timeout 10 bash -c 'while ! /usr/local/lib/sbin/interne_well_known.pl > /var/www/html/.well-known/openid-configuration/int; do sleep 1; done'
ExecStartPost=-/bin/bash -c '/usr/local/lib/sbin/interne_well_known.pl no > /var/www/html/.well-known/openid-configuration/ext'

View file

@ -1,6 +1,7 @@
%echo "#!/usr/bin/env perl"
use HTTP::Tiny;
use JSON qw(from_json to_json);
my $response = HTTP::Tiny->new->get('https://%%domain_name_eth0/.well-known/openid-configuration');

View file

@ -5,5 +5,5 @@ depends:
- postgresql-client
- relay-lmtp-client
- reverse-proxy-client
- nginx-https
- nginx-common
- oauth2-client

View file

@ -11,9 +11,8 @@
<service name="postorius" target="multi-user" engine="creole">
<file engine="none">/etc/postorius/gunicorn_config.py</file>
<file engine="none" source="sysuser-postorius.conf">/sysusers.d/0postorius.conf</file>
<file source="config-nginx.conf">/etc/nginx/default.d/postorius.conf</file>
<file source="config-nginx.conf">/etc/nginx/conf.d/postorius.conf</file>
<file source="postorius-settings.py">/etc/mailman3.d/postorius.py</file>
<file>/tests/mailman.yml</file>
</service>
<service name="postgresqlclient" target="multi-user" engine="creole">
<file owner="postorius" mode="400">/etc/pki/tls/private/postgresql_postorius.key</file>
@ -48,11 +47,6 @@
<variable name="oauth2_client_external" redefine="True" remove_fill="True"/>
</family>
</family>
<family name="nginx">
<variable name="nginx_root" redefine="True">
<value>/usr/share/webapps/postorius</value>
</variable>
</family>
<family name="postgresql">
<variable name="pg_client_key_owner" redefine="True">
<value>mailman</value>

View file

@ -3,7 +3,7 @@
<variables>
<family name="list_" description="Listes du domaine " dynamic="mailman_domains">
<variable name="name_" description="Nom des listes" type="unix_user" multi="True" mandatory="True"/>
<variable name="names_" description="Address names" type="string" mandatory="True" hidden="True"/>
<variable name="names_" description="Address names" type="string" multi="True" mandatory="True" hidden="True"/>
</family>
<variable name="names_" description="Address names" type="string" multi="True" mandatory="True" hidden="True" supplier="LMTP:criteria"/>
</variables>

View file

@ -1,18 +1,20 @@
from risotto.utils import multi_function as _multi_function
from itertools import chain
@_multi_function
def mailman_emails(lists, domain):
return '.*@' + domain
# ret = []
# for lst in lists:
# for suffix in [None, 'bounces(\+.*)?', 'confirm(\+.*)?', 'join', 'leave', 'owner', 'request', 'subscribe', 'unsubscribe']:
# if suffix:
# lst_name = lst + '-' + suffix
# else:
# lst_name = lst
# ret.append(lst_name + '@' + domain)
# return ret
ret = []
for lst in lists:
for suffix in [None, 'bounces(\+.*)?', 'confirm(\+.*)?', 'join', 'leave', 'owner', 'request', 'subscribe', 'unsubscribe']:
if suffix:
lst_name = lst + '-' + suffix
else:
lst_name = lst
ret.append(lst_name + '@' + domain)
return ret
@_multi_function
def mailman_concat(lists):
return lists
# list of lists to a single list
return list(chain(*lists))

View file

@ -1,5 +1,15 @@
server {
listen 443 ssl;
server_name %%domain_name_eth0;
ssl_client_certificate %%revprox_ca_file;
ssl_certificate %%revprox_cert_file;
ssl_certificate_key %%revprox_key_file;
charset utf-8;
client_max_body_size 75M;
root /usr/share/webapps/postorius;
location /mailman/postorius_static {
alias /usr/lib/python3.10/site-packages/postorius/static;
}
@ -29,3 +39,4 @@ location /mailman/%%location {
location /mailman {
rewrite ^(/mailman/.*)$ /mailman/postorius/ permanent;
}
}

View file

@ -1,12 +0,0 @@
%set %%username="rougail_test@silique.fr"
ip: %%ip_eth0
revprox_ip: %%revprox_client_server_ip
%set %%domain = %%revprox_client_external_domainnames[0]
domain_name: %%domain
base_url: https://%%domain%%domain.revprox_client_location
auth_url: %%oauth2_client_external[0]
auth_server: %%oauth2_server_domainname
username: %%username
password: %%get_password(server_name='test', username=%%username, description='test', type="cleartext", hide=%%hide_secret, temporary=True)
mailman_domain: %%mailman_domains[0]
internal_address: %%domain_name_eth0

View file

@ -1,6 +1,6 @@
[Unit]
Description=Postorius WSGI Service
After=risotto.target
After=postgresqlclient.service
[Service]
%for %%domain in %%mailman_domains

View file

@ -1,6 +1,6 @@
[Unit]
Description=Postorius WSGI Service
After=risotto.target
After=network.target postgresqlclient.service
[Service]
Type=notify

View file

@ -1,396 +0,0 @@
from yaml import load, SafeLoader
from os import environ
from os.path import join, isdir
from revprox import Authentication
from execute import run
from re import search
from time import sleep
from imaplib2 import IMAP4_SSL
from smtplib import SMTP
from email import message_from_bytes
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
DATA = None
DATA_IMAP = None
IMAP = None
IMAP_FAMILY = None
def get_data():
global DATA
if not DATA:
conf_file = f'{environ["MACHINE_TEST_DIR"]}/mailman.yml'
with open(conf_file) as yaml:
DATA = load(yaml, Loader=SafeLoader)
return DATA
def get_imap_data():
global DATA_IMAP
if not DATA_IMAP:
machine_test_dir = environ['MACHINE_TEST_DIR']
print("FIXME")
machine_test_dir = machine_test_dir.replace('mailman', 'dovecot')
if not isdir(machine_test_dir):
print('!!! No local IMAP server found !!!')
return
conf_file = f'{machine_test_dir}/imap.yml'
with open(conf_file) as yaml:
DATA_IMAP = load(yaml, Loader=SafeLoader)
return DATA_IMAP
def get_authentication(data, family=False):
if family:
username = data['username_family']
password = data['password_family']
else:
username = data['username']
password = data['password']
return Authentication(data['auth_url'],
data['auth_server'],
data['revprox_ip'],
username,
password,
f"""<title>
Listes - {data["domain_name"]}
</title>""",
)
def check_mail(subject, family=False, reply=False):
global IMAP, IMAP_FAMILY
data = get_imap_data()
if data is None:
return
if family:
if IMAP_FAMILY is None:
IMAP_FAMILY = IMAP4_SSL(data['address'])
IMAP_FAMILY.LOGIN(data['username_family'], data['password_family'] + '2')
imap = IMAP_FAMILY
else:
if IMAP is None:
IMAP = IMAP4_SSL(data['address'])
IMAP.LOGIN(data['username'], data['password'])
imap = IMAP
imap.SELECT(readonly=False)
typ, req = imap.SEARCH(None, 'ALL')
assert typ == 'OK'
if not req[0].decode():
raise Exception('pas de mail')
num = req[0].decode().split()[-1]
field = imap.FETCH(num, '(RFC822)')
assert field[0] == 'OK'
msg = message_from_bytes(field[1][-2][-1])
#if msg.is_multipart():
# for part in msg.walk():
# # extract content type of email
# try:
# print(part.get_payload(decode=True).decode())
# except:
# pass
#else:
# print(msg.get_payload(decode=True).decode())
if subject is not None:
assert subject == msg['Subject']
if reply:
reply_message(msg)
ret = imap.store(num, '+FLAGS', '\\Deleted')
assert ret[0] == 'OK', f'error when deleting mail: {ret}'
imap.expunge()
# imap.CLOSE()
# imap.LOGOUT()
def reply_message(msg):
data = get_imap_data()
body = MIMEText('resend')
message = MIMEMultipart()
message["to"] = msg["From"]
message['from'] = msg["To"]
message['subject'] = f'Re: {msg["Subject"]}'
message.add_header('reply-to', msg["From"])
message.attach(body)
#
smtp = SMTP(data['address'], '587')
smtp.starttls()
smtp.login(data['username_family'], data['password_family'] + '2')
smtp.sendmail(msg["To"],
msg["From"],
message.as_string(),
)
smtp.quit()
def send_mail(subject, data, data_mm, family=False, add_mail=''):
list_name = 'test'
smtp = SMTP(data['address'], '587')
smtp.starttls()
if not family:
sender = data['username']
smtp.login(sender, data['password'])
else:
sender = data['username_family']
smtp.login(sender, data['password_family'] + '2')
list_addr = f'{list_name}{add_mail}@{data_mm["mailman_domain"]}'
msg = f"""From: {sender}\r\n\
To: {list_addr}\r\n\
Subject: {subject}\r\n\
\r\n\
MESSAGE"""
smtp.sendmail(sender,
list_addr,
msg,
)
smtp.quit()
def test_mailman():
data = get_data()
get_authentication(data)
def test_mailman_login():
data = get_data()
authentication = get_authentication(data)
content = authentication.get(data['auth_url'])
login = data['username'].split('@')[0]
assert f'Successfully signed in as {login}.' in content
def test_mailman_list():
data = get_data()
authentication = get_authentication(data)
list_name = 'test'
search_list = f'href="/mailman/postorius/lists/{list_name}.{data["mailman_domain"]}/"'
if 'FIRST_RUN' in environ:
content = authentication.get(data['auth_url'])
assert search_list not in content
result = run(data['internal_address'],
['/usr/bin/mailman3', 'create', '--language', 'fr', '-o', data['username'], '-N', '-d', f'{list_name}@{data["mailman_domain"]}'],
'mailman',
)
assert list(result) == [f'Liste de diffusion créée : {list_name}@{data["mailman_domain"]}']
content = authentication.get(data['auth_url'])
assert search_list in content
def test_mailman_inscription():
data = get_data()
authentication = get_authentication(data)
list_name = 'test'
search_inscription = f'<td>Adresse principale ({data["username"]})</td>'
url = join(data['base_url'], f'postorius/lists/{list_name}.{data["mailman_domain"]}/')
if 'FIRST_RUN' in environ:
content = authentication.get(url)
assert search_inscription not in content
pattern_csrf = r'name="csrfmiddlewaretoken" value="([a-zA-Z0-9\-\_=]+)"'
pattern_sub = r'<option value="([a-zA-Z0-9\-\_=]+)">Adresse principale'
csrf = search(pattern_csrf, content)[1]
subscriber = search(pattern_sub, content)[1]
headers = {'Referer': url}
authentication.post(url + 'subscribe', {'csrfmiddlewaretoken': csrf, 'subscriber': subscriber, 'delivery_mode': 'regular', 'display_name': ''}, headers=headers)
subject = f'=?utf-8?q?Bienvenue_sur_la_liste_de_diffusion_=22{list_name.capitalize()}=22?='
idx = 0
while True:
try:
check_mail(subject)
break
except:
idx += 1
if idx == 10:
raise Exception('mail not arrived')
sleep(1)
content = authentication.get(url)
assert search_inscription in content
def test_send_mail():
data = get_imap_data()
if data is None:
return
data_mm = get_data()
subject = 'TEST MAILMAN'
send_mail(subject, data, data_mm)
idx = 0
while True:
try:
check_mail("=?utf-8?b?W1Rlc3Rd?= TEST MAILMAN")
break
except:
idx += 1
if idx == 10:
raise Exception('mail not arrived')
sleep(1)
def test_send_wrong_mail():
data = get_imap_data()
if data is None:
return
data_mm = get_data()
url = f'{data_mm["base_url"]}postorius/lists/test.{data_mm["mailman_domain"]}/held_messages'
# no moderated mail
authentication = get_authentication(data_mm)
content = authentication.get(url)
subject = 'TEST MAILMAN'
assert subject not in content
# send mail
smtp = SMTP(data['address'], '587')
smtp.starttls()
smtp.login(data['username_family'], data['password_family'] + '2')
list_name = 'test'
list_addr = f'{list_name}@{data_mm["mailman_domain"]}'
msg = f"""From: {data["username_family"]}\r\n\
To: {list_addr}\r\n\
Subject: {subject}\r\n\
\r\n\
MESSAGE"""
smtp.sendmail(data['username_family'], list_addr, msg)
smtp.quit()
idx = 0
mailman_domain = data_mm['mailman_domain'].replace('.', '=2E')
while True:
try:
check_mail(f'=?utf-8?q?Votre_message_=C3=A0_test=40{mailman_domain}_attend_la_validation_d=27un_mod=C3=A9rateur?=', family=True)
break
except:
idx += 1
if idx == 10:
raise Exception('mail not arrived...')
sleep(1)
#
mail = data['username_family'].replace('@', '=40').replace('.', '=2E').replace('_', '=5F')
while True:
try:
check_mail(f'=?utf-8?q?Le_message_test=40{mailman_domain}_de_{mail}_n=C3=A9cessite_une_validation?=')
break
except:
idx += 1
if idx == 10:
raise Exception('mail 2 not arrived...')
sleep(1)
# reject mail
content = authentication.get(url)
assert subject in content
pattern_csrf = r'name="csrfmiddlewaretoken" value="([a-zA-Z0-9\-\_=]+)"'
csrf = search(pattern_csrf, content)[1]
pattern_msg_id = r'class="message-checkbox" name="choices" value="([a-zA-Z0-9\-\_=]+)"'
msg_id = search(pattern_msg_id, content)[1]
#
headers = {'Referer': url}
authentication.post(url, {'csrfmiddlewaretoken': csrf, 'reject': 'Rejeter', 'choices': msg_id}, headers=headers)
content = authentication.get(url)
assert subject not in content
while True:
try:
check_mail('=?utf-8?q?Requ=C3=AAte_pour_la_liste_de_diffusion_=22Test=22_rejet=C3=A9e?=', family=True)
break
except:
idx += 1
if idx == 10:
raise Exception('mail reject not arrived...')
sleep(1)
def test_mailman_create_delete():
data = get_data()
authentication = get_authentication(data)
list_name = 'test_tmp'
search_list = f'href="/mailman/postorius/lists/{list_name}.{data["mailman_domain"]}/"'
#
content = authentication.get(data['auth_url'])
assert search_list not in content
#
result = run(data['internal_address'],
['/usr/bin/mailman3', 'create', '--language', 'fr', '-o', data['username'], '-N', '-d', f'{list_name}@{data["mailman_domain"]}'],
'mailman',
)
assert list(result) == [f'Liste de diffusion créée : {list_name}@{data["mailman_domain"]}']
content = authentication.get(data['auth_url'])
assert search_list in content
#
result = run(data['internal_address'],
['/usr/bin/mailman3', 'remove', f'{list_name}@{data["mailman_domain"]}'],
'mailman',
)
assert list(result) == [f'Liste supprimée : {list_name}@{data["mailman_domain"]}']
content = authentication.get(data['auth_url'])
assert search_list not in content
def test_mailman_inscription_desinscription_mail():
list_name = 'test'
data = get_imap_data()
if data is None:
return
data_mm = get_data()
send_mail('subscribe', data, data_mm, family=True, add_mail='-join')
idx = 0
# inscription + validation
mailman_domain = data_mm['mailman_domain'].replace('.', '=2E')
while True:
try:
check_mail(f'=?utf-8?q?Votre_validation_est_n=C3=A9cessaire_pour_vous_abonner_de_la_liste_de_diffusion_test=40{mailman_domain}?=', family=True, reply=True)
break
except:
idx += 1
if idx == 10:
raise Exception('mail not arrived...')
sleep(1)
idx = 0
while True:
try:
check_mail(f'=?utf-8?q?Bienvenue_sur_la_liste_de_diffusion_=22{list_name.capitalize()}=22?=', family=True)
break
except:
idx += 1
if idx == 10:
raise Exception('confirmation not arrived...')
sleep(1)
# send mail and check
subject = 'TEST MAILMAN'
send_mail(subject, data, data_mm)
idx = 0
while True:
try:
check_mail("=?utf-8?b?W1Rlc3Rd?= TEST MAILMAN")
break
except:
idx += 1
if idx == 10:
raise Exception('mail not arrived')
sleep(1)
while True:
try:
check_mail("=?utf-8?b?W1Rlc3Rd?= TEST MAILMAN", family=True)
break
except:
idx += 1
if idx == 10:
raise Exception('mail not arrived')
sleep(1)
# unsubscribe
send_mail('unsubscribe', data, data_mm, family=True, add_mail="-leave")
idx = 0
while True:
try:
check_mail(f'=?utf-8?q?Votre_validation_est_n=C3=A9cessaire_pour_vous_d=C3=A9sabonner_de_la_liste_de_diffusion_test=40{mailman_domain}?=', family=True, reply=True)
break
except:
idx += 1
if idx == 10:
raise Exception('unsubscribe not arrived...')
sleep(1)
idx = 0
while True:
try:
check_mail(f'=?utf-8?q?Vous_avez_=C3=A9t=C3=A9_d=C3=A9sinscrits_de_la_liste_de_diffusion_Test?=', family=True)
break
except:
idx += 1
if idx == 10:
raise Exception('unsubscribe confirmation not arrived...')
sleep(1)

View file

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail version="0.10">
<services>
<service name="mariadbclient" target="risotto" engine="creole"/>
<service name="mariadbclient" target="multi-user" engine="creole"/>
</services>
<variables>
<family name="mariadb" description="MariaDB">

View file

@ -1,7 +1,6 @@
[Unit]
Description=Waiting for mariadb server
After=network-online.target
Before=risotto.target
Before=network.target
[Service]
Type=oneshot

View file

@ -1,6 +1,6 @@
[Unit]
Description=Nextcloud management
After=risotto.target
After=postgresqlclient.service
Before=apache.service php-fpm.service
[Service]

View file

@ -13,7 +13,6 @@
<file file_type="variable" source="ca_InternalReverseProxy.crt">revprox_ca_file</file>
<file filelist="nginx_default_https" mode="600">/etc/pki/tls/certs/nginx.crt</file>
<file filelist="nginx_default_https" mode="600">/etc/pki/tls/private/nginx.key</file>
<file>/tests/nginx-common.yml</file>
</service>
</services>
<variables>

View file

@ -1,13 +0,0 @@
address: %%ip_eth0
nginx_default_http: %slurp
%if %%getVar('nginx_default_http', False) and not %%getVar('revprox_client_external_domainnames', None)
true
%else
false
%end if
nginx_default_https: %slurp
%if %%getVar('nginx_default_https', False) and not %%getVar('revprox_client_external_domainnames', None)
true
%else
false
%end if

View file

@ -76,24 +76,14 @@ http {
%if %%nginx_default_https
server {
listen 443 ssl http2;
%if %%getVar('revprox_client_external_domainnames', None)
%for %%domain in %%revprox_client_external_domainnames
server_name %%domain;
%end for
%else
server_name _;
%end if
server_name %%domain_name_eth0;
root %%nginx_root;
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
ssl_certificate /etc/pki/tls/certs/nginx.crt;
ssl_certificate_key /etc/pki/tls/private/nginx.key;
%if %%getVar('revprox_client_external_domainnames', None)
ssl_client_certificate %%revprox_ca_file;
%else
ssl_client_certificate /etc/pki/ca-trust/source/anchors/ca_HTTP.crt;
%end if
ssl_client_certificate /etc/pki/ca-trust/source/anchors/ca_InternalReverseProxy.crt;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
@ -115,7 +105,5 @@ http {
%else
include /etc/nginx/sites-enabled/*;
%end if
%if not %%getVar('revprox_client_external_domainnames', None)
include /etc/nginx/sites-enabled/*;
%end if
}

View file

@ -1,50 +0,0 @@
from yaml import load, SafeLoader
from os import environ
from pytest import raises
import warnings
import socket
from requests import get
from requests.exceptions import SSLError
def req(url, ip, verify=True):
# Monkey patch to force IPv4 resolution
old_getaddrinfo = socket.getaddrinfo
def new_getaddrinfo(*args, **kwargs):
ret = old_getaddrinfo(*args, **kwargs)
dns = list(ret[0])
dns[-1] = (ip, dns[-1][1])
return [dns]
socket.getaddrinfo = new_getaddrinfo
if not verify:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
ret = get(url, verify=verify)
else:
ret = get(url, verify=verify)
ret_code = ret.status_code
content = ret.content
socket.getaddrinfo = old_getaddrinfo
return ret_code, content.decode()
def test_revprox():
conf_file = f'{environ["MACHINE_TEST_DIR"]}/nginx-common.yml'
with open(conf_file) as yaml:
data = load(yaml, Loader=SafeLoader)
# test unknown domain
url = 'google.fr'
protocols = []
if data['nginx_default_http']:
protocols.append('http')
if data['nginx_default_https']:
protocols.append('https')
# test certificate
with raises(SSLError):
# not certificat problem for https://{url}
req(f'https://{url}', data['address'])
for protocol in protocols:
ret_code, content = req(f'{protocol}://{url}', data['address'], verify=False)
assert ret_code == 200, f'{protocol}://{url} do not returns code 200 but {ret_code}'
assert "<title>Test Page for the HTTP Server on Fedora</title>" in content, f'{protocol}://{url} do not returns default fedora page'

View file

@ -1,16 +1,22 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail version="0.10">
<services>
<service name='nginx'>
<file filelist="nginx_default_risotto">/etc/nginx/default.d/risotto.conf</file>
</service>
</services>
<variables>
<family name="nginx">
<variable name="nginx_default_http" redefine="True" hidden="True">
<value>False</value>
</variable>
<variable name="nginx_default_https" redefine="True" hidden="True">
<variable name="nginx_default_https" redefine="True">
<value>True</value>
</variable>
<variable name="php_fpm_user" redefine="True" exists="True">
<value>nginx</value>
</variable>
<variable name="nginx_root_directory" type="filename"/>
<variable name="nginx_locations" type="filename" multi="True" mandatory="True">
<value>/</value>
</variable>
</family>
<family name="redis" description="Redis">
<variable name="redis_client_key_owner" redefine="True" exists="True">
@ -23,4 +29,11 @@
</variable>
</family>
</variables>
<constraints>
<condition name="disabled_if_in" source="nginx_root_directory">
<param type="nil"/>
<target type="filelist">nginx_default_risotto</target>
<target type="variable">nginx_locations</target>
</condition>
</constraints>
</rougail>

View file

@ -1,5 +1,3 @@
# To allow POST on static pages
error_page 405 =200 $uri;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
@ -9,14 +7,17 @@ add_header X-Permitted-Cross-Domain-Policies none;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;';
add_header Referrer-Policy no-referrer always;
%for %%location in %%piwigo_locations
%for %%location in %%nginx_locations
location %%location {
%if %%location == '/'
root %slurp
%else
alias %slurp
%end if
/usr/local/share/piwigo;
%%nginx_root_directory;
%if not %%getVar('php_fpm_installed', False)
index index.html;
%else
index index.php;
location ~ ^(?<script_name>.+?\.php)(?<path_info>/.*)?$ {
fastcgi_pass php-fpm;
@ -24,5 +25,6 @@ location %%location {
fastcgi_param SCRIPT_FILENAME $request_filename;
include fastcgi_params;
}
%end if
}
%end for

View file

@ -1,8 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail version="0.10">
<services>
<service name='nginx'>
<file>/etc/pki/ca-trust/source/anchors/ca_HTTP.crt</file>
</service>
</services>
</rougail>

View file

@ -4,7 +4,7 @@
<service name='nginx'>
<override engine="creole"/>
<file source="nginx-options-rp.conf">/etc/nginx/conf.d/options-rp.conf</file>
<file source="revprox-nginx.conf">/etc/nginx/sites-enabled/risotto.conf</file>
<file source="revprox-nginx.conf">/etc/nginx/conf.d/risotto.conf</file>
<file source="certificate.crt" file_type="variable" mode="600" variable="nginx.revprox_domainnames">nginx.nginx_certificate_filename</file>
<file source="private.key" file_type="variable" mode="600" variable="nginx.revprox_domainnames">nginx.nginx_private_key_filename</file>
<file>/tests/reverse-proxy.yml</file>
@ -22,9 +22,6 @@
<variable name="nginx_default_http" redefine="True">
<value>True</value>
</variable>
<variable name="nginx_default_https" redefine="True">
<value>True</value>
</variable>
</family>
</variables>
</rougail>

View file

@ -1,3 +0,0 @@
%for %%idx in %%range(%%len(%%zones_list))
%%get_chain(authority_cn=%%getVar('domain_name_eth' + %%str(%%idx)), authority_name="HTTP", hide=%%hide_secret)
%end for

View file

@ -1,2 +0,0 @@
%%get_certificate(%%nginx_default, authority_cn=%%domain_name_eth0, authority_name='HTTP', type="server", hide=%%hide_secret)
%%get_chain(%%nginx_default, 'HTTP', hide=%%hide_secret)

View file

@ -1 +0,0 @@
%%get_private_key(%%nginx_default, authority_cn=%%domain_name_eth0, authority_name='HTTP', type='server', hide=%%hide_secret)

View file

@ -41,7 +41,7 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Destination $dest;
%end if
proxy_ssl_trusted_certificate %%revprox_ca_file;
proxy_ssl_trusted_certificate /etc/pki/ca-trust/source/anchors/ca_InternalReverseProxy.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_session_reuse on;

View file

@ -32,8 +32,19 @@ def test_revprox():
conf_file = f'{environ["MACHINE_TEST_DIR"]}/reverse-proxy.yml'
with open(conf_file) as yaml:
data = load(yaml, Loader=SafeLoader)
# test unknown domain
url = 'google.fr'
ret_code, content = req(f'https://{url}', data['address'], verify=False)
assert ret_code == 200, f'https://{url} do not returns code 200 but {ret_code}'
assert "<title>Test Page for the HTTP Server on Fedora</title>" in content, f'https://{url} returns default fedora page'
# test certificate
try:
req(f'https://{url}', data['address'])
raise Exception(f'not certificat problem for https://{url}')
except SSLError:
pass
# test known domains
for url in data['urls']:
ret_code, content = req(f'https://{url}', data['address'])
assert ret_code == 200, f'https://{url} do not returns code 200 but {ret_code}'
assert "<title>Test Page for the HTTP Server on Fedora</title>" not in content, f'https://{url} do returns default fedora page'
assert "<title>Test Page for the HTTP Server on Fedora</title>" not in content, f'https://{url} returns default fedora page'

View file

@ -1,2 +1,2 @@
[Unit]
After=risotto.target
After=network.target

View file

@ -1,8 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail version="0.10">
<services>
<service name="oauth2-client" target="risotto" engine="creole"/>
</services>
<variables>
<family name="oauth2_client" description="OAuth2 client">
<variable name="oauth2_client_server_domainname" type="domainname" description="OAuth2 server domain name" mandatory='True' supplier="OAuth2"/>

View file

@ -1 +0,0 @@
PKG="$PKG curl"

View file

@ -1,7 +0,0 @@
[Unit]
After=network-online.target
Before=risotto.target
[Service]
Type=oneshot
ExecStart=/usr/bin/timeout 90 bash -c 'while ! [ "$(/usr/bin/curl --write-out '%{http_code}' --silent --output /dev/null https://%%oauth2_client_server_domainname/.well-known/openid-configuration)" = 200 ]; do sleep 1; done;'

View file

@ -7,5 +7,5 @@ depends:
- relay-mail-client
- reverse-proxy-client
- redis-client
- nginx-https
- nginx-common
- oauth2-client

View file

@ -6,8 +6,7 @@
<file engine="none" source="sysuser-peertube.conf">/sysusers.d/0peertube.conf</file>
<file engine="none" source="tmpfile-peertube.conf">/tmpfiles.d/0peertube.conf</file>
<file>/etc/peertube/production.yaml</file>
<file source="nginx.peertube.conf">/etc/nginx/default.d/peertube.conf</file>
<file source="nginx.peertube.conf.d.conf">/etc/nginx/conf.d/peertube.conf</file>
<file source="nginx.peertube.conf">/etc/nginx/conf.d/peertube.conf</file>
</service>
</services>
<variables>
@ -46,9 +45,6 @@
</family>
</family>
<family name="nginx">
<variable name="nginx_root" redefine='True'>
<value>/usr/share/peertube</value>
</variable>
<family name="revprox_client">
<variable name="revprox_client_location" redefine="True">
<value>/</value>

View file

@ -16,14 +16,15 @@
# GNUNUX location / { return 301 https://$host$request_uri; }
# GNUNUX }
# GNUNUX upstream %%domain_name_eth0 {
upstream %%domain_name_eth0 {
# GNUNUX server ${PEERTUBE_HOST};
# GNUNUX }
server localhost:9000;
}
# GNUNUX server {
# GNUNUX listen 443 ssl http2;
# GNUNUX listen [::]:443 ssl http2;
# GNUNUX server_name %%domain_name_eth0;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name %%domain_name_eth0;
# GNUNUX access_log /var/log/nginx/peertube.access.log; # reduce I/0 with buffer=10m flush=5m
# GNUNUX error_log /var/log/nginx/peertube.error.log;
@ -34,6 +35,11 @@
##
# GNUNUX ssl_certificate /etc/letsencrypt/live/${WEBSERVER_HOST}/fullchain.pem;
# GNUNUX ssl_certificate_key /etc/letsencrypt/live/${WEBSERVER_HOST}/privkey.pem;
#>GNUNUX
ssl_client_certificate %%revprox_ca_file;
ssl_certificate %%revprox_cert_file;
ssl_certificate_key %%revprox_key_file;
#<GNUNUX
# GNUNUX location ^~ '/.well-known/acme-challenge' {
# GNUNUX default_type "text/plain";
@ -45,14 +51,14 @@
# based on Mozilla Guideline v5.6
##
# GNUNUX ssl_protocols TLSv1.2 TLSv1.3;
# GNUNUX ssl_prefer_server_ciphers on;
# GNUNUX ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256; # add ECDHE-RSA-AES256-SHA if you want compatibility with Android 4
# GNUNUX ssl_session_timeout 1d; # defaults to 5m
# GNUNUX ssl_session_cache shared:SSL:10m; # estimated to 40k sessions
# GNUNUX ssl_session_tickets off;
# GNUNUX ssl_stapling on;
# GNUNUX ssl_stapling_verify on;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256; # add ECDHE-RSA-AES256-SHA if you want compatibility with Android 4
ssl_session_timeout 1d; # defaults to 5m
ssl_session_cache shared:SSL:10m; # estimated to 40k sessions
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
# HSTS (https://hstspreload.org), requires to be copied in 'location' sections that have add_header directives
#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
@ -139,6 +145,7 @@
##
# GNUNUX root /var/www/peertube/storage;
root /usr/share/peertube;
# Enable compression for JS/CSS/HTML, for improved client load times.
# It might be nice to compress JSON/XML as returned by the API, but
@ -261,4 +268,4 @@
try_files $uri @api;
}
# GNUNUX }
}

View file

@ -1,4 +0,0 @@
upstream %%domain_name_eth0 {
# GNUNUX server ${PEERTUBE_HOST};
server localhost:9000;
}

View file

@ -7,13 +7,16 @@
<file>/etc/piwigo/database.inc.php</file>
<file mode="755">/sbin/piwigo.sh</file>
<file engine="none">/etc/php-fpm.d/piwigo.conf</file>
<file source="piwigo.nginx.conf">/etc/nginx/default.d/piwigo.conf</file>
</service>
</services>
<variables>
<variable name="piwigo_admin_email" type="mail" description="Adresse courriel de l'administrateur Piwigo" mandatory="True"/>
<variable name="piwigo_admin_password" type="password" auto_save="False" hidden="True"/>
<variable name="piwigo_locations" type="filename" multi="True" mandatory="True"/>
<family name="nginx">
<variable name="nginx_root_directory" mandatory="True" redefine="True">
<value>/usr/local/share/piwigo</value>
</variable>
</family>
<variable name="piwigo_title" type="string" description="Titre de l'album" mandatory="True">
<value>Album photographique</value>
</variable>
@ -50,7 +53,7 @@
</fill>
<fill name="get_locations">
<param name="usernames" type="variable">piwigo_users</param>
<target>piwigo_locations</target>
<target>nginx_locations</target>
</fill>
</constraints>
</rougail>

View file

@ -1,6 +1,6 @@
[Unit]
Description=Piwigo management
After=risotto.target
After=mariadbclient.service
Before=nginx.service php-fpm.service
[Service]

View file

@ -7,7 +7,7 @@
<file engine="none" source="tmpfile-peertube.conf">/tmpfiles.d/0peertube.conf</file>
<file>/etc/peertube/production.yaml</file>
<file engine="none">/etc/pam.d/login</file>
<file source="nginx.peertube.conf">/etc/nginx/sites-enabled/peertube.conf</file>
<file source="nginx.peertube.conf">/etc/nginx/conf.d/peertube.conf</file>
</service>
</services>
<variables>

View file

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail version="0.10">
<services>
<service name="postgresqlclient" target="risotto" engine="creole">
<service name="postgresqlclient" target="multi-user" engine="creole">
<file mode="400">/secrets/postgresql.pass</file>
<file>/etc/pki/ca-trust/source/anchors/ca_PostgreSQL.crt</file>
<file>/etc/pki/tls/certs/postgresql.crt</file>

View file

@ -1,7 +1,6 @@
[Unit]
Description=Waiting for postgresql server
After=network-online.target
Before=risotto.target
Before=network.target
[Service]
Type=oneshot

View file

@ -1 +0,0 @@
pg_dumpall --clean > /srv/database.sql

View file

@ -33,7 +33,7 @@ class Authentication:
ret = req.get(url)
code = ret.status_code
content = ret.content
assert code == 200, f"cannot access to lemonldap; {content}"
assert code == 200
assert b'<title trspan="authPortal">Authentication portal</title>' in content, f'cannot find LemonLdap title: {content}'
def auth_lemonldap(self,
@ -62,8 +62,7 @@ class Authentication:
authorize_url = f'{portal_url}authorize'
ret = req.get(authorize_url)
assert ret.status_code == 200
content = ret.content.decode()
assert title in content, f'cannot find {title} in {content}'
assert title in ret.content.decode()
def get(self,
url,
@ -79,8 +78,7 @@ class Authentication:
def post(self,
url,
data,
headers=None,
):
with MookDns(self.ip):
ret = post(url, cookies=self.cookies, data=data, headers=headers)
ret = post(url, cookies=self.cookies, data=data)
assert ret.status_code == 200, f'return code is {ret.status_code}'

View file

@ -1,6 +1,6 @@
[Unit]
Description=Roundcube database init
After=risotto.target
After=postgresqlclient.service
Before=nginx.service php-fpm.service
[Service]

View file

@ -29,7 +29,6 @@
<file>/secrets/root.pwd</file>
<file engine="none">/tmpfiles.d/risotto-volatile.conf</file>
</service>
<service name="risotto" target="multi-user" type="target" engine="none"/>
</services>
<variables>
<variable name='root_password' type="password" description="Mot de passe de l'administrateur système root" auto_save='False' mandatory="True"/>

View file

@ -1,5 +0,0 @@
[Unit]
Description=Waiting for all dependencies
Before=multi-user.target
After=network-online.target
Wants=network-online.target