diff --git a/seed/base-machine/dictionaries/12-base.xml b/seed/base-machine/dictionaries/12-base.xml
index 168801c2..f2dbdf3e 100644
--- a/seed/base-machine/dictionaries/12-base.xml
+++ b/seed/base-machine/dictionaries/12-base.xml
@@ -10,39 +10,25 @@
False
-
-
+
+
+
-
-
+
+
-
+
-
- zones_name
- zones_list
-
-
- zones_name
- interfaces_list
-
- server_name
+ domain_name_eth
ip_eth
-
-
- server_name
- extra_domainnames
-
- domain_name_eth
-
- zones_name
+ zones_list
zone_name_eth
diff --git a/seed/base-machine/funcs/funcs.py b/seed/base-machine/funcs/funcs.py
index 848abfaf..8e67ffdc 100644
--- a/seed/base-machine/funcs/funcs.py
+++ b/seed/base-machine/funcs/funcs.py
@@ -6,7 +6,7 @@ from os.path import dirname as _dirname, abspath as _abspath, join as _join, isf
from os import makedirs as _makedirs
-from risotto.utils import ZONES_SERVER
+#from risotto.utils import ZONES_SERVER
_HERE = _dirname(_abspath(__main__.__file__))
@@ -81,30 +81,8 @@ def _set_password(server_name: str,
return file_content
-def get_range(lst):
- return list(range(max(1, len(lst))))
-
-
def get_zone_name(zones: list,
index: str,
):
if zones is not None:
return zones[int(index)]
-
-
-def get_domain_name(server_name: str,
- extra_domainnames: list,
- suffix: str,
- ) -> str:
- index = int(suffix)
- if index == 0:
- return server_name
- return extra_domainnames[index - 1]
-
-
-def get_provider_name(network_name: str,
- provider: str,
- ) -> str:
- if network_name not in ZONES_SERVER['providers'] or provider not in ZONES_SERVER['providers'][network_name]:
- return
- return ZONES_SERVER['providers'][network_name][provider][0]
diff --git a/seed/base/funcs/base.py b/seed/base/funcs/base.py
index 10a4031f..6e3dde36 100644
--- a/seed/base/funcs/base.py
+++ b/seed/base/funcs/base.py
@@ -1,9 +1,26 @@
from typing import List
from risotto.utils import load_domains, DOMAINS
+from risotto.utils import multi_function as _multi_function
+@_multi_function
def get_ip(server_name: str) -> str:
- load_domains()
- host_name, domain_name = server_name.split('.', 1)
- domain = DOMAINS[domain_name]
- return domain[1][domain[0].index(host_name)]
+ if server_name is None:
+ return
+ if isinstance(server_name, list):
+ return_list = True
+ else:
+ return_list = False
+ server_name = [server_name]
+ lst = []
+ for s_name in server_name:
+ host_name, domain_name = s_name.split('.', 1)
+ if not domain_name in DOMAINS:
+ raise ValueError(f'cannot find IP in domain name "{domain_name}" (for "{s_name}")')
+ domain = DOMAINS[domain_name]
+ ret = domain[1][domain[0].index(host_name)]
+ if not return_list:
+ return ret
+ if ret not in lst:
+ lst.append(ret)
+ return lst
diff --git a/seed/dns-external/dictionaries/14-dns-external.xml b/seed/dns-external/dictionaries/14-dns-external.xml
index 9cdb18b2..06f3b086 100644
--- a/seed/dns-external/dictionaries/14-dns-external.xml
+++ b/seed/dns-external/dictionaries/14-dns-external.xml
@@ -5,14 +5,7 @@
False
-
+
-
-
- zone_name_eth0
- ExternalDNS
- dns_client_address
-
-
diff --git a/seed/dns-local/dictionaries/13-dns-local.xml b/seed/dns-local/dictionaries/13-dns-local.xml
index 129bfc85..fb58cb48 100644
--- a/seed/dns-local/dictionaries/13-dns-local.xml
+++ b/seed/dns-local/dictionaries/13-dns-local.xml
@@ -10,21 +10,13 @@
True
-
+
-
- zone_name_eth0
- LocalDNS
- dns_client_address
-
-
- dns_client_address
- dns
- ip_eth0
- ip
+
+ dns_client_address
ip_dns
diff --git a/seed/dns-local/templates/dns-local.yml b/seed/dns-local/templates/dns-local.yml
index 10e01013..1f5b1cef 100644
--- a/seed/dns-local/templates/dns-local.yml
+++ b/seed/dns-local/templates/dns-local.yml
@@ -3,13 +3,13 @@ addresses:
%if %%getVar('dns_client_address', None)
- dns_address: '%%dns_client_address'
dns_ip: '%%ip_dns'
-%elif %%getVar('unbound_forward_address', None)
+%elif %%getVar('unbound_forward_address', None) is not None
%for %%authority in %%unbound_forward_address
- dns_address: %%authority
dns_ip: %%get_ip(%%str(%%authority))
%end for
%else
- %for %%zone in %%nsd_zones_auto
+ %for %%zone in %%nsd_zones
%set %%suffix = %%normalize_family(%%zone)
%set %%hostnames = %%nsd["nsd_zone_" + %%suffix]["hostname_" + %%suffix]["hostname_" + %%suffix]
%for %%nsd in %%hostnames
diff --git a/seed/dns-local/tests/mookdns.py b/seed/dns-local/tests/mookdns.py
new file mode 100644
index 00000000..8ab236f1
--- /dev/null
+++ b/seed/dns-local/tests/mookdns.py
@@ -0,0 +1,44 @@
+import socket
+from shutil import copyfile, move
+from os import remove
+from os.path import isfile
+
+
+class MookDns:
+ # Monkey patch to force IPv4 resolution
+ def __init__(self, ip):
+ self.ip = ip
+
+ def __enter__(self):
+ self.old_getaddrinfo = socket.getaddrinfo
+ def new_getaddrinfo(*args, **kwargs):
+ ret = self.old_getaddrinfo(*args, **kwargs)
+ dns = list(ret[0])
+ dns[-1] = (self.ip, dns[-1][1])
+ return [dns]
+ socket.getaddrinfo = new_getaddrinfo
+ return self
+
+ def __exit__(self, exc_type, exc, tb):
+ socket.getaddrinfo = self.old_getaddrinfo
+
+
+class MookDnsSystem:
+ # Monkey patch to force IPv4 resolution
+ def __init__(self, dns, ip):
+ self.dns = dns
+ self.ip = ip
+
+ def __enter__(self):
+ if not isfile('/etc/hosts.risotto'):
+ copyfile('/etc/hosts', '/etc/hosts.risotto')
+ with open('/etc/hosts.risotto', 'r') as risotto:
+ with open('/etc/hosts', 'w') as hosts:
+ for line in risotto.readlines():
+ if self.dns not in line:
+ hosts.write(line)
+ hosts.write(f'{self.ip} {self.dns}')
+
+ def __exit__(self, exc_type, exc, tb):
+ remove('/etc/hosts')
+ move('/etc/hosts.risotto', '/etc/hosts')
diff --git a/seed/dovecot/applicationservice.yml b/seed/dovecot/applicationservice.yml
index 5f39c6dd..8237e8e8 100644
--- a/seed/dovecot/applicationservice.yml
+++ b/seed/dovecot/applicationservice.yml
@@ -2,8 +2,7 @@ format: '0.1'
description: Postfix et Dovecot
depends:
- base-fedora-36
- - relay-mail-client
+ - relay-lmtp-client
- ldap-client-fedora
- oauth2-client
- nginx-https
-provider: IMAP
diff --git a/seed/dovecot/dictionaries/26_dovecot.xml b/seed/dovecot/dictionaries/26_dovecot.xml
index 084e5e43..05e555eb 100644
--- a/seed/dovecot/dictionaries/26_dovecot.xml
+++ b/seed/dovecot/dictionaries/26_dovecot.xml
@@ -47,7 +47,7 @@
/etc/pki/tls/private/dovecot.key
external_imap_crt
external_imap_key
- /tests/imap.yml
+ /tests/imap.yml
@@ -71,9 +71,8 @@
-
+
-
@@ -81,36 +80,22 @@
-
+
+
+
+
+
False
-
-
-
-
-
-
-
- smtp_relay_address
- lmtp_server
- domain_name_eth0
- mail_domains
-
-
- smtp_relay_address
- lmtp_criteria
- domain_name_eth0
- mail_domains
-
/etc/pki/tls/certs/imap_
imap_domainname
@@ -136,14 +121,12 @@
postfix_pem_files
- mail_domains
- mail_domains_calc
+ domain_name_eth0
+ imap_internal_address
- autoconfig
mail_domains
- .
- mail_domains_calc_autoconfig
+ mail_domains_calc
/var/www/html/mail/
@@ -154,49 +137,20 @@
True
well_known_filenames
-
- revprox_client_server_domainname
- revprox_clients
- revprox_location
- /mail/config-v1.1.xml
- revprox_is_websocket
- False
- revprox_url
- well_knowns
- True
-
- mail_domains_calc_autoconfig
-
-
- revprox_client_server_domainname
- revprox_clients
- revprox_location
- /.well-known/autoconfig/mail/config-v1.1.xml
- revprox_is_websocket
- False
- revprox_url
- well_knowns
- True
-
- mail_domains_calc
-
-
- revprox_client_server_domainname
- revprox_clients
- revprox_location
- /autodiscover/autodiscover.xml
- revprox_is_websocket
- False
- revprox_url
- well_knowns
- True
-
- mail_domains_calc
-
+
domain_name_eth0
mail_domains
- well_knowns
+ revprox_client_web_address
+
+
+ mail_domains
+ revprox_client_external_domainnames
+
+
+ revprox_client_external_domainnames
+
+ revprox_client_location
diff --git a/seed/dovecot/funcs/dovecot.py b/seed/dovecot/funcs/dovecot.py
index 415c5277..ea3be207 100644
--- a/seed/dovecot/funcs/dovecot.py
+++ b/seed/dovecot/funcs/dovecot.py
@@ -11,10 +11,29 @@ def sha512_crypt(password):
@_multi_function
-def calc_well_known(*args):
- if None in args:
- return
+def calc_domains(domains):
ret = []
- for dom in args[1]:
- ret.append(f'https://{args[0]}/mail/{dom}/autodiscover/autodiscover.xml')
+ for domain in domains:
+ ret.append(domain)
+ ret.append(domain)
+ ret.append(f'autoconfig.{domain}')
return ret
+
+
+@_multi_function
+def calc_locations(domain, index):
+ i = index//3
+ if 3 * i == index:
+ # divisible by three
+ return '/autodiscover/autodiscover.xml'
+ elif 3 * i + 1 == index:
+ return '/.well-known/autoconfig/mail/config-v1.1.xml'
+ return '/mail/config-v1.1.xml'
+
+
+@_multi_function
+def calc_well_known(index, dns, doms):
+ if None in (dns, doms):
+ return None
+ i = index//3
+ return f'https://{dns}/mail/{doms[i]}/autodiscover/autodiscover.xml'
diff --git a/seed/dovecot/templates/dovecot-ldap.conf.ext b/seed/dovecot/templates/dovecot-ldap.conf.ext
index 4e3bf82d..168da383 100644
--- a/seed/dovecot/templates/dovecot-ldap.conf.ext
+++ b/seed/dovecot/templates/dovecot-ldap.conf.ext
@@ -107,7 +107,7 @@ auth_bind = yes
# LDAP base. %variables can be used here.
# For example: dc=mail, dc=example, dc=org
# GNUNUX base =
-base = %%ldapclient_base_dn
+base = %%ldapclient_search_dn
# Dereference: never, searching, finding, always
#deref = never
diff --git a/seed/dovecot/templates/imap.yml b/seed/dovecot/templates/imap.yml
index 1836ec5e..d5ef2bf4 100644
--- a/seed/dovecot/templates/imap.yml
+++ b/seed/dovecot/templates/imap.yml
@@ -4,7 +4,7 @@
address: %%ip_eth0
dns: %%domain_name_eth0
username: %%username
-password: %%get_password(server_name=%%ldap_server_address, username=%%username, description="ldap user", type="cleartext", hide=%%hide_secret, temporary=True)
+password: %%get_password(server_name='test', username=%%username, description="test", type="cleartext", hide=%%hide_secret, temporary=True)
username_family: %%username_family
-password_family: %%get_password(server_name=%%ldap_server_address, username=%%username_family, description="ldap family user", type="cleartext", hide=%%hide_secret, temporary=True)
+password_family: %%get_password(server_name='test', username=%%username_family, description='test', type="cleartext", hide=%%hide_secret, temporary=True)
name_family: %%name_family
diff --git a/seed/dovecot/templates/ldapsource.cf b/seed/dovecot/templates/ldapsource.cf
index 56068014..38555927 100644
--- a/seed/dovecot/templates/ldapsource.cf
+++ b/seed/dovecot/templates/ldapsource.cf
@@ -8,6 +8,6 @@ version = 3
bind = yes
bind_dn = %%ldapclient_user
bind_pw = %%ldapclient_user_password
-search_base = %%ldapclient_base_dn
+search_base = %%ldapclient_search_dn
query_filter = (mailLocalAddress=%s)
result_attribute = cn
diff --git a/seed/dovecot/tests/test_imap.py b/seed/dovecot/tests/test_imap.py
index ba4a9b7e..9fb425b6 100644
--- a/seed/dovecot/tests/test_imap.py
+++ b/seed/dovecot/tests/test_imap.py
@@ -10,8 +10,8 @@ from smtplib import SMTP, SMTPNotSupportedError, SMTPAuthenticationError
conf_file = f'{environ["MACHINE_TEST_DIR"]}/imap.yml'
with open(conf_file) as yaml:
data = load(yaml, Loader=SafeLoader)
-parameters = (('user', data['username'], data['password']),
- ('family', data['username_family'], data['password_family']),
+parameters = (('user', data['username'], [data['password']]),
+ ('family', data['username_family'], [data['password_family'], data['password_family'] + "2"]),
)
@@ -19,8 +19,8 @@ def get_msg(username, msg='MESSAGE'):
return f'From: {username}\r\nTo: {username}\r\n\r\nSubject: TEST\r\n{msg}\r\n'
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_imap_wrong_password(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_imap_wrong_password(typ, username, passwords):
imap = IMAP4_SSL(data['address'])
try:
imap.LOGIN(username, 'b')
@@ -30,17 +30,33 @@ def test_imap_wrong_password(typ, username, password):
raise Exception('wrong login !')
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_imap_migration(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_imap_migration(typ, username, passwords):
msg = get_msg(username, 'MIGRATION')
if 'FIRST_RUN' in environ:
smtp = SMTP(data['address'], '587')
smtp.starttls()
- smtp.login(username, password)
+ error = None
+ for password in passwords:
+ try:
+ smtp.login(username, password)
+ break
+ except SMTPAuthenticationError as err:
+ error = err
+ else:
+ raise error from error
smtp.sendmail(username, username, msg)
smtp.quit()
imap = IMAP4_SSL(data['address'])
- imap.LOGIN(username, password)
+ error = None
+ for password in passwords:
+ try:
+ imap.LOGIN(username, password)
+ break
+ except Exception as err:
+ error = err
+ else:
+ raise error from error
imap.SELECT(readonly=True)
typ, req = imap.SEARCH(None, 'ALL')
assert typ == 'OK'
@@ -53,49 +69,67 @@ def test_imap_migration(typ, username, password):
imap.LOGOUT()
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_smtp_no_tls(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_smtp_no_tls(typ, username, passwords):
smtp = SMTP(data['address'], '587')
- try:
- smtp.login(username, password)
- raise Exception('no tls!')
- except SMTPNotSupportedError:
- pass
+ with pytest.raises(SMTPNotSupportedError):
+ smtp.login(username, passwords[0])
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_smtp_wrong_passwd(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_smtp_wrong_passwd(typ, username, passwords):
smtp = SMTP(data['address'], '587')
smtp.starttls()
- try:
+ with pytest.raises(SMTPAuthenticationError):
smtp.login(username, 'a')
- raise Exception('wrong password!')
- except SMTPAuthenticationError:
- pass
smtp.quit()
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_smtp_login(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_smtp_login(typ, username, passwords):
smtp = SMTP(data['address'], '587')
smtp.starttls()
- smtp.login(username, password)
+ error = None
+ for password in passwords:
+ try:
+ smtp.login(username, password)
+ break
+ except SMTPAuthenticationError as err:
+ error = err
+ else:
+ raise error from error
smtp.quit()
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_smtp_sendmail(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_smtp_sendmail(typ, username, passwords):
smtp = SMTP(data['address'], '587')
smtp.starttls()
- smtp.login(username, password)
+ error = None
+ for password in passwords:
+ try:
+ smtp.login(username, password)
+ break
+ except SMTPAuthenticationError as err:
+ error = err
+ else:
+ raise error from error
smtp.sendmail(username, username, get_msg(username))
smtp.quit()
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_imap_read_mail(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_imap_read_mail(typ, username, passwords):
imap = IMAP4_SSL(data['address'])
- imap.LOGIN(username, password)
+ error = None
+ for password in passwords:
+ try:
+ imap.LOGIN(username, password)
+ break
+ except Exception as err:
+ error = err
+ else:
+ raise error from error
imap.SELECT(readonly=True)
typ, req = imap.SEARCH(None, 'ALL')
assert typ == 'OK'
@@ -111,10 +145,18 @@ def test_imap_read_mail(typ, username, password):
imap.LOGOUT()
-@pytest.mark.parametrize('typ, username, password', parameters)
-def test_imap_delete_mail(typ, username, password):
+@pytest.mark.parametrize('typ, username, passwords', parameters)
+def test_imap_delete_mail(typ, username, passwords):
imap = IMAP4_SSL(data['address'])
- imap.LOGIN(username, password)
+ error = None
+ for password in passwords:
+ try:
+ imap.LOGIN(username, password)
+ break
+ except Exception as err:
+ error = err
+ else:
+ raise error from error
imap.SELECT()
typ, req = imap.SEARCH(None, 'ALL')
msg_no = req[0].split()
diff --git a/seed/gitea/DEBUG.md b/seed/gitea/DEBUG.md
index ca0ce107..26232baa 100644
--- a/seed/gitea/DEBUG.md
+++ b/seed/gitea/DEBUG.md
@@ -3,5 +3,8 @@ Créer un utilisateur
su - gitea -s /bin/bash -c "gitea admin user create --username gnunux --password Njw_csh7DeeZtWDxC6WVXDdB-9A --email gnunux@gnunux.info --admin -c /etc/gitea/app.ini"
+DEBUG
+=====
-
+sed -i 's/info/debug/g' /etc/gitea/app.ini
+systemctl restart gitea
diff --git a/seed/gitea/applicationservice.yml b/seed/gitea/applicationservice.yml
index 03b4102c..4d15a3d5 100644
--- a/seed/gitea/applicationservice.yml
+++ b/seed/gitea/applicationservice.yml
@@ -1,7 +1,7 @@
format: '0.1'
description: Gitea
depends:
- - base-fedora-35
+ - base-fedora-36
- postgresql-client
- reverse-proxy-client
- relay-mail-client
diff --git a/seed/gitea/dictionaries/31_gitea.xml b/seed/gitea/dictionaries/31_gitea.xml
index 812ea94e..094c691e 100644
--- a/seed/gitea/dictionaries/31_gitea.xml
+++ b/seed/gitea/dictionaries/31_gitea.xml
@@ -6,6 +6,7 @@
/sysusers.d/0gitea.conf
/tmpfiles.d/0gitea.conf
/etc/gitea/app.ini
+ /tests/gitea.yml
@@ -24,9 +25,11 @@
-
- /
-
+
+
+ /
+
+
3000
diff --git a/seed/gitea/templates/app.ini b/seed/gitea/templates/app.ini
index 5d12cdf3..a031250f 100644
--- a/seed/gitea/templates/app.ini
+++ b/seed/gitea/templates/app.ini
@@ -19,10 +19,10 @@ ROOT = /srv/gitea/lib/data/gitea-repositories
DEFAULT_BRANCH = main
[server]
-SSH_DOMAIN = %%revprox_client_external_domainname
-DOMAIN = %%revprox_client_external_domainname
+SSH_DOMAIN = %%revprox_client_external_domainnames[0]
+DOMAIN = %%revprox_client_external_domainnames[0]
HTTP_PORT = 3000
-ROOT_URL = https://%%revprox_client_external_domainname/gitea/
+ROOT_URL = https://%%revprox_client_external_domainnames[0]/gitea/
LOCAL_ROOT_URL = https://%%domain_name_eth0:3000/
DISABLE_SSH = false
START_SSH_SERVER = true
diff --git a/seed/gitea/templates/gitea.service b/seed/gitea/templates/gitea.service
index 3100900a..9f19bf7b 100644
--- a/seed/gitea/templates/gitea.service
+++ b/seed/gitea/templates/gitea.service
@@ -16,7 +16,7 @@ User=gitea
Group=gitea
WorkingDirectory=/srv/gitea/lib/
ExecStart=/usr/bin/gitea web --config /etc/gitea/app.ini
-ExecStartPost=-/usr/bin/timeout 90 bash -c 'while ! /usr/bin/gitea admin auth list --config /etc/gitea/app.ini | grep "OAuth2"; do echo "TRY TO CONFIGURE"; /usr/bin/gitea admin auth add-oauth --name "%%domain_name_eth0" --provider "openidConnect" --key "%%oauth2_client_id" --secret "%%oauth2_client_secret" --scopes "profile email" --auto-discover-url "https://%%oauth2_client_server_domainname/.well-known/openid-configuration" --config /etc/gitea/app.ini; sleep 2; done; echo "CONFIGURATION DONE"'
+ExecStartPre=-/bin/bash -c 'if /usr/bin/gitea admin auth list --config /etc/gitea/app.ini | grep "OAuth2"; then echo "UPDATE";id=$(/usr/bin/gitea --config /etc/gitea/app.ini admin auth list |tail -n 1|awk "{ print \$1}");/usr/bin/gitea admin auth update-oauth --id $id --name "%%domain_name_eth0" --provider "openidConnect" --key "%%oauth2_client_id" --secret "%%oauth2_client_secret" --scopes "profile email" --auto-discover-url "https://%%oauth2_client_server_domainname/.well-known/openid-configuration" --config /etc/gitea/app.ini;else echo "CONFIGURE"; /usr/bin/gitea admin auth add-oauth --name "%%domain_name_eth0" --provider "openidConnect" --key "%%oauth2_client_id" --secret "%%oauth2_client_secret" --scopes "profile email" --auto-discover-url "https://%%oauth2_client_server_domainname/.well-known/openid-configuration" --config /etc/gitea/app.ini;fi;sleep 2; echo "CONFIGURATION DONE"'
Restart=always
Environment=USER=gitea HOME=/srv/gitea/home GITEA_WORK_DIR=/srv/gitea/lib
diff --git a/seed/gitea/templates/gitea.yml b/seed/gitea/templates/gitea.yml
new file mode 100644
index 00000000..ab40fd56
--- /dev/null
+++ b/seed/gitea/templates/gitea.yml
@@ -0,0 +1,10 @@
+%set %%username="rougail_test@silique.fr"
+ip: %%ip_eth0
+revprox_ip: %%revprox_client_server_ip
+%set %%domain = %%revprox_client_external_domainnames[0]
+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)
+gitea_title: "%%gitea_title"
diff --git a/seed/gitea/tests/test_gitea.py b/seed/gitea/tests/test_gitea.py
new file mode 100644
index 00000000..003aca09
--- /dev/null
+++ b/seed/gitea/tests/test_gitea.py
@@ -0,0 +1,226 @@
+from yaml import load, SafeLoader
+from os import environ, makedirs
+from os.path import expandvars, isfile, isdir, dirname, join
+from re import search
+from dulwich.porcelain import init, clone, add, commit, push
+
+from tempfile import TemporaryDirectory
+from subprocess import run
+
+
+from revprox import Authentication
+from mookdns import MookDnsSystem
+
+
+PORT = '3000'
+GITEA_USERNAME = 'gitea'
+KEY_FILE = expandvars("$HOME/tests/risotto")
+
+
+AUTHENTICATION = None
+DATA = None
+
+
+def get_data():
+ global DATA
+ if not DATA:
+ conf_file = f'{environ["MACHINE_TEST_DIR"]}/gitea.yml'
+ with open(conf_file) as yaml:
+ DATA = load(yaml, Loader=SafeLoader)
+ return DATA
+
+
+def get_authentication(data):
+ global AUTHENTICATION
+ if not AUTHENTICATION:
+ AUTHENTICATION = Authentication(data['auth_url'],
+ data['auth_server'],
+ data['revprox_ip'],
+ data['username'],
+ data['password'],
+ f'{data["username"]} - Dashboard - {data["gitea_title"]}',
+ )
+ return AUTHENTICATION
+
+
+def get_info(authentication,
+ url,
+ with_uid=False,
+ with_data_id=False,
+ found_string=None
+ ):
+ #
+ pattern_csrf = r'name="_csrf" value="([a-zA-Z0-9\-\_=]+)"'
+ ret = authentication.get(url)
+ csrf = search(pattern_csrf, ret)[1]
+ ret_data = []
+ if with_uid:
+ pattern_uid = r'input type="hidden" id="uid" name="uid" value="(\d)+"'
+ uid = search(pattern_uid, ret)
+ if uid is None:
+ ret_data.append(uid)
+ else:
+ ret_data.append(uid[1])
+ if with_data_id:
+ pattern_uid = r'/user/settings/keys/delete?type=ssh" data-id="(\d)+"'
+ uid = search(pattern_uid, ret)
+ if uid is None:
+ ret_data.append(uid)
+ else:
+ ret_data.append(uid[1])
+ if found_string:
+ ret_data.append(found_string in ret)
+ ret_data.append(csrf)
+ if len(ret_data) == 1:
+ return ret_data[0]
+ return ret_data
+
+
+def add_ssh_key(authentication, data):
+ # Send key to gitea
+ url = f'{data["base_url"]}user/settings/keys'
+ is_already_key, csrf = get_info(authentication, url, found_string='test_key_risotto')
+ if is_already_key:
+ return
+ # Gen SSH key if needed
+ if not isfile(KEY_FILE):
+ key_dir = dirname(KEY_FILE)
+ if not isdir(key_dir):
+ makedirs(key_dir)
+ cmd = ['/usr/bin/ssh-keygen', '-N', '', '-f', KEY_FILE]
+ run(cmd)
+ with open(f'{KEY_FILE}.pub') as fh:
+ key = fh.read()
+ authentication.post(url, {'_csrf': csrf, 'title': 'test_key_risotto', 'content': key, 'type': 'ssh'})
+
+
+def delete_ssh_key(authentication, data):
+ url = f'{data["base_url"]}user/settings/keys'
+ is_already_key, csrf = get_info(authentication, url, found_string='test_key_risotto')
+ if is_already_key:
+ uid, csrf = get_info(authentication, url, with_data_id=True)
+ url = f'{data["base_url"]}user/settings/keys/delete?type=ssh'
+ authentication.post(url, {'_csrf': csrf, 'id': uid})
+ is_already_key, csrf = get_info(authentication, url, found_string='test_key_risotto')
+
+
+def test_gitea():
+ data = get_data()
+ get_authentication(data)
+
+
+def test_gitea_repos():
+ data = get_data()
+ authentication = get_authentication(data)
+ if 'FIRST_RUN' in environ:
+ url = f'{data["base_url"]}repo/create'
+ uid, csrf = get_info(authentication, url, with_uid=True)
+ authentication.post(url, {'_csrf': csrf, 'uid': uid, 'repo_name': 'test_persistent'})
+ url = f'{data["base_url"]}api/v1/repos/search?sort=updated&order=desc&uid=1&team_id=0&q=&page=1&mode='
+ json = authentication.get(url, json=True)
+ assert json['ok']
+ assert len(json['data']) == 1
+ username = data['username'].split('@', 1)[0]
+ assert json['data'][0]['full_name'] == f'{username}/test_persistent'
+
+
+def test_gitea_create_repo():
+ data = get_data()
+ authentication = get_authentication(data)
+ url = f'{data["base_url"]}repo/create'
+ uid, csrf = get_info(authentication, url, with_uid=True)
+ authentication.post(url, {'_csrf': csrf, 'uid': uid, 'repo_name': 'test', 'default_branch': 'main'})
+ url = f'{data["base_url"]}api/v1/repos/search?sort=updated&order=desc&uid=1&team_id=0&q=&page=1&mode='
+ json = authentication.get(url, json=True)
+ assert json['ok']
+ assert len(json['data']) == 2
+ username = data['username'].split('@', 1)[0]
+ assert {dat['full_name'] for dat in json['data']} == set([f'{username}/test_persistent', f'{username}/test'])
+
+
+def test_repo():
+ data = get_data()
+ authentication = get_authentication(data)
+ if 'FIRST_RUN' in environ:
+ # delete_ssh_key(authentication, data)
+ add_ssh_key(authentication, data)
+ with TemporaryDirectory() as tmpdirname:
+ username = data['username'].split('@', 1)[0]
+ dns = data['base_url'].split('/', 3)[2]
+ ssh_url = f'ssh://{GITEA_USERNAME}@{dns}:2222/{username}/test.git'
+ with MookDnsSystem(dns, data['ip']):
+ filename = join(tmpdirname, 'test.txt')
+ with open(filename, 'w') as fh:
+ fh.write('test')
+ repo = init(tmpdirname)
+ add(repo, filename)
+ commit(repo, message=b'test commit')
+ push(repo=repo,
+ remote_location=ssh_url,
+ refspecs='master',
+ )
+ lst = list(repo.get_walker())
+ assert len(lst) == 1
+ assert lst[0].commit.message == b'test commit'
+
+
+def test_clone_http():
+ data = get_data()
+ authentication = get_authentication(data)
+ if 'FIRST_RUN' in environ:
+ # delete_ssh_key(authentication, data)
+ add_ssh_key(authentication, data)
+ with TemporaryDirectory() as tmpdirname:
+ username = data['username'].split('@', 1)[0]
+ dns = data['base_url'].split('/', 3)[2]
+ http_url = f'{data["base_url"]}{username}/test.git'
+ with MookDnsSystem(dns, data['revprox_ip']):
+ repo = clone(http_url, tmpdirname)
+ lst = list(repo.get_walker())
+ assert len(lst) == 1
+ assert lst[0].commit.message == b'test commit'
+
+
+def test_gitea_delete_repo():
+ repo_name = 'test'
+ data = get_data()
+ authentication = get_authentication(data)
+ username = data['username'].split('@', 1)[0]
+ url = f'{data["base_url"]}{username}/{repo_name}/settings'
+ csrf = get_info(authentication, url)
+ authentication.post(url, {'_csrf': csrf, 'action': 'delete', 'repo_name': repo_name})
+ url = f'{data["base_url"]}api/v1/repos/search?sort=updated&order=desc&uid=1&team_id=0&q=&page=1&mode='
+ json = authentication.get(url, json=True)
+ assert json['ok']
+ assert len(json['data']) == 1
+ username = data['username'].split('@', 1)[0]
+ assert json['data'][0]['full_name'] == f'{username}/test_persistent'
+
+
+def test_repo_persistent():
+ data = get_data()
+ authentication = get_authentication(data)
+ if 'FIRST_RUN' in environ:
+ # delete_ssh_key(authentication, data)
+ add_ssh_key(authentication, data)
+ with TemporaryDirectory() as tmpdirname:
+ username = data['username'].split('@', 1)[0]
+ dns = data['base_url'].split('/', 3)[2]
+ ssh_url = f'ssh://{GITEA_USERNAME}@{dns}:2222/{username}/test_persistent.git'
+ with MookDnsSystem(dns, data['ip']):
+ if 'FIRST_RUN' in environ:
+ filename = join(tmpdirname, 'test.txt')
+ with open(filename, 'w') as fh:
+ fh.write('test')
+ repo = init(tmpdirname)
+ add(repo, filename)
+ commit(repo, message=b'test commit')
+ push(repo=repo,
+ remote_location=ssh_url,
+ refspecs='master',
+ )
+ else:
+ repo = clone(ssh_url, tmpdirname)
+ lst = list(repo.get_walker())
+ assert len(lst) == 1
+ assert lst[0].commit.message == b'test commit'
diff --git a/seed/host-systemd-machined/dictionaries/21-machined.xml b/seed/host-systemd-machined/dictionaries/21-machined.xml
index 3e9636f0..219d9f8f 100644
--- a/seed/host-systemd-machined/dictionaries/21-machined.xml
+++ b/seed/host-systemd-machined/dictionaries/21-machined.xml
@@ -24,7 +24,7 @@
-
+
@@ -64,10 +64,6 @@
True
systemd_netzone_filename
-
- server_name
- host_name
-
zone_name
cidr
diff --git a/seed/host-systemd-machined/extras/machined/00-machined.xml b/seed/host-systemd-machined/extras/machined/00-machined.xml
index c75f73d9..4f31f93c 100644
--- a/seed/host-systemd-machined/extras/machined/00-machined.xml
+++ b/seed/host-systemd-machined/extras/machined/00-machined.xml
@@ -7,14 +7,14 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/seed/imap-client/dictionaries/21_imap_client.xml b/seed/imap-client/dictionaries/21_imap_client.xml
index 50e7392b..065ec0ea 100644
--- a/seed/imap-client/dictionaries/21_imap_client.xml
+++ b/seed/imap-client/dictionaries/21_imap_client.xml
@@ -7,14 +7,7 @@
-
+
-
-
- zone_name_eth0
- IMAP
- imap_address
-
-
diff --git a/seed/ldap-client/dictionaries/21_ldap-client.xml b/seed/ldap-client/dictionaries/21_ldap-client.xml
index 362e6f9b..ac1ce2f4 100644
--- a/seed/ldap-client/dictionaries/21_ldap-client.xml
+++ b/seed/ldap-client/dictionaries/21_ldap-client.xml
@@ -12,16 +12,19 @@
-
+
636
-
-
-
-
+
+
+
+
+
+
+
@@ -38,10 +41,23 @@
ldapclient_base_dn
-
- zone_name_eth0
- LDAP
- ldap_server_address
+
+ ldap_server_address
+ ldapclient_base_dn
+
+
+ ou=accounts
+ ldapclient_base_dn
+ ,
+ ldapclient_search_dn
+
+
+ cn=
+ domain_name_eth0
+ ,
+ ldapclient_base_dn
+
+ ldapclient_user
tls_ca_directory
@@ -61,16 +77,6 @@
/
ldap_key_file
-
- ldap_server_address
- clients
- domain_name_eth0
- client_family
- ldapclient_family
- True
- dn
- ldapclient_user
-
ldap_server_address
ldapclient_user
@@ -80,13 +86,14 @@
True
ldapclient_user_password
-
- ldap_server_address
- client_password
- ldapclient_user_password
- base_dn
- domain_name_eth0
- ldapclient_base_dn
+
+ ldapclient_base_dn
+ True
+ ldapclient_group_dn
+
+
+ ldapclient_base_dn
+ ldapclient_user_dn
diff --git a/seed/ldap-client/funcs/openldap_client.py b/seed/ldap-client/funcs/openldap_client.py
index 59789164..e67591b7 100644
--- a/seed/ldap-client/funcs/openldap_client.py
+++ b/seed/ldap-client/funcs/openldap_client.py
@@ -11,6 +11,8 @@ def calc_ldapclient_base_dn(ldap_base_dn: str,
base: bool=False,
group: bool=False,
) -> str:
+ if ldap_base_dn is None:
+ return
if family_name == 'all':
family_name = None
base = True
@@ -28,3 +30,23 @@ def calc_ldapclient_base_dn(ldap_base_dn: str,
if family_name != '-':
base_name = f'ou={family_name},{base_name}'
return base_name
+
+
+class _Undefined:
+ pass
+
+
+_undefined = _Undefined()
+
+
+def get_default_base_dn(server_name: str) -> str:
+ if not server_name or '.' not in server_name:
+ return None
+ values = server_name.split('.')
+ # cannot calculated base dn should be server.domain.tld
+ # remove 'server' in dn
+ if len(values) < 3:
+ return None
+ domain = ['ou=' + domain for domain in values[1:-2]]
+ domain.append(f'o={values[-2]},o={values[-1]}')
+ return ','.join(domain)
diff --git a/seed/ldap-client/templates/ldap.conf b/seed/ldap-client/templates/ldap.conf
index 7c40a7f5..3a65745d 100644
--- a/seed/ldap-client/templates/ldap.conf
+++ b/seed/ldap-client/templates/ldap.conf
@@ -6,7 +6,7 @@
# This file should be world readable but not world writable.
#BASE dc=example,dc=com
-BASE %%ldapclient_base_dn
+BASE %%ldapclient_search_dn
#URI ldap://ldap.example.com ldap://ldap-master.example.com:666
URI ldaps://%%ldap_server_address:%%ldap_port
diff --git a/seed/lemonldap/applicationservice.yml b/seed/lemonldap/applicationservice.yml
index f715b6a0..723261ea 100644
--- a/seed/lemonldap/applicationservice.yml
+++ b/seed/lemonldap/applicationservice.yml
@@ -6,4 +6,3 @@ depends:
- reverse-proxy-client
- relay-mail-client
- nginx-common
-provider: OAuth2
diff --git a/seed/lemonldap/dictionaries/70_lemonldap_ng.xml b/seed/lemonldap/dictionaries/70_lemonldap_ng.xml
index bf229ac1..37b7de86 100644
--- a/seed/lemonldap/dictionaries/70_lemonldap_ng.xml
+++ b/seed/lemonldap/dictionaries/70_lemonldap_ng.xml
@@ -28,6 +28,7 @@
False
+
@@ -40,15 +41,13 @@
all
-
-
- ldap_server_address
- ldap_group
- ldapclient_group_dn
+
+ revprox_client_external_domainnames
+ oauth2_client_external_domain
diff --git a/seed/lemonldap/extras/oauth2/00_oauth2.xml b/seed/lemonldap/extras/oauth2/00_oauth2.xml
index 924ef09e..2f4a1c92 100644
--- a/seed/lemonldap/extras/oauth2/00_oauth2.xml
+++ b/seed/lemonldap/extras/oauth2/00_oauth2.xml
@@ -1,23 +1,30 @@
-
+
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
-
+
+
HS512
RS256
+
+
+
+ oauth2.remotes
+ oauth2.clients
+
+
diff --git a/seed/lemonldap/templates/lemonldap-ng-fastcgi-server.service b/seed/lemonldap/templates/lemonldap-ng-fastcgi-server.service
index 984b4f0e..f61277e7 100644
--- a/seed/lemonldap/templates/lemonldap-ng-fastcgi-server.service
+++ b/seed/lemonldap/templates/lemonldap-ng-fastcgi-server.service
@@ -3,5 +3,5 @@ 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 5; 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'
diff --git a/seed/lemonldap/templates/lemonldap.yml b/seed/lemonldap/templates/lemonldap.yml
index 73fcfdbc..07bd77a4 100644
--- a/seed/lemonldap/templates/lemonldap.yml
+++ b/seed/lemonldap/templates/lemonldap.yml
@@ -1,3 +1,3 @@
-address: %%revprox_client_external_domainname
+address: %%revprox_client_external_domainnames[0]
internal_address: %%domain_name_eth0
ip: %%ip_eth0
diff --git a/seed/lemonldap/templates/lmConf-1.json b/seed/lemonldap/templates/lmConf-1.json
index c7dd06fd..2a084980 100644
--- a/seed/lemonldap/templates/lmConf-1.json
+++ b/seed/lemonldap/templates/lmConf-1.json
@@ -13,7 +13,7 @@ commentStartToken = §
"ldapPpolicyControl" : 1,
"ldapAllowResetExpiredPassword" : 1,
"ldapChangePasswordAsUser" : 1,
- "ldapBase" : "%%ldapclient_base_dn",
+ "ldapBase" : "%%ldapclient_search_dn",
"ldapExportedVars" : {
"uid" : "uid",
"cn" : "cn",
@@ -41,7 +41,7 @@ commentStartToken = §
"mail" : "mail",
"uid" : "uid"
},
- "domain" : "%%revprox_client_external_domainname",
+ "domain" : "%%revprox_client_external_domainnames[0]",
"exportedVars" : {
"UA" : "HTTP_USER_AGENT",
"cn" : "cn",
@@ -60,21 +60,21 @@ commentStartToken = §
"namespace" : "lemonldap-ng-sessions"
},
"locationRules" : {
- "%%revprox_client_external_domainname" : {
+ "%%revprox_client_external_domainnames[0]" : {
"default" : "accept"
%set %%domains = []
%for %%app in %%oauth2.remotes
%set %%key = %%normalize_family(%%app)
§ somethink like ['https://domain/']
%for %%external in %%oauth2['oauth2_' + %%key]['external_' + %%key]['hosts_' + %%key]
- %set %%domain = %%str(%%external).split('/', 3)[-2]
- %if %%domain not in %%domains
+ %set %%domain = %%str(%%external).split('/', 3)[-2]
+ %if %%domain not in %%domains
},
"%%domain" : {
"^/logout" : "logout_sso",
"default" : "$groups eq \"%%external['family_' + %%key]\""
%%domains.append(%%domain)%slurp
- %end if
+ %end if
%end for
%end for
}
@@ -84,7 +84,7 @@ commentStartToken = §
"UA" : "$ENV{HTTP_USER_AGENT}",
"_whatToTrace" : "$_auth eq 'SAML' ? lc($_user.'@'.$_idpConfKey) : $_auth eq 'OpenIDConnect' ? lc($_user.'@'.$_oidc_OP) : lc($_user)"
},
- "mailUrl" : "https://%%revprox_client_external_domainname/resetpwd",
+ "mailUrl" : "https://%%revprox_client_external_domainnames[0]/resetpwd",
"mySessionAuthorizedRWKeys" : [
"_appsListOrder",
"_oidcConnectedRP",
@@ -161,12 +161,13 @@ commentStartToken = §
"Directory": "/srv/lemonldap-ng/psessions",
"LockDirectory": "/srv/lemonldap-ng/psessions/lock"
},
- "portal" : "https://%%revprox_client_external_domainname/",
+ "portal" : "https://%%revprox_client_external_domainnames[0]/",
"portalCheckLogins": 0,
"portalDisplayRegister": 0,
"portalDisplayResetPassword": 0,
"portalMainLogo": "risotto/logo.png",
"showLanguages": 0,
+ "requireToken": "$env->{REMOTE_ADDR} ne '%%gateway_eth0'",
"whatToTrace" : "_whatToTrace",
%set %%remotes = {}
%for %%index, %%app in %%enumerate(%%oauth2.remotes)
diff --git a/seed/lemonldap/templates/portal-nginx.conf b/seed/lemonldap/templates/portal-nginx.conf
index 54087f9c..45ad1591 100644
--- a/seed/lemonldap/templates/portal-nginx.conf
+++ b/seed/lemonldap/templates/portal-nginx.conf
@@ -48,7 +48,7 @@ server {
# GNUNUX server_name auth.example.com;
#>GNUNUX
listen 443 ssl;
- server_name %%revprox_client_external_domainname;
+ server_name %%{revprox_client_external_domainnames[0]};
ssl_certificate %%revprox_cert_file;
ssl_certificate_key %%revprox_key_file;
ssl_client_certificate %%revprox_ca_file;
diff --git a/seed/letsencrypt/funcs/letsencrypt.py b/seed/letsencrypt/funcs/letsencrypt.py
index 170ca950..9a2ba330 100644
--- a/seed/letsencrypt/funcs/letsencrypt.py
+++ b/seed/letsencrypt/funcs/letsencrypt.py
@@ -56,7 +56,8 @@ def letsencrypt_certif(domain: str,
]
ret = _run(cli_args, capture_output=True)
if ret.returncode != 0:
- raise ValueError(ret.stderr.decode())
+ print("FIXME")
+ #raise ValueError(ret.stderr.decode())
print("Done")
with open(date_file, 'w') as fh:
fh.write(today)
diff --git a/seed/mailman/applicationservice.yml b/seed/mailman/applicationservice.yml
index 7ade44c6..769c5867 100644
--- a/seed/mailman/applicationservice.yml
+++ b/seed/mailman/applicationservice.yml
@@ -3,7 +3,7 @@ description: Gestionnaire de liste de diffusion Mailman
depends:
- base-fedora-35
- postgresql-client
- - relay-mail-client
+ - relay-lmtp-client
- reverse-proxy-client
- nginx-common
- oauth2-client
diff --git a/seed/mailman/extras/mailman/20_mailman.xml b/seed/mailman/extras/mailman/20_mailman.xml
index 4811ffed..97066c77 100644
--- a/seed/mailman/extras/mailman/20_mailman.xml
+++ b/seed/mailman/extras/mailman/20_mailman.xml
@@ -5,7 +5,7 @@
-
+
@@ -17,18 +17,6 @@
mailman.list_.names_
mailman.names_
-
- smtp_relay_address
- lmtp_server
- domain_name_eth0
- mailman.names_
-
-
- smtp_relay_address
- lmtp_criteria
- domain_name_eth0
- mailman.names_
-
diff --git a/seed/mailman/templates/postorius-settings.py b/seed/mailman/templates/postorius-settings.py
index 7d26767b..64cf818b 100644
--- a/seed/mailman/templates/postorius-settings.py
+++ b/seed/mailman/templates/postorius-settings.py
@@ -13,9 +13,9 @@ DATABASES = {
'OPTIONS': {'sslmode': 'verify-full', 'sslcert': '/etc/pki/tls/certs/postgresql.crt', 'sslkey': '/etc/pki/tls/private/postgresql_postorius.key', 'sslrootcert': '/etc/pki/ca-trust/source/anchors/ca_PostgreSQL.crt'},
}
}
-ALLOWED_HOSTS = ['%%revprox_client_external_domainname']
-POSTORIUS_TEMPLATE_BASE_URL = 'https://%%revprox_client_external_domainname'
-CSRF_TRUSTED_ORIGINS = ['%%revprox_client_external_domainname']
+ALLOWED_HOSTS = ['%%{revprox_client_external_domainnames[0]}']
+POSTORIUS_TEMPLATE_BASE_URL = 'https://%%{revprox_client_external_domainnames[0]}'
+CSRF_TRUSTED_ORIGINS = ['%%{revprox_client_external_domainnames[0]}']
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
LANGUAGE_CODE = 'fr'
diff --git a/seed/mailman/templates/postorius.service b/seed/mailman/templates/postorius.service
index 484c26fa..1f29c6c4 100644
--- a/seed/mailman/templates/postorius.service
+++ b/seed/mailman/templates/postorius.service
@@ -19,7 +19,7 @@ RestrictRealtime=yes
PrivateMounts=yes
Environment="MAILMAN_WEB_CONFIG=/usr/share/postorius/m_postorius/settings.py"
ExecStartPre=/usr/share/postorius/manage.py migrate
-ExecStartPre=/usr/share/postorius/manage.py shell -c 'from django.contrib.sites.models import Site; site=Site.objects.first(); site.name="%%revprox_client_external_domainname"; site.domain="%%revprox_client_external_domainname"; site.save()'
+ExecStartPre=/usr/share/postorius/manage.py shell -c 'from django.contrib.sites.models import Site; site=Site.objects.first(); site.name="%%{revprox_client_external_domainnames[0]}"; site.domain="%%{revprox_client_external_domainnames[0]}"; site.save()'
ExecStartPre=/usr/share/postorius/manage.py shell -c 'from allauth.socialaccount.models import SocialApp; SocialApp.objects.create() if SocialApp.objects.count() == 0 else print("social app already exists"); a=SocialApp.objects.first(); a.name = "%%domain_name_eth0"; a.provider = "risotto"; a.client_id = "%%oauth2_client_id"; a.secret = "%%oauth2_client_secret"; a.sites.set([1]); a.save()'
ExecStartPre=-/usr/share/postorius/manage.py createsuperuser --username "%%mailman_mail_owner" --email "%%mailman_mail_owner" --noinput
ExecStart=/usr/bin/gunicorn --config /etc/postorius/gunicorn_config.py m_postorius.wsgi
diff --git a/seed/mariadb-client/dictionaries/20_mariadb.xml b/seed/mariadb-client/dictionaries/20_mariadb.xml
index 23fbd224..39ac585e 100644
--- a/seed/mariadb-client/dictionaries/20_mariadb.xml
+++ b/seed/mariadb-client/dictionaries/20_mariadb.xml
@@ -5,33 +5,28 @@
-
+
-
+
-
- zone_name_eth0
- MariaDB
- mariadb_client_server_domainname
-
-
- mariadb_client_server_domainname
- clients
- domain_name_eth0
+
+ domain_name_eth0
mariadb_client_username
-
- mariadb_client_server_domainname
- client_password
- mariadb_client_username
- mariadb_client_password
-
mariadb_client_username
mariadb_client_database
+
+ mariadb_client_server_domainname
+ domain_name_eth0
+ remote
+ cleartext
+ hide_secret
+ mariadb_client_password
+
diff --git a/seed/mariadb/applicationservice.yml b/seed/mariadb/applicationservice.yml
index ed6250a8..a0d982e3 100644
--- a/seed/mariadb/applicationservice.yml
+++ b/seed/mariadb/applicationservice.yml
@@ -1,6 +1,4 @@
format: '0.1'
description: Mariadb
depends:
- - server
- - base-fedora-35
-provider: MariaDB
+ - base-fedora-36
diff --git a/seed/mariadb/extras/accounts/00_accounts.xml b/seed/mariadb/extras/accounts/00_accounts.xml
new file mode 100644
index 00000000..63f1451d
--- /dev/null
+++ b/seed/mariadb/extras/accounts/00_accounts.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/seed/mariadb/templates/mariadb.sql b/seed/mariadb/templates/mariadb.sql
index d77a9738..7c3f0657 100644
--- a/seed/mariadb/templates/mariadb.sql
+++ b/seed/mariadb/templates/mariadb.sql
@@ -10,4 +10,3 @@ CREATE DATABASE IF NOT EXISTS %%name CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON %%name.* TO '%%name'@'%%server' IDENTIFIED BY '%%password';
%end for
FLUSH PRIVILEGES;
-
diff --git a/seed/nextcloud/dictionaries/31_nextcloud.xml b/seed/nextcloud/dictionaries/31_nextcloud.xml
index ef6a93e2..26e5a2e7 100644
--- a/seed/nextcloud/dictionaries/31_nextcloud.xml
+++ b/seed/nextcloud/dictionaries/31_nextcloud.xml
@@ -5,7 +5,7 @@
/etc/nextcloud/config.php
- /etc/nextcloud/nextcloud.init
+ /sbin/nextcloud.init
/etc/httpd/conf.d/a-nextcloud-access.conf
/etc/httpd/conf.d/z-nextcloud-access.conf
/etc/php.d/20-pgsql.ini
@@ -66,38 +66,12 @@
hide_secret
nextcloud_instance_id
-
- revprox_client_external_domainnames
- nextcloud_well_known_server
-
-
domain_name_eth0
revprox_client_port
/.well-known/caldav
nextcloud_well_known_caldav
-
domain_name_eth0
revprox_client_port
diff --git a/seed/nextcloud/templates/nextcloud-config.php b/seed/nextcloud/templates/nextcloud-config.php
index ceb39df0..05569b4b 100644
--- a/seed/nextcloud/templates/nextcloud-config.php
+++ b/seed/nextcloud/templates/nextcloud-config.php
@@ -11,7 +11,7 @@ $CONFIG = array (
'trusted_domains' =>
array (
0 => 'localhost',
- 1 => '%%revprox_client_external_domainname',
+ 1 => '%%revprox_client_external_domainnames[0]',
),
'apps_paths' =>
array (
@@ -49,7 +49,7 @@ $CONFIG = array (
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'trusted_proxies' => '%%revprox_client_server_ip',
- 'overwritehost' => '%%revprox_client_external_domainname',
+ 'overwritehost' => '%%revprox_client_external_domainnames[0]',
'filelocking.enabled' => true,
'redis' => [
'host' => '%%redis_client_server_domainname',
diff --git a/seed/nextcloud/templates/nextcloud.init b/seed/nextcloud/templates/nextcloud.init
index f68c9595..81293361 100644
--- a/seed/nextcloud/templates/nextcloud.init
+++ b/seed/nextcloud/templates/nextcloud.init
@@ -29,9 +29,9 @@ fi
/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapPort "%%ldap_port"
/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapAgentName "%%ldapclient_user"
/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapAgentPassword "%%ldapclient_user_password"
-/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapBase "%%ldapclient_base_dn"
-/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapBaseUsers "%%ldapclient_base_dn"
-/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapBaseGroups "%%ldapclient_base_dn"
+/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapBase "%%ldapclient_search_dn"
+/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapBaseUsers "%%ldapclient_user_dn"
+/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapBaseGroups "%%ldapclient_group_dn"
/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapExperiencedAdmin "0"
/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapExpertUUIDUserAttr "cn"
/usr/bin/php /usr/share/nextcloud/occ ldap:set-config s01 ldapLoginFilter "(&(cn=%uid)(ObjectClass=inetOrgPerson))"
diff --git a/seed/nextcloud/templates/nextcloud.service b/seed/nextcloud/templates/nextcloud.service
index 052de7a3..0a9339fe 100644
--- a/seed/nextcloud/templates/nextcloud.service
+++ b/seed/nextcloud/templates/nextcloud.service
@@ -8,7 +8,7 @@ Type=oneshot
WorkingDirectory=/usr/share/nextcloud
#FIXME
ExecStart=+/usr/bin/chmod +w /etc/nextcloud/config.php
-ExecStart=/etc/nextcloud/nextcloud.init
+ExecStart=/usr/local/lib/sbin/nextcloud.init
ExecStart=+/usr/bin/chmod -w /etc/nextcloud/config.php
User=apache
Group=apache
diff --git a/seed/nginx-reverse-proxy/applicationservice.yml b/seed/nginx-reverse-proxy/applicationservice.yml
index b8c2c665..486d2385 100644
--- a/seed/nginx-reverse-proxy/applicationservice.yml
+++ b/seed/nginx-reverse-proxy/applicationservice.yml
@@ -3,4 +3,3 @@ description: Nginx as reverse proxy
depends:
- base-fedora-36
- nginx-common
-provider: ReverseProxy
diff --git a/seed/nginx-reverse-proxy/dictionaries/25_nginx.xml b/seed/nginx-reverse-proxy/dictionaries/25_nginx.xml
index 3572c6fe..3ed3d35b 100644
--- a/seed/nginx-reverse-proxy/dictionaries/25_nginx.xml
+++ b/seed/nginx-reverse-proxy/dictionaries/25_nginx.xml
@@ -5,8 +5,8 @@
/etc/nginx/conf.d/options-rp.conf
/etc/nginx/conf.d/risotto.conf
- nginx_certificate_filename
- nginx_private_key_filename
+ nginx.nginx_certificate_filename
+ nginx.nginx_private_key_filename
/tests/reverse-proxy.yml
@@ -22,34 +22,6 @@
True
-
-
-
-
-
-
-
- revprox_domainnames
- revprox_domainnames_auto
- revprox_domainnames_all
-
-
- /etc/pki/tls/certs/
- revprox_domainnames_all
- .crt
-
- True
- nginx_certificate_filename
-
-
- /etc/pki/tls/private/
- revprox_domainnames_all
- .key
-
- True
- nginx_private_key_filename
-
-
diff --git a/seed/nginx-reverse-proxy/extras/nginx/00-nginx.xml b/seed/nginx-reverse-proxy/extras/nginx/00-nginx.xml
index 8c37ebb0..c80da867 100644
--- a/seed/nginx-reverse-proxy/extras/nginx/00-nginx.xml
+++ b/seed/nginx-reverse-proxy/extras/nginx/00-nginx.xml
@@ -1,16 +1,40 @@
-
-
- False
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ nginx.reverse_proxy_for_.reverse_proxy_.revprox_domainnames_
+ nginx.revprox_domainnames
+
+
+ /etc/pki/tls/certs/
+ nginx.revprox_domainnames
+ .crt
+
+ True
+ nginx.nginx_certificate_filename
+
+
+ /etc/pki/tls/private/
+ nginx.revprox_domainnames
+ .key
+
+ True
+ nginx.nginx_private_key_filename
+
+
diff --git a/seed/nginx-reverse-proxy/funcs/nginx.py b/seed/nginx-reverse-proxy/funcs/nginx.py
index 6f84a4ef..6496b443 100644
--- a/seed/nginx-reverse-proxy/funcs/nginx.py
+++ b/seed/nginx-reverse-proxy/funcs/nginx.py
@@ -1,9 +1,11 @@
-from typing import List as _List
-from risotto.utils import multi_function
+from risotto.utils import multi_function as _multi_function
-@multi_function
-def nginx_concat_lists(list1: _List[str],
- list2: _List[str],
- ) -> _List[str]:
- return list1 + list2
+@_multi_function
+def nginx_list(lst):
+ ret = []
+ for l in lst:
+ ret.extend(l)
+ ret = list(set(ret))
+ ret.sort()
+ return ret
diff --git a/seed/nginx-reverse-proxy/templates/nginx.service b/seed/nginx-reverse-proxy/templates/nginx.service
index db75ec16..7a327997 100644
--- a/seed/nginx-reverse-proxy/templates/nginx.service
+++ b/seed/nginx-reverse-proxy/templates/nginx.service
@@ -1,9 +1,9 @@
%set %%domains = set()
-%for %%domainname in %%revprox_domainnames_all
+%for %%domainname in %%nginx.remotes
%set %%family = %%normalize_family(%%domainname)
%set %%revprox = %%nginx['reverse_proxy_for_' + family]['reverse_proxy_' + family]
- %for %%location in %%revprox['revprox_location_' + family]
- %set %%domain = %%location['revprox_url_' + family].split('/', 3)[2].split(':')[0]
+ %for %%domain in %%revprox['revprox_domainnames_' + family]
+ %set %%domain = %%domain['revprox_url_' + family].split('/', 3)[2].split(':')[0]
%%domains.add(%%domain)%slurp
%end for
%end for
diff --git a/seed/nginx-reverse-proxy/templates/reverse-proxy.yml b/seed/nginx-reverse-proxy/templates/reverse-proxy.yml
index c4dbdcec..2c856d76 100644
--- a/seed/nginx-reverse-proxy/templates/reverse-proxy.yml
+++ b/seed/nginx-reverse-proxy/templates/reverse-proxy.yml
@@ -1,10 +1,12 @@
address: %%ip_eth0
urls:
-%for %%domain in %%revprox_domainnames_all
+%for %%domain in %%nginx.remotes
%set %%suffix = %%normalize_family(%%domain)
- %for %%location in %%nginx['reverse_proxy_for_' + %%suffix]['reverse_proxy_' + %%suffix]['revprox_location_' + %%suffix]
- %if not %%location['revprox_is_websocket_' + %%suffix]
-- %%domain%%location
- %end if
+ %for %%revprox in %%nginx['reverse_proxy_for_' + %%suffix]['reverse_proxy_' + %%suffix]['revprox_domainnames_' + %%suffix]
+ %for %%loc_idx, %%location in %%enumerate(%%revprox['revprox_location_' + %%suffix])
+ %if not %%revprox['revprox_is_websocket_' + %%suffix][%%loc_idx]
+- %%revprox%%location
+ %end if
+ %end for
%end for
%end for
diff --git a/seed/nginx-reverse-proxy/templates/revprox-nginx.conf b/seed/nginx-reverse-proxy/templates/revprox-nginx.conf
index f0763ac1..c1c7ff68 100644
--- a/seed/nginx-reverse-proxy/templates/revprox-nginx.conf
+++ b/seed/nginx-reverse-proxy/templates/revprox-nginx.conf
@@ -1,7 +1,4 @@
-%for %%idx, %%domainname in %%enumerate(%%revprox_domainnames_all)
- %set %%family = %%normalize_family(%%domainname)
- %set %%revprox = %%nginx['reverse_proxy_for_' + family]['reverse_proxy_' + family]
- %set %%wildcard = %%nginx['reverse_proxy_for_' + family]['revprox_domain_wildcard_' + family]
+%for %%idx, %%domainname in %%enumerate(%%nginx.revprox_domainnames)
# Configuration HTTP %%domainname
server {
listen 80;
@@ -12,23 +9,29 @@ server {
# Configuration HTTPS %%domainname
server {
listen 443 ssl http2;
- ssl_certificate %%nginx_certificate_filename[%%idx];
- ssl_certificate_key %%nginx_private_key_filename[%%idx];
+ ssl_certificate %%nginx.nginx_certificate_filename[%%idx];
+ ssl_certificate_key %%nginx.nginx_private_key_filename[%%idx];
server_name %%domainname;
error_page 403 404 502 503 504 /error.html;
location = /error.html{
root /var/www/html;
}
- %for %%location in %%revprox['revprox_location_' + family]
- %set %%location_str = %%str(%%location)
+ %for %%remote in %%nginx.remotes
+ %set %%family = %%normalize_family(%%remote)
+ %set %%revprox = %%nginx['reverse_proxy_for_' + %%family]['reverse_proxy_' + %%family]
+ %for %%rp_domainname in %%revprox['revprox_domainnames_' + %%family]
+ %if %%domainname != %%str(%%rp_domainname)
+ %continue
+ %end if
+ %for %%loc_idx, %%location in %%enumerate(%%rp_domainname['revprox_location_' + %%family])
location %%location {
- proxy_pass %%location['revprox_url_' + family];
- %if %%location['revprox_is_websocket_' + family]
+ proxy_pass %%rp_domainname['revprox_url_' + %%family];
+ %if %%rp_domainname['revprox_is_websocket_' + %%family][%%loc_idx]
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
- %else
+ %else
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
@@ -37,25 +40,28 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Destination $dest;
- %end if
+ %end if
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;
- %set %%maxbody = %%location['revprox_max_body_size_' + family]
- %if %%maxbody
+ %set %%maxbody = %%rp_domainname['revprox_max_body_size_' + %%family]
+ %if %%maxbody
client_max_body_size %%maxbody;
- %end if
+ %end if
set $dest $http_destination;
index error.html;
root /var/www/html;
}
# If user missing '/'
- %if %%location_str != '/' and %%location_str.endswith('/')
- location %%location_str[:-1] {
- rewrite ^(%%location_str[:-1])$ $1/ permanent;
+ %if %%location != '/' and %%location.endswith('/')
+ location %%location[:-1] {
+ rewrite ^(%%location[:-1])$ $1/ permanent;
}
- %end if
+ %end if
+ %end for
+ %end for
%end for
}
+
%end for
diff --git a/seed/nginx-reverse-proxy/tests/test_revprox.py b/seed/nginx-reverse-proxy/tests/test_revprox.py
index ea50b26f..d48cc788 100644
--- a/seed/nginx-reverse-proxy/tests/test_revprox.py
+++ b/seed/nginx-reverse-proxy/tests/test_revprox.py
@@ -16,7 +16,12 @@ def req(url, ip, verify=True):
dns[-1] = (ip, dns[-1][1])
return [dns]
socket.getaddrinfo = new_getaddrinfo
- ret = get(url, verify=verify)
+ 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
@@ -29,9 +34,7 @@ def test_revprox():
data = load(yaml, Loader=SafeLoader)
# test unknown domain
url = 'google.fr'
- with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- ret_code, content = req(f'https://{url}', data['address'], verify=False)
+ 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 "
Test Page for the HTTP Server on Fedora" in content, f'https://{url} returns default fedora page'
# test certificate
diff --git a/seed/nsd/applicationservice.yml b/seed/nsd/applicationservice.yml
index 8637ef57..c2b20d80 100644
--- a/seed/nsd/applicationservice.yml
+++ b/seed/nsd/applicationservice.yml
@@ -3,4 +3,3 @@ description: Configuration du serveur faisant autorité NSD
service: true
depends:
- base-fedora-36
-provider: LocalDNS
diff --git a/seed/nsd/dictionaries/20_nsd.xml b/seed/nsd/dictionaries/20_nsd.xml
index 19ee6222..eb7c213f 100644
--- a/seed/nsd/dictionaries/20_nsd.xml
+++ b/seed/nsd/dictionaries/20_nsd.xml
@@ -5,9 +5,9 @@
nsd_allowed_all_client
/etc/nsd/conf.d/risotto.conf
- nsd_zone_filenames
+ nsd_zone_filenames
nsd_zone_filenames_signed
- nsd_reverse_filenames
+ nsd_reverse_filenames
nsd_reverse_filenames_signed
/sysusers.d/0nsd.conf
/tmpfiles.d/0nsd.conf
@@ -20,78 +20,59 @@
-
-
+
+
+
-
-
-
+
+
-
- zone_name_eth0
- ExternalDNS
- nsd_resolver
-
ip_eth0
ip_dns
-
- ip_eth
+
nsd_allowed_client
- nsd_resolve_ip
- nsd_allowed_all_client
-
-
- nsd_resolver
- authorities
- domain_name_eth0
- ip
- 0
- nsd_resolve_ip
-
-
- nsd_resolver
- authorities
- domain_name_eth0
- authority_zones
- nsd_zones_all
-
-
- nsd_resolver
- authorities
- domain_name_eth0
- authority_zones
- nsd_reverse_reverse_name
-
-
- nsd_zones_auto
+ nsd_allowed_client_ip
nsd_zones
- nsd_zones_auto
+ nsd_reverse_name
nsd_zones_all
+
+ ip_eth
+ nsd_allowed_client_ip
+ nsd_resolve_ip
+ nsd_allowed_all_client
+
+
+ nsd_resolver
+ nsd_resolve_ip
+
+
+ nsd_zones
+
nsd_reverse_network
- nsd_reverse_reverse_name
+ nsd_reverse_name
/etc/nsd/
- nsd_zones_all
+ nsd_zones
.zone
True
@@ -106,7 +87,7 @@
/etc/nsd/
- nsd_reverse_reverse_name
+ nsd_reverse_name
reverse
True
diff --git a/seed/nsd/extras/nsd/00_nsd.xml b/seed/nsd/extras/nsd/00_nsd.xml
index 2db2f9d0..16228bad 100644
--- a/seed/nsd/extras/nsd/00_nsd.xml
+++ b/seed/nsd/extras/nsd/00_nsd.xml
@@ -1,8 +1,7 @@
-
-
+
@@ -16,20 +15,13 @@
-
-
- nsd_zones_auto
- nsd.nsd_zone_.is_auto_
-
- nsd.nsd_zone_.is_auto_
host
nsd.nsd_zone_.hostname_.hostname_
- nsd.nsd_zone_.is_auto_
ip
nsd.nsd_zone_.hostname_.ip_
@@ -42,9 +34,5 @@
CNAME
nsd.nsd_zone_.hostname_.ip_
-
- True
- nsd.nsd_zone_.hostname_
-
diff --git a/seed/nsd/funcs/funcs.py b/seed/nsd/funcs/funcs.py
index ca521b1e..387743a6 100644
--- a/seed/nsd/funcs/funcs.py
+++ b/seed/nsd/funcs/funcs.py
@@ -8,6 +8,8 @@ from shutil import rmtree as _rmtree, copy2 as _copy2
from glob import glob as _glob
from filecmp import cmp as _cmp
+from risotto.utils import DOMAINS as _DOMAINS
+
_PKI_DIR = _abspath('pki/dnssec')
_ALGO = 'ECDSAP256SHA256'
@@ -32,9 +34,11 @@ def nsd_concat_lists(list1: _List[str],
list2: _List[str],
str1: str=None,
) -> _List[str]:
- ret = list1 + list2
+ ret = set(list1 + list2)
if str1:
- ret.append(str1)
+ ret.add(str1)
+ ret = list(ret)
+ ret.sort()
return ret
@@ -117,3 +121,14 @@ def sign(zone_filename: str,
with open(signed_filename) as fh:
content = fh.read().strip()
return content
+
+
+def get_internal_info_in_zone(zone: str,
+ type: str,
+ index: int=None,
+ ) -> _List[str]:
+ if zone not in _DOMAINS:
+ return []
+ if type == 'host':
+ return list(_DOMAINS[zone][0])
+ return _DOMAINS[zone][1][index]
diff --git a/seed/nsd/templates/nsd.reverse b/seed/nsd/templates/nsd.reverse
index 48d50761..7ac9f94c 100644
--- a/seed/nsd/templates/nsd.reverse
+++ b/seed/nsd/templates/nsd.reverse
@@ -1,6 +1,6 @@
%set %%name = None
%set %%network = %%ip_network(%%nsd_reverse_network[%%rougail_index])
-%for %%zone in %%nsd_zones_all
+%for %%zone in %%nsd_zones
%set %%suffix = %%normalize_family(%%zone)
%set %%hostnames = %%nsd["nsd_zone_" + %%suffix]["hostname_" + %%suffix]["hostname_" + %%suffix]
%for %%hostname in %%hostnames
diff --git a/seed/nsd/templates/nsd.yml b/seed/nsd/templates/nsd.yml
index c0809d03..8da8c983 100644
--- a/seed/nsd/templates/nsd.yml
+++ b/seed/nsd/templates/nsd.yml
@@ -1,6 +1,6 @@
address: '%%ip_eth0'
records:
-%for %%domain in %%nsd_zones_all
+%for %%domain in %%nsd_zones
%set %%suffix = %%normalize_family(%%domain)
%set %%hostnames = %%nsd["nsd_zone_" + %%suffix]["hostname_" + %%suffix]["hostname_" + %%suffix]
%for %%nsd in %%hostnames
diff --git a/seed/nsd/templates/risotto.conf b/seed/nsd/templates/risotto.conf
index 9a40a9a2..05346121 100644
--- a/seed/nsd/templates/risotto.conf
+++ b/seed/nsd/templates/risotto.conf
@@ -10,7 +10,7 @@ server:
remote-control:
control-enable: no
-%for %%zone in %%nsd_zones_all
+%for %%zone in %%nsd_zones
zone:
name: "%%zone"
@@ -19,6 +19,6 @@ zone:
%for %%reverse in %%nsd_reverse_network
zone:
- name: "%%reverse.nsd_reverse_reverse_name"
- zonefile: "%%{reverse.nsd_reverse_reverse_name}reverse.signed"
+ name: "%%reverse.nsd_reverse_name"
+ zonefile: "%%{reverse.nsd_reverse_name}reverse.signed"
%end for
diff --git a/seed/oauth2-client/dictionaries/30_oauth2_client.xml b/seed/oauth2-client/dictionaries/30_oauth2_client.xml
index 9d3d0bcb..cc6e415c 100644
--- a/seed/oauth2-client/dictionaries/30_oauth2_client.xml
+++ b/seed/oauth2-client/dictionaries/30_oauth2_client.xml
@@ -2,41 +2,37 @@
-
+
False
-
-
-
+
+
+
-
-
+
+
users
-
+
Défaut
-
+
demo.png
-
-
+
+
HS512
HS512
RS256
-
+
+
-
- zone_name_eth0
- OAuth2
- oauth2_client_server_domainname
-
domain_name_eth0
oauth2_client_id
@@ -49,32 +45,6 @@
hide_secret
oauth2_client_secret
-
- oauth2_client_server_domainname
- domain_name_eth0
- oauth2
- oauth2_client_secret
- oauth2_secret
- oauth2_client_name
- oauth2_name
- oauth2_client_description
- oauth2_description
- oauth2_client_external
- oauth2_external
- oauth2_client_family
- oauth2_family
- oauth2_client_category
- oauth2_category
- oauth2_client_logo
- oauth2_logo
- oauth2_client_login
- oauth2_login
- True
- oauth2_client_token_signature_algo
- oauth2_token_signature_algo
- external_domainname
- oauth2_server_domainname
-
revprox_client_external_domainnames
revprox_client_location
diff --git a/seed/oauth2-client/funcs/oauth2_client.py b/seed/oauth2-client/funcs/oauth2_client.py
index 47647fed..7329157d 100644
--- a/seed/oauth2-client/funcs/oauth2_client.py
+++ b/seed/oauth2-client/funcs/oauth2_client.py
@@ -4,6 +4,8 @@ from risotto.utils import multi_function as _multi_function
@_multi_function
def calc_oauth2_client_external(external, location, *extras):
if not external or not location or None in extras:
+ if isinstance(external, list):
+ return []
return
if isinstance(external, list):
return [f'https://{exter}{location[0]}' + ''.join(extras) for exter in external]
diff --git a/seed/openldap/DEBUG.md b/seed/openldap/DEBUG.md
index 8b910629..a7607801 100644
--- a/seed/openldap/DEBUG.md
+++ b/seed/openldap/DEBUG.md
@@ -27,4 +27,8 @@ grep ldapAgentPassword /etc/nextcloud/nextcloud.init
Search information with standard user:
-ldapsearch -D cn=gnunux@gnunux.info,ou=users,ou=in,o=gnunux,o=info -w "1vCE09NRW2kxHIpf1PkehOS9bSLZual82saHSBj9RPM" -b cn=gnunux@gnunux.info,ou=users,ou=in,o=gnunux,o=info
+ldapsearch -D cn=admin,ou=in,o=gnunux,o=info -w "1vCE09NRW2kxHIpf1PkehOS9bSLZual82saHSBj9RPM" -b cn=gnunux@gnunux.info,ou=users,ou=in,o=gnunux,o=info
+
+# Delete User
+
+ldapdelete -D cn=gnunux@gnunux.info,ou=users,ou=in,o=gnunux,o=info -y /usr/local/lib/secrets/admin_ldap.pwd cn=rougail_test@gnunux.info,ou=in,o=gnunux,o=info
diff --git a/seed/openldap/applicationservice.yml b/seed/openldap/applicationservice.yml
index 1d656054..d6ae0dd4 100644
--- a/seed/openldap/applicationservice.yml
+++ b/seed/openldap/applicationservice.yml
@@ -2,5 +2,4 @@ format: '0.1'
description: OpenLDAP server
depends:
- ldap-client-fedora
- - base-fedora-35
-provider: LDAP
+ - base-fedora-36
diff --git a/seed/openldap/dictionaries/21_openldap-server.xml b/seed/openldap/dictionaries/21_openldap-server.xml
index c23a7629..29dfa490 100644
--- a/seed/openldap/dictionaries/21_openldap-server.xml
+++ b/seed/openldap/dictionaries/21_openldap-server.xml
@@ -14,7 +14,7 @@
/secrets/admin_ldap.pwd
/sysusers.d/risotto-openldap.conf
/tmpfiles.d/0openldap-server.conf
- /tests/openldap.yml
+ /tests/openldap.yml
@@ -76,10 +76,9 @@
-
+
-
-
+
@@ -89,29 +88,20 @@
domain_name_eth0
ldap_server_address
-
- domain_name_eth0
- ldapclient_base_dn
-
ldapclient_base_dn
True
ldap_account_dn
-
- ldapclient_base_dn
- True
- ldap_group_dn
-
-
- ldapclient_base_dn
- ldap_user_dn
-
cn=admin
ldapclient_base_dn
,
ldapclient_user
+
+ ldapclient_base_dn
+ ldapclient_search_dn
+
diff --git a/seed/openldap/extras/accounts/00_account.xml b/seed/openldap/extras/accounts/00_account.xml
index 63691336..aea85549 100644
--- a/seed/openldap/extras/accounts/00_account.xml
+++ b/seed/openldap/extras/accounts/00_account.xml
@@ -1,13 +1,12 @@
-
+
-
-
-
-
-
+
+
+
+
@@ -30,19 +29,6 @@
-
- ldapclient_base_dn
- accounts.remote_.family_
- accounts.remote_.base_dn_
-
-
- cn=
-
- ,
- ldapclient_base_dn
-
- accounts.remote_.dn_
-
domain_name_eth0
accounts.users.ldap_user_mail
diff --git a/seed/openldap/funcs/ldap.py b/seed/openldap/funcs/ldap.py
index 597d26cb..e0059c78 100644
--- a/seed/openldap/funcs/ldap.py
+++ b/seed/openldap/funcs/ldap.py
@@ -29,16 +29,3 @@ def ssha_encode(password):
with open(_SSHA_PASSWORD_DIR, 'w') as fh:
_dump(passwords, fh)
return ret
-
-
-def get_default_base_dn(server_name: str) -> str:
- if not server_name or '.' not in server_name:
- return None
- values = server_name.split('.')
- # cannot calculated base dn should be server.domain.tld
- # remove 'server' in dn
- if len(values) < 3:
- return None
- domain = ['ou=' + domain for domain in values[1:-2]]
- domain.append(f'o={values[-2]},o={values[-1]}')
- return ','.join(domain)
diff --git a/seed/openldap/templates/config_acl.ldif b/seed/openldap/templates/config_acl.ldif
index e26b4f68..5a6dcec4 100644
--- a/seed/openldap/templates/config_acl.ldif
+++ b/seed/openldap/templates/config_acl.ldif
@@ -11,11 +11,12 @@
%set %%name = %%normalize_family(%%remote)
%set %%family = %%accounts['remote_' + %%name]['family_' + %%name]
%%groups.append(%%accounts['remote_' + %%name]['dn_' + %%name])%slurp
- %if %%accounts['remote_' + %%name]['read_only_' + %%name]
- %set %%right = 'read'
- %else
- %set %%right = 'write'
- %end if
+%set %%right = 'read'
+# %if %%accounts['remote_' + %%name]['read_only_' + %%name]
+# %set %%right = 'read'
+# %else
+# %set %%right = 'write'
+# %end if
%%dns.setdefault(%%family, []).append((%%accounts['remote_' + %%name]['dn_' + %%name], %%right))%slurp
%end for
dn: olcDatabase={2}mdb,cn=config
@@ -25,7 +26,7 @@ olcAccess: {0}to attrs=userPassword
by self write
by anonymous auth
by * none
-olcAccess: {1}to dn.subtree="%%ldap_group_dn"
+olcAccess: {1}to dn.subtree="%%ldapclient_group_dn"
%for group in %%groups
by dn="%%group" read
%end for
diff --git a/seed/openldap/templates/openldap.yml b/seed/openldap/templates/openldap.yml
index 5466d094..e8c68279 100644
--- a/seed/openldap/templates/openldap.yml
+++ b/seed/openldap/templates/openldap.yml
@@ -1,25 +1,30 @@
%set %%username = "rougail_test@silique.fr"
%set %%username_family = "rougail_test@gnunux.info"
-%set %%familydn = %%calc_ldapclient_base_dn(%%ldapclient_base_dn, family_name='gnunux')
+%set %%name_family = 'gnunux'
+%set %%familydn = %%calc_ldapclient_base_dn(%%ldapclient_base_dn, family_name=%%name_family)
+%set %%userdn = 'cn=' + %%username + ',' + %%calc_ldapclient_base_dn(%%ldapclient_base_dn)
+%set %%userfamilydn = 'cn=' + %%username_family + ',' + %%familydn
address: %%ip_eth0
admin_dn: %%ldapclient_user
admin_password: %%ldapclient_user_password
-user_dn: cn=%%username,%%ldap_user_dn
-user_password: %%get_password(server_name=%%ldap_server_address, username=%%username, description="ldap user", type="cleartext", hide=%%hide_secret, temporary=True)
-user_family_dn: cn=%%username_family,%%familydn
-user_family_password: %%get_password(server_name=%%ldap_server_address, username=%%username_family, description="ldap family user", type="cleartext", hide=%%hide_secret, temporary=True)
+user_dn: %%userdn
+user_password: %%get_password(server_name='test', username=%%username, description='test', type="cleartext", hide=%%hide_secret, temporary=True)
+user_family_dn: %%userfamilydn
+user_family_password: %%get_password(server_name='test', username=%%username_family, description="test", type="cleartext", hide=%%hide_secret, temporary=True)
base_account_dn: %%ldap_account_dn
-base_user_dn: %%ldap_user_dn
+base_user_dn: %%ldapclient_user_dn
base_family_dn: %%familydn
-base_group_dn: %%ldap_group_dn
+base_group_dn: %%ldapclient_group_dn
%for %%idx in %%range(3)
%set %%name = 'remote_test' + %%str(%%idx)
remote%%idx: cn=%%name,%%ldapclient_base_dn
remote_password%%idx: %%get_password(server_name=%%domain_name_eth0, username=%%name, description="remote account", type="cleartext", hide=%%hide_secret, temporary=True)
%end for
users:
+ %%username: %%userdn
+ %%username_family: %%userfamilydn
%for %%user in %%accounts.users.ldap_user_mail
- %%user: cn=%%user,%%ldap_user_dn
+ %%user: cn=%%user,%%ldapclient_user_dn
%end for
%for %%family in %%accounts.families
%set %%families = %%calc_ldapclient_base_dn(%%ldapclient_base_dn, %%family)
@@ -29,11 +34,15 @@ users:
%end for
groups:
users:
+ - %%userdn
%for %%user in %%accounts.users.ldap_user_mail
- - cn=%%user,%%ldap_user_dn
+ - cn=%%user,%%ldapclient_user_dn
%end for
%for %%family in %%accounts.families
%%family:
+ %if %%family == %%name_family
+ - %%userfamilydn
+ %end if
%for %%user in %%accounts['family_' + %%family]['users_' + %%family]['ldap_user_mail_' + %%family]
- cn=%%user,%%families
%end for
diff --git a/seed/openldap/templates/users.ldif b/seed/openldap/templates/users.ldif
index 057c5bcc..bae187ec 100644
--- a/seed/openldap/templates/users.ldif
+++ b/seed/openldap/templates/users.ldif
@@ -1,4 +1,6 @@
-%set name_family = 'gnunux'
+%set %%username="rougail_test@silique.fr"
+%set %%username_family="rougail_test@gnunux.info"
+%set %%name_family="gnunux"
# BaseDN
%set groups = {}
dn: %%ldapclient_base_dn
@@ -38,36 +40,23 @@ objectClass: top
objectClass: organizationalUnit
## Accounts users
-%set %%users = %%ldap_user_dn
+%set %%users = %%ldapclient_user_dn
dn: %%users
ou: users
objectClass: top
objectClass: organizationalUnit
+%set %%userdn = 'cn=' + %%username + ',' + %%calc_ldapclient_base_dn(%%ldapclient_base_dn)
+%set %%userfamilydn = 'cn=' + %%username_family + ',' + %%calc_ldapclient_base_dn(%%ldapclient_base_dn, family_name=%%name_family)
+%set %%acc = [(%%userdn, %%username, %%get_password(server_name='test', username=%%username, description="test", type="cleartext", hide=%%hide_secret, temporary=True), 'Rougail', 'Test', 'rougail_test', [], 'users'),
+ (%%userfamilydn, %%username_family, %%get_password(server_name='test', username=%%username_family, description='test', type="cleartext", hide=%%hide_secret, temporary=True), 'Rougail', 'Test', 'rougail_test_gnunux', [], %%name_family),
+ ]
+%set %%groups['users'] = [%%userdn]
+%set %%groups[%%name_family] = [%%userfamilydn]
%for %%user in %%accounts.users.ldap_user_mail
%set %%userdn = "cn=" + %%user + "," + %%users
-%%groups.setdefault('users', []).append(%%userdn)
-dn: %%userdn
-cn: %%user
-mail: %%user
-sn: %%user.ldap_user_sn
-givenName: %%user.ldap_user_gn
-uid: %%user.ldap_user_uid
-userPassword:: %%ssha_encode(%%user.ldap_user_password)
-homeDirectory: /srv/home/users/%%user
-mailLocalAddress: %%user
- %if %%user.ldap_user_aliases
- %for %%alias in %%user.ldap_user_aliases
-mailLocalAddress: %%alias
- %end for
- %end if
-uidNumber: 0
-gidNumber: 0
-objectClass: top
-objectClass: inetOrgPerson
-objectClass: posixAccount
-objectClass: inetLocalMailRecipient
-
+%%acc.append((%%userdn, %%user, %%user.ldap_user_password, %%user.ldap_user_sn, %%user.ldap_user_gn, %%user.ldap_user_uid, %%user.ldap_user_aliases, 'users'))%slurp
+%%groups.setdefault('users', []).append(%%userdn)%slurp
%end for
## Families
dn: %%calc_ldapclient_base_dn(%%ldapclient_base_dn, family_name='-')
@@ -84,21 +73,53 @@ objectClass: organizationalUnit
%for %%user in %%accounts['family_' + %%family]['users_' + %%family]['ldap_user_mail_' + %%family]
%set %%userdn = "cn=" + %%user + "," + %%families
-%%groups.setdefault(%%family, []).append(%%userdn)
+%%groups.setdefault(%%family, []).append(%%userdn)%slurp
+%%acc.append((%%userdn, %%user, %%user['ldap_user_password_' + %%family], %%user['ldap_user_sn_' + %%family], %%user['ldap_user_gn_' + %%family], %%user['ldap_user_uid_' + %%family], %%user['ldap_user_aliases_' + %%family], %%family))%slurp
+#pouet
+#dn: %%userdn
+#cn: %%user
+#mail: %%user
+#sn:
+#givenName:
+#uid:
+#userPassword:: %%ssha_encode()
+#homeDirectory: /srv/home/families/%%family/%%user
+#mailLocalAddress: %%user
+# %if %%user['ldap_user_aliases_' + %%family]
+# %for %%alias in
+#mailLocalAddress: %%alias
+# %end for
+# %end if
+#uidNumber: 0
+#gidNumber: 0
+#objectClass: top
+#objectClass: inetOrgPerson
+#objectClass: posixAccount
+#objectClass: inetLocalMailRecipient
+#
+# %end for
+#%end for
+ %end for
+%end for
+%for %%userdn, %%user, %%password, %%sn, %%gn, %%uid, %%aliases, %%family in %%acc
dn: %%userdn
cn: %%user
mail: %%user
-sn: %%user['ldap_user_sn_' + %%family]
-givenName: %%user['ldap_user_gn_' + %%family]
-uid: %%user['ldap_user_uid_' + %%family]
-userPassword:: %%ssha_encode(%%user['ldap_user_password_' + %%family])
+sn: %%sn
+givenName: %%gn
+uid: %%uid
+userPassword:: %%ssha_encode(%%password)
+%if %%family == 'users'
+homeDirectory: /srv/home/users/%%user
+%else
homeDirectory: /srv/home/families/%%family/%%user
+%end if
mailLocalAddress: %%user
- %if %%user['ldap_user_aliases_' + %%family]
- %for %%alias in %%user['ldap_user_aliases_' + %%family]
+ %if %%aliases
+ %for %%alias in %%aliases
mailLocalAddress: %%alias
- %end for
- %end if
+ %end for
+ %end if
uidNumber: 0
gidNumber: 0
objectClass: top
@@ -106,10 +127,9 @@ objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: inetLocalMailRecipient
- %end for
%end for
## Groups
-%set %%groupdn = %%ldap_group_dn
+%set %%groupdn = %%ldapclient_group_dn
dn: %%groupdn
ou: groups
objectClass: top
diff --git a/seed/openldap/templates/users_mod.ldif b/seed/openldap/templates/users_mod.ldif
index f4fce200..1bdcdb45 100644
--- a/seed/openldap/templates/users_mod.ldif
+++ b/seed/openldap/templates/users_mod.ldif
@@ -1,4 +1,6 @@
-%set groups = {}
+%set %%username="rougail_test@silique.fr"
+%set %%username_family="rougail_test@gnunux.info"
+%set %%name_family="gnunux"
# Remote
%set %%acc = []
%for %%idx in %%range(3)
@@ -17,30 +19,29 @@ userPassword:: %%ssha_encode(%%password)
%end for
# Users
-%set %%users = %%ldap_user_dn
+%set %%userdn = 'cn=' + %%username + ',' + %%ldapclient_base_dn
+%set %%userfamilydn = 'cn=' + %%username_family + ',' + %%calc_ldapclient_base_dn(%%ldapclient_base_dn, family_name=%%name_family)
+%set %%acc = [(%%userdn, %%username, ['alias_' + %%username]),
+ (%%userfamilydn, %%username_family, ['alias_' + %%username_family]),
+ ]
+%set groups = {'users': [%%userdn],
+ %%name_family: [%%userfamilydn],
+ }
+%set %%users = %%ldapclient_user_dn
%for %%user in %%accounts.users.ldap_user_mail
%set %%userdn = 'cn=' + %%user + ',' + %%users
-%%groups.setdefault('users', []).append(%%userdn)%slurp
-dn: %%userdn
-changetype: modify
-#add: objectClass
-#objectClass: inetLocalMailRecipient
-#-
-replace: mailLocalAddress
-mailLocalAddress: %%user
- %if %%user.ldap_user_aliases
- %for %%alias in %%user.ldap_user_aliases
-mailLocalAddress: %%alias
- %end for
- %end if
-
+%%groups['users'].append(%%userdn)%slurp
+%%acc.append((%%userdn, %%user, %%user.ldap_user_aliases))%slurp
%end for
-# Families
%for %%family in %%accounts.families
%set %%families = %%calc_ldapclient_base_dn(%%ldapclient_base_dn, %%family)
%for %%user in %%accounts['family_' + %%family]['users_' + %%family]['ldap_user_mail_' + %%family]
%set %%userdn = 'cn=' + %%user + ',' + %%families
%%groups.setdefault(%%family, []).append(%%userdn)%slurp
+%%acc.append((%%userdn, %%user, %%user['ldap_user_aliases_' + %%family]))%slurp
+ %end for
+%end for
+%for %%userdn, %%user, %%aliases in %%acc
dn: %%userdn
changetype: modify
#add: objectClass
@@ -48,16 +49,15 @@ changetype: modify
#-
replace: mailLocalAddress
mailLocalAddress: %%user
- %if %%user['ldap_user_aliases_' + %%family]
- %for %%alias in %%user['ldap_user_aliases_' + %%family]
+ %if %%aliases
+ %for %%alias in %%aliases
mailLocalAddress: %%alias
- %end for
- %end if
+ %end for
+ %end if
- %end for
%end for
# Groups
-%set %%groupdn = %%ldap_group_dn
+%set %%groupdn = %%ldapclient_group_dn
%for %%group, %%members in %%groups.items()
dn: cn=%%group,%%groupdn
changetype: modify
diff --git a/seed/openldap/tests/test_openldap.py b/seed/openldap/tests/test_openldap.py
index 62a89770..b5284cf6 100644
--- a/seed/openldap/tests/test_openldap.py
+++ b/seed/openldap/tests/test_openldap.py
@@ -23,7 +23,7 @@ def test_ldap_admin():
l.simple_bind_s(data['admin_dn'], data['admin_password'])
assert l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
-
+
def test_ldap_accounts():
conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
@@ -70,13 +70,16 @@ def test_ldap_user():
l.simple_bind_s(data['user_dn'], data['user_password'])
-def test_ldap_user_family():
+def test_ldap_migration():
conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
with open(conf_file) as yaml:
data = load(yaml, Loader=SafeLoader)
set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
l = initialize(f'ldaps://{data["address"]}')
- l.simple_bind_s(data['user_family_dn'], data['user_family_password'])
+ if 'FIRST_RUN' in environ:
+ l.simple_bind_s(data['admin_dn'], data['admin_password'])
+ l.passwd_s(data['user_family_dn'], data['user_family_password'], data['user_family_password'] + "2")
+ l.simple_bind_s(data['user_family_dn'], data['user_family_password'] + "2")
def test_ldap_remote_auth():
diff --git a/seed/peertube/templates/nginx.peertube.conf b/seed/peertube/templates/nginx.peertube.conf
index 9ee5b7eb..145eee6f 100644
--- a/seed/peertube/templates/nginx.peertube.conf
+++ b/seed/peertube/templates/nginx.peertube.conf
@@ -68,7 +68,7 @@ server {
location @api {
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host %%revprox_client_external_domainname;
+ proxy_set_header Host %%revprox_client_external_domainnames[0];
# proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 100k; # default is 1M
@@ -119,7 +119,7 @@ server {
location @api_websocket {
proxy_http_version 1.1;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host %%revprox_client_external_domainname;
+ proxy_set_header Host %%revprox_client_external_domainnames[0];
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
diff --git a/seed/peertube/templates/production.yaml b/seed/peertube/templates/production.yaml
index d9b1e315..95ca2bc5 100644
--- a/seed/peertube/templates/production.yaml
+++ b/seed/peertube/templates/production.yaml
@@ -8,7 +8,7 @@ listen:
# Correspond to your reverse proxy server_name/listen configuration (i.e., your public PeerTube instance URL)
webserver:
https: true
- hostname: '%%revprox_client_external_domainname'
+ hostname: '%%revprox_client_external_domainnames[0]'
port: 443
rates_limit:
diff --git a/seed/piwigo/dictionaries/31_piwigo.xml b/seed/piwigo/dictionaries/31_piwigo.xml
index 9274b5ad..58659e1c 100644
--- a/seed/piwigo/dictionaries/31_piwigo.xml
+++ b/seed/piwigo/dictionaries/31_piwigo.xml
@@ -5,7 +5,7 @@
/tmpfiles.d/0piwigo.conf
/etc/piwigo/config.inc.php
/etc/piwigo/database.inc.php
- /bin/piwigo.sh
+ /sbin/piwigo.sh
/etc/php-fpm.d/piwigo.conf
diff --git a/seed/piwigo/funcs/piwigo.sh b/seed/piwigo/funcs/piwigo.py
similarity index 100%
rename from seed/piwigo/funcs/piwigo.sh
rename to seed/piwigo/funcs/piwigo.py
diff --git a/seed/piwigo/templates/piwigo.service b/seed/piwigo/templates/piwigo.service
index 3afd3384..e75c1c11 100644
--- a/seed/piwigo/templates/piwigo.service
+++ b/seed/piwigo/templates/piwigo.service
@@ -5,7 +5,7 @@ Before=nginx.service php-fpm.service
[Service]
Type=oneshot
-ExecStart=/usr/local/lib/bin/piwigo.sh
+ExecStart=/usr/local/lib/sbin/piwigo.sh
User=nginx
Group=nginx
diff --git a/seed/postfix-lmtp-relay/applicationservice.yml b/seed/postfix-lmtp-relay/applicationservice.yml
new file mode 100644
index 00000000..0a719101
--- /dev/null
+++ b/seed/postfix-lmtp-relay/applicationservice.yml
@@ -0,0 +1,2 @@
+format: '0.1'
+description: Postfix as LMTP relay
diff --git a/seed/postfix-relay/extras/lmtp/00-lmtp.xml b/seed/postfix-lmtp-relay/extras/lmtp/00-lmtp.xml
similarity index 87%
rename from seed/postfix-relay/extras/lmtp/00-lmtp.xml
rename to seed/postfix-lmtp-relay/extras/lmtp/00-lmtp.xml
index 93479ab2..ca50b309 100644
--- a/seed/postfix-relay/extras/lmtp/00-lmtp.xml
+++ b/seed/postfix-lmtp-relay/extras/lmtp/00-lmtp.xml
@@ -1,9 +1,9 @@
-
+
-
+
diff --git a/seed/postfix-relay/DEBUG.md b/seed/postfix-relay/DEBUG.md
index 324683b4..4e7d1b65 100644
--- a/seed/postfix-relay/DEBUG.md
+++ b/seed/postfix-relay/DEBUG.md
@@ -49,3 +49,9 @@ postconf maillog_file=/dev/stdout
https://www.mail-tester.com/
https://dkimvalidator.com/
+
+# debug mail :
+
+journalctl -m -u postfix -g address mail
+# get date
+journalctl -m -u postfix --since "2022-07-31 23:14:04"
diff --git a/seed/postfix-relay/applicationservice.yml b/seed/postfix-relay/applicationservice.yml
index 88aa4724..c004d5ba 100644
--- a/seed/postfix-relay/applicationservice.yml
+++ b/seed/postfix-relay/applicationservice.yml
@@ -1,6 +1,6 @@
format: '0.1'
-description: Postfix has relay
+description: Postfix as relay
depends:
- base-fedora-35
- dns-external
-provider: SMTP
+ - postfix-lmtp-relay
diff --git a/seed/postfix-relay/dictionaries/30_postfix.xml b/seed/postfix-relay/dictionaries/30_postfix.xml
index 57230e0b..eb8258b1 100644
--- a/seed/postfix-relay/dictionaries/30_postfix.xml
+++ b/seed/postfix-relay/dictionaries/30_postfix.xml
@@ -43,10 +43,9 @@
-
+
-
-
+
@@ -63,14 +62,6 @@
True
opendkim_keys
-
- domain_name_eth0
-
- local authentification
- cleartext
- hide_secret
- local_authentification_password_
-
/etc/postfix/certs/
domain_name_eth
diff --git a/seed/postfix-relay/templates/main.cf b/seed/postfix-relay/templates/main.cf
index b7ec3e8a..8810003c 100644
--- a/seed/postfix-relay/templates/main.cf
+++ b/seed/postfix-relay/templates/main.cf
@@ -318,7 +318,7 @@ smtpd_recipient_restrictions =
#mynetworks = 168.100.3.0/28, 127.0.0.0/8
#mynetworks = $config_directory/mynetworks
#mynetworks = hash:/etc/postfix/network_table
-mynetworks = 172.0.0.0/8
+mynetworks = 127.0.0.0/8
# The relay_domains parameter restricts what destinations this system will
# relay mail to. See the smtpd_recipient_restrictions description in
diff --git a/seed/postfix-relay/templates/postfix.service b/seed/postfix-relay/templates/postfix.service
index 7e33496e..bf6a4ede 100644
--- a/seed/postfix-relay/templates/postfix.service
+++ b/seed/postfix-relay/templates/postfix.service
@@ -4,7 +4,7 @@ ExecStartPre=/usr/sbin/postmap -F /etc/postfix/sni
%for %%local in %%postfix_relay_authentifications
%set %%user = %%normalize_family(%%local)
%set %%password = %%getVar('local_authentification_password_' + %%user)
- %set %%ip = %%getVar('local_authentification_ip_' + %%user)
+ %set %%ip = %%get_ip(%%local)
ExecStartPre=-/usr/bin/bash -c "echo %%password | /usr/sbin/saslpasswd2 -u %%ip %%user -p"
%end for
ExecStartPre=/usr/bin/chown postfix: /etc/sasl2/sasldb2
diff --git a/seed/postgresql-client/dictionaries/23_postgresql.xml b/seed/postgresql-client/dictionaries/23_postgresql.xml
index 9a64f739..138f7d3f 100644
--- a/seed/postgresql-client/dictionaries/23_postgresql.xml
+++ b/seed/postgresql-client/dictionaries/23_postgresql.xml
@@ -10,9 +10,9 @@
-
+
-
+
apache
@@ -24,18 +24,17 @@
domain_name_eth0
pg_client_username
-
+
+
+ pg_client_server_domainname
+ domain_name_eth0
+ remote
+ cleartext
+ hide_secret
pg_client_password
diff --git a/seed/postgresql-client/templates/postgresqlclient.service b/seed/postgresql-client/templates/postgresqlclient.service
index 0dc6a152..7addde19 100644
--- a/seed/postgresql-client/templates/postgresqlclient.service
+++ b/seed/postgresql-client/templates/postgresqlclient.service
@@ -5,5 +5,5 @@ Before=network.target
[Service]
Type=oneshot
Environment=PGPASSFILE=/usr/local/lib/secrets/postgresql.pass
-ExecStart=/usr/bin/timeout 90 bash -c 'while ! 3<> /dev/tcp/%%pg_client_server_domainname/5432; do sleep 1; done; echo "POSTGRESQL STARTED"'
+ExecStart=/usr/bin/timeout 300 bash -c 'while ! 3<> /dev/tcp/%%pg_client_server_domainname/5432; do sleep 1; done; echo "POSTGRESQL STARTED"'
ExecStart=/usr/bin/timeout 90 bash -c 'while ! /usr/bin/psql --set=sslmode=verify-full -h %%pg_client_server_domainname -U %%pg_client_username %%pg_client_database -c "\l"; do sleep 1; done; echo "POSTGRESQL READY"'
diff --git a/seed/postgresql/applicationservice.yml b/seed/postgresql/applicationservice.yml
index d2057227..e1055d6f 100644
--- a/seed/postgresql/applicationservice.yml
+++ b/seed/postgresql/applicationservice.yml
@@ -1,6 +1,4 @@
format: '0.1'
description: Postgresql
depends:
- - server
- - base-fedora-35
-provider: Postgresql
+ - base-fedora-36
diff --git a/seed/postgresql/dictionaries/22_postgresql.xml b/seed/postgresql/dictionaries/22_postgresql.xml
index 4cb925ff..b9a67c8f 100644
--- a/seed/postgresql/dictionaries/22_postgresql.xml
+++ b/seed/postgresql/dictionaries/22_postgresql.xml
@@ -2,17 +2,19 @@
-
+
accounts.remote_.remote_ip_
/etc/postgresql/postgresql.conf
/etc/postgresql/pg_hba.conf
/etc/postgresql/postgresql.sql
/etc/postgresql/pg_ident.conf
- /bin/postgresql_init
+ /sbin/postgresql_init
/sysusers.d/0postgresql.conf
+ /tmpfiles.d/0postgresql.conf
/etc/pki/ca-trust/source/anchors/ca_PostgreSQL.crt
/etc/pki/tls/certs/postgresql.crt
/etc/pki/tls/private/postgresql.key
+ /tests/postgresql.yml
diff --git a/seed/server/extras/accounts/00_accounts.xml b/seed/postgresql/extras/accounts/00_accounts.xml
similarity index 50%
rename from seed/server/extras/accounts/00_accounts.xml
rename to seed/postgresql/extras/accounts/00_accounts.xml
index 7f8a1f01..66111a88 100644
--- a/seed/server/extras/accounts/00_accounts.xml
+++ b/seed/postgresql/extras/accounts/00_accounts.xml
@@ -1,20 +1,16 @@
-
+
-
-
+
+
-
- domain_name_eth0
-
- remote
- cleartext
- hide_secret
- accounts.remote_.password_
+
+
+ accounts.remote_.remote_ip_
diff --git a/seed/postgresql/manual/image/preinstall/postgresql_server.sh b/seed/postgresql/manual/image/preinstall/postgresql_server.sh
index 4bdeb8e7..3f653485 100644
--- a/seed/postgresql/manual/image/preinstall/postgresql_server.sh
+++ b/seed/postgresql/manual/image/preinstall/postgresql_server.sh
@@ -1 +1,3 @@
PKG="$PKG postgresql-server postgresql-contrib"
+# for postgresql-setup
+PKG="$PKG util-linux postgresql-upgrade"
diff --git a/seed/postgresql/templates/pg_hba.conf b/seed/postgresql/templates/pg_hba.conf
index 7a48fb3d..8987c7cc 100644
--- a/seed/postgresql/templates/pg_hba.conf
+++ b/seed/postgresql/templates/pg_hba.conf
@@ -88,6 +88,7 @@ local all postgres ident map=pg_map
# IPv4 local connections:
#>GNUNUX
# host all all 127.0.0.1/32 ident
+hostssl rougail_test rougail_test %%gateway_eth0/32 md5
%for %%server in %%accounts.remotes
hostssl %%normalize_family(%%server) %%normalize_family(%%server) %%server md5
%end for
diff --git a/seed/postgresql/templates/postgresql.conf b/seed/postgresql/templates/postgresql.conf
index 8849422a..44df34cd 100644
--- a/seed/postgresql/templates/postgresql.conf
+++ b/seed/postgresql/templates/postgresql.conf
@@ -47,9 +47,6 @@ directiveStartToken = §
#data_directory = 'ConfigDir' # use data in another directory
# (change requires restart)
-#>GNUNUX
-data_directory = '/srv/postgresql'
-#GNUNUX
@@ -116,7 +113,7 @@ unix_socket_directories = '/var/run/postgresql'
#>GNUNUX
authentication_timeout = §§{pg_authentication_timeout}s
#GNUNUX
wal_buffers = §§pg_wal_buffers
+#GNUNUX
#max_wal_size = 1GB
max_wal_size = §§{pg_max_wal_size}§§pg_max_wal_size_unit
#GNUNUX
+effective_cache_size = §§{pg_effective_cache_size}§§pg_effective_cache_size_unit
+#GNUNUX
#log_destination = 'stderr' # Valid values are combinations of
# stderr, csvlog, syslog, and eventlog,
# depending on platform. csvlog
# requires logging_collector to be on.
-log_destination = 'syslog'
-#GNUNUX
+log_destination = 'syslog'
+# 0 logs only
+ # actions running at least this number
+ # of milliseconds.
#log_checkpoints = off
#log_connections = off
#log_disconnections = off
@@ -579,9 +589,11 @@ log_destination = 'syslog'
# %h = remote host
# %b = backend type
# %p = process ID
+ # %P = process ID of parallel group leader
# %t = timestamp without milliseconds
# %m = timestamp with milliseconds
# %n = timestamp with milliseconds (as a Unix epoch)
+ # %Q = query ID (0 if none or not computed)
# %i = command tag
# %e = SQL state
# %c = session ID
@@ -594,6 +606,8 @@ log_destination = 'syslog'
# %% = '%'
# e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout
+#log_recovery_conflict_waits = off # log standby recovery conflict waits
+ # >= deadlock_timeout
#log_parameter_max_length = -1 # when logging statements, limit logged
# bind-parameter values to N bytes;
# -1 means print in full, 0 disables
@@ -608,6 +622,7 @@ log_destination = 'syslog'
#FIXME en dure ?
log_timezone = 'Europe/Paris'
+
#------------------------------------------------------------------------------
# PROCESS TITLE
#------------------------------------------------------------------------------
@@ -624,19 +639,21 @@ log_timezone = 'Europe/Paris'
# - Query and Index Statistics Collector -
#track_activities = on
+#track_activity_query_size = 1024 # (change requires restart)
#track_counts = on
#track_io_timing = off
+#track_wal_io_timing = off
#track_functions = none # none, pl, all
-#track_activity_query_size = 1024 # (change requires restart)
#stats_temp_directory = 'pg_stat_tmp'
# - Monitoring -
+#compute_query_id = auto
+#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
#log_executor_stats = off
-#log_statement_stats = off
#------------------------------------------------------------------------------
@@ -652,10 +669,6 @@ autovacuum = on
autovacuum = off
§end if
# 0 logs only
- # actions running at least this number
- # of milliseconds.
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
# (change requires restart)
#autovacuum_naptime = 1min # time between autovacuum runs
@@ -701,10 +714,11 @@ autovacuum = off
# error
#search_path = '"$user", public' # schema names
#row_security = on
+#default_table_access_method = 'heap'
#default_tablespace = '' # a tablespace name, '' uses the default
+#default_toast_compression = 'pglz' # 'pglz' or 'lz4'
#temp_tablespaces = '' # a list of tablespace names, '' uses
# only default tablespace
-#default_table_access_method = 'heap'
#check_function_bodies = on
#default_transaction_isolation = 'read committed'
#default_transaction_read_only = off
@@ -713,17 +727,16 @@ autovacuum = off
#statement_timeout = 0 # in milliseconds, 0 is disabled
#lock_timeout = 0 # in milliseconds, 0 is disabled
#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
-#vacuum_freeze_min_age = 50000000
+#idle_session_timeout = 0 # in milliseconds, 0 is disabled
#vacuum_freeze_table_age = 150000000
-#vacuum_multixact_freeze_min_age = 5000000
+#vacuum_freeze_min_age = 50000000
+#vacuum_failsafe_age = 1600000000
#vacuum_multixact_freeze_table_age = 150000000
-#vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples
- # before index cleanup, 0 always performs
- # index cleanup
+#vacuum_multixact_freeze_min_age = 5000000
+#vacuum_multixact_failsafe_age = 1600000000
#bytea_output = 'hex' # hex, escape
#xmlbinary = 'base64'
#xmloption = 'content'
-#gin_fuzzy_search_limit = 0
#gin_pending_list_limit = 4MB
# - Locale and Formatting -
@@ -757,14 +770,15 @@ default_text_search_config = 'pg_catalog.french'
# - Shared Library Preloading -
-#shared_preload_libraries = '' # (change requires restart)
#local_preload_libraries = ''
#session_preload_libraries = ''
+#shared_preload_libraries = '' # (change requires restart)
#jit_provider = 'llvmjit' # JIT library to use
# - Other Defaults -
#dynamic_library_path = '$libdir'
+#gin_fuzzy_search_limit = 0
#------------------------------------------------------------------------------
@@ -792,7 +806,6 @@ default_text_search_config = 'pg_catalog.french'
#backslash_quote = safe_encoding # on, off, or safe_encoding
#escape_string_warning = on
#lo_compat_privileges = off
-#operator_precedence_warning = off
#quote_all_identifiers = off
#standard_conforming_strings = on
#synchronize_seqscans = on
@@ -811,6 +824,7 @@ default_text_search_config = 'pg_catalog.french'
#data_sync_retry = off # retry or panic on failure to fsync
# data?
# (change requires restart)
+#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+)
#------------------------------------------------------------------------------
diff --git a/seed/postgresql/templates/postgresql.service b/seed/postgresql/templates/postgresql.service
index 2e4e7458..4bae4c9b 100644
--- a/seed/postgresql/templates/postgresql.service
+++ b/seed/postgresql/templates/postgresql.service
@@ -1,11 +1,38 @@
[Service]
-ExecStartPre=
-ExecStartPre=+/usr/local/lib/bin/postgresql_init
-ExecStartPre=/usr/libexec/postgresql-check-db-dir %N
-Environment=PGDATA=/srv/postgresql
+Environment=PGDATA=/srv/postgresql/postgresql
Environment=PG_CONF=/etc/postgresql/postgresql.conf
Environment=PG_HBA=/etc/postgresql/pg_hba.conf
Environment=PG_IDENT=/etc/postgresql/pg_ident.conf
+Environment=LC_ALL=fr_FR.UTF-8
+ExecStartPre=
+ExecStartPre=+/usr/local/lib/sbin/postgresql_init
+# if upgrade needed, do it
+ExecStartPre=/bin/bash -c '%slurp
+/usr/libexec/postgresql-check-db-dir %N || (%slurp
+ echo "UPGRADE" &&%slurp
+# directory creation must have 700 rights
+ umask 0077 &&%slurp
+# pg_upgrade do not like ssl activation
+ /bin/grep -v "ssl " ${PG_CONF} > /tmp/postgresql.conf &&%slurp
+ mv -f /tmp/postgresql.conf ${PGDATA}/postgresql.conf &&%slurp
+# pg_upgrade modify pg_hba.conf so copy it
+ /bin/rm ${PGDATA}/pg_hba.conf &&%slurp
+ /bin/cp -af ${PG_HBA} ${PGDATA} &&%slurp
+# do upgrade
+ /usr/bin/postgresql-setup --upgrade &&%slurp
+# re do link
+ ln -sf ${PG_HBA} ${PGDATA}/ &&%slurp
+ ln -sf ${PG_CONF} ${PGDATA}/ &&%slurp
+# remove old cluster
+ /srv/postgresql/postgresql/delete_old_cluster.sh &&%slurp
+ rm -f /srv/postgresql/postgresql/delete_old_cluster.sh &&%slurp
+# force index (see later)
+ touch /srv/postgresql/risotto_upgrade.lock%slurp
+)'
+# recheck db
+ExecStartPre=/usr/libexec/postgresql-check-db-dir %N
ExecStart=
ExecStart=/usr/bin/postmaster -D ${PGDATA} -c config_file=${PG_CONF} -c hba_file=${PG_HBA} -c ident_file=${PG_IDENT}
ExecStartPost=-/usr/bin/psql -f /etc/postgresql/postgresql.sql
+# if lock do reindex
+ExecStartPost=/bin/bash -c 'if [ -f /srv/postgresql/risotto_upgrade.lock ];then echo REINDEX; /usr/bin/reindexdb && rm -f /srv/postgresql/risotto_upgrade.lock; fi'
diff --git a/seed/postgresql/templates/postgresql.sql b/seed/postgresql/templates/postgresql.sql
index 7f3892ac..bb53c957 100644
--- a/seed/postgresql/templates/postgresql.sql
+++ b/seed/postgresql/templates/postgresql.sql
@@ -1,7 +1,12 @@
+%set %%new_accounts = [('rougail_test', %%get_password(server_name=%%domain_name_eth0, username='rougail_test', description="remote", type="cleartext", hide=%%hide_secret, temporary=True))]
%for %%server in %%accounts.remotes
%set %%name = %%normalize_family(%%server)
+ %set %%password = %%accounts["remote_" + %%name]["password_" + %%name]
+%%new_accounts.append((%%name, %%password))%slurp
+%end for
+%for %%name, %%password in %%new_accounts
CREATE DATABASE "%%name";
-CREATE ROLE "%%name" WITH LOGIN ENCRYPTED PASSWORD '%%accounts["remote_" + %%name]["password_" + %%name]';
-ALTER USER "%%name" PASSWORD '%%accounts["remote_" + %%name]["password_" + %%name]';
+CREATE ROLE "%%name" WITH LOGIN ENCRYPTED PASSWORD '%%password';
+ALTER USER "%%name" PASSWORD '%%password';
GRANT ALL PRIVILEGES ON DATABASE "%%name" TO "%%name";
%end for
diff --git a/seed/postgresql/templates/postgresql.yml b/seed/postgresql/templates/postgresql.yml
new file mode 100644
index 00000000..471b4cd3
--- /dev/null
+++ b/seed/postgresql/templates/postgresql.yml
@@ -0,0 +1,4 @@
+address: %%ip_eth0
+user: rougail_test
+password: %%get_password(server_name=%%domain_name_eth0, username='rougail_test', description="remote", type="cleartext", hide=%%hide_secret, temporary=True)
+dbname: rougail_test
diff --git a/seed/postgresql/templates/postgresql_init b/seed/postgresql/templates/postgresql_init
index fc84c6c9..fda9a325 100644
--- a/seed/postgresql/templates/postgresql_init
+++ b/seed/postgresql/templates/postgresql_init
@@ -1,14 +1,22 @@
#!/bin/bash -e
-[ -d "/srv/postgresql" ] && exit 0 || true
-
-/bin/mkdir /srv/postgresql
-/bin/chown postgres: /srv/postgresql
-mkdir /var/lib/pgsql
-/bin/chown postgres: /var/lib/pgsql
-/usr/bin/postgresql-setup --initdb
-/bin/rm /srv/postgresql/postgresql.conf
-/bin/rm /srv/postgresql/pg_hba.conf
-/bin/rm /srv/postgresql/pg_ident.conf
-
+if [ ! -d "/srv/postgresql" ]; then
+ /bin/mkdir -p /srv/postgresql/postgresql
+ /bin/chown -R postgres: /srv/postgresql
+ /usr/bin/postgresql-setup --initdb
+ #/bin/rm /srv/postgresql/postgresql.conf
+ #/bin/rm /srv/postgresql/pg_hba.conf
+ #/bin/rm /srv/postgresql/pg_ident.conf
+elif [ ! -d "/srv/postgresql/postgresql" ]; then
+ # migrate /srv/postgresql to /srv/postgresql/postgresql
+ # needed for upgrade...
+ mkdir /srv/postgresql/postgresql
+ mv /srv/postgresql/* /srv/postgresql/postgresql || true
+ chown postgres: /srv/postgresql/postgresql
+ chmod 700 /srv/postgresql/postgresql
+fi
+# for postgresql-setup...
+/bin/ln -sf /etc/postgresql/postgresql.conf /srv/postgresql/postgresql/postgresql.conf
+/bin/ln -sf /etc/postgresql/pg_hba.conf /srv/postgresql/postgresql/pg_hba.conf
+/bin/ln -sf /etc/postgresql/pg_ident.conf /srv/postgresql/postgresql/pg_ident.conf
exit 0
diff --git a/seed/postgresql/templates/sysuser-postgresql.conf b/seed/postgresql/templates/sysuser-postgresql.conf
index d07f84f2..44588765 100644
--- a/seed/postgresql/templates/sysuser-postgresql.conf
+++ b/seed/postgresql/templates/sysuser-postgresql.conf
@@ -1,3 +1,3 @@
g postgres 26 -
-u postgres 26:26 "PostgreSQL Server" /srv/postgresql /bin/bash
+u postgres 26:26 "PostgreSQL Server" /srv/postgresql/postgresql /bin/bash
diff --git a/seed/postgresql/templates/tmpfiles.postgresql.conf b/seed/postgresql/templates/tmpfiles.postgresql.conf
new file mode 100644
index 00000000..29b3c420
--- /dev/null
+++ b/seed/postgresql/templates/tmpfiles.postgresql.conf
@@ -0,0 +1,2 @@
+# for postgresql-setup only...
+d /var/lib/pgsql/ 0750 postgres postgres -
diff --git a/seed/postgresql/tests/test_postgresql.py b/seed/postgresql/tests/test_postgresql.py
new file mode 100644
index 00000000..c89c2fc3
--- /dev/null
+++ b/seed/postgresql/tests/test_postgresql.py
@@ -0,0 +1,79 @@
+from yaml import load, SafeLoader
+from os import environ
+from pytest import raises
+
+from psycopg2 import connect, OperationalError
+
+
+def test_postgresql_wrong_password():
+ conf_file = f'{environ["MACHINE_TEST_DIR"]}/postgresql.yml'
+ with open(conf_file) as yaml:
+ data = load(yaml, Loader=SafeLoader)
+ with raises(OperationalError):
+ connect(host=data['address'], user=data['user'], password='a', database=data['dbname'])
+
+
+def test_postgresql_connection():
+ conf_file = f'{environ["MACHINE_TEST_DIR"]}/postgresql.yml'
+ with open(conf_file) as yaml:
+ data = load(yaml, Loader=SafeLoader)
+ db = connect(host=data['address'], user=data['user'], password=data['password'], database=data['dbname'])
+ db.close()
+
+
+def test_postgresql_migration():
+ conf_file = f'{environ["MACHINE_TEST_DIR"]}/postgresql.yml'
+ with open(conf_file) as yaml:
+ data = load(yaml, Loader=SafeLoader)
+ db = connect(host=data['address'], user=data['user'], password=data['password'], database=data['dbname'])
+ cursor = db.cursor()
+ if 'FIRST_RUN' in environ:
+ sql = """CREATE TABLE test (col CHAR(20) NOT NULL)"""
+ cursor.execute(sql)
+ sql = """INSERT INTO test (col) VALUES ('test')"""
+ cursor.execute(sql)
+ db.commit()
+ sql = """SELECT * FROM test"""
+ cursor.execute(sql)
+ results = cursor.fetchall()
+ assert len(results) == 1
+ results[0] == ('test',)
+ cursor.close()
+ db.close()
+
+
+def test_postgresql_insert():
+ conf_file = f'{environ["MACHINE_TEST_DIR"]}/postgresql.yml'
+ with open(conf_file) as yaml:
+ data = load(yaml, Loader=SafeLoader)
+ db = connect(host=data['address'], user=data['user'], password=data['password'], database=data['dbname'])
+ cursor = db.cursor()
+ sql = """INSERT INTO test (col) VALUES ('test2')"""
+ cursor.execute(sql)
+ db.commit()
+ #
+ sql = """SELECT * FROM test WHERE col = 'test2'"""
+ cursor.execute(sql)
+ results = cursor.fetchall()
+ assert len(results) == 1
+ results[0] == ('test2',)
+ cursor.close()
+ db.close()
+
+
+def test_postgresql_delete():
+ conf_file = f'{environ["MACHINE_TEST_DIR"]}/postgresql.yml'
+ with open(conf_file) as yaml:
+ data = load(yaml, Loader=SafeLoader)
+ db = connect(host=data['address'], user=data['user'], password=data['password'], database=data['dbname'])
+ cursor = db.cursor()
+ sql = """DELETE FROM test WHERE col = 'test2'"""
+ cursor.execute(sql)
+ db.commit()
+ #
+ sql = """SELECT * FROM test WHERE col = 'test2'"""
+ cursor.execute(sql)
+ results = cursor.fetchall()
+ assert len(results) == 0
+ cursor.close()
+ db.close()
diff --git a/seed/provider-systemd-machined/dictionaries/10-machined.xml b/seed/provider-systemd-machined/dictionaries/10-machined.xml
index 16d589e1..0519f9be 100644
--- a/seed/provider-systemd-machined/dictionaries/10-machined.xml
+++ b/seed/provider-systemd-machined/dictionaries/10-machined.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/seed/provider-systemd-machined/dictionaries/16-machined.xml b/seed/provider-systemd-machined/dictionaries/16-machined.xml
index 0e08e934..877b4343 100644
--- a/seed/provider-systemd-machined/dictionaries/16-machined.xml
+++ b/seed/provider-systemd-machined/dictionaries/16-machined.xml
@@ -13,24 +13,25 @@
/var/lib/risotto/srv
-
+
/var/lib/risotto/configurations
-
+
/var/lib/risotto/journals
-
+
False
-
-
+
+
host
+
@@ -41,65 +42,24 @@
container_srv_path
/
- domain_name_eth0
+ server_name
srv_dir
container_journal_path
/
- domain_name_eth0
+ server_name
journal_dir
container_config_path
/
- domain_name_eth0
+ server_name
config_dir
-
- machines
- domain_name_eth0
- host
-
-
- host
- incoming_ports
- domain_name_eth0
- incoming_ports
-
-
- host
- outgoing_ports
- domain_name_eth0
- outgoing_ports
-
-
- host
- machine_srv
- domain_name_eth0
- srv_dir
-
-
- host
- machine_journal
- domain_name_eth0
- journal_dir
-
-
- host
- machine_config
- domain_name_eth0
- config_dir
-
-
- host
- machine_zones
- domain_name_eth0
- zones_list
-
diff --git a/seed/redis-client/dictionaries/23_redis.xml b/seed/redis-client/dictionaries/23_redis.xml
index 0a1d32ca..04b405c2 100644
--- a/seed/redis-client/dictionaries/23_redis.xml
+++ b/seed/redis-client/dictionaries/23_redis.xml
@@ -10,38 +10,26 @@
-
-
-
+
+
+
apache
-
- zone_name_eth0
- Redis
- redis_client_server_domainname
-
-
- redis_client_server_domainname
- redis_client
- domain_name_eth0
+
+ domain_name_eth0
redis_client_username
-
- redis_client_server_domainname
- redis_client_password
- redis_client_username
+
+ redis_client_server_domainname
+ domain_name_eth0
+ redis
+ cleartext
+ hide_secret
redis_client_password
-
- redis_client_server_domainname
- redis_client_ip
- ip_eth0
- redis_client_username
- redis_client_password
-
diff --git a/seed/redis/applicationservice.yml b/seed/redis/applicationservice.yml
index d173f89b..ad2c0918 100644
--- a/seed/redis/applicationservice.yml
+++ b/seed/redis/applicationservice.yml
@@ -1,5 +1,4 @@
format: '0.1'
description: Redis
depends:
- - base-fedora-35
-provider: Redis
+ - base-fedora-36
diff --git a/seed/redis/dictionaries/90_redis.xml b/seed/redis/dictionaries/90_redis.xml
index 43e9f074..dfd0b166 100644
--- a/seed/redis/dictionaries/90_redis.xml
+++ b/seed/redis/dictionaries/90_redis.xml
@@ -9,6 +9,7 @@
/etc/pki/ca-trust/source/anchors/ca_Redis.crt
/etc/pki/tls/certs/redis.crt
/etc/pki/tls/private/redis.key
+ /tests/redis.yml
diff --git a/seed/redis/extras/account/00_account.xml b/seed/redis/extras/account/00_account.xml
index 3202ce19..5de6542c 100644
--- a/seed/redis/extras/account/00_account.xml
+++ b/seed/redis/extras/account/00_account.xml
@@ -1,18 +1,14 @@
-
-
-
+
+
+
-
- domain_name_eth0
- account.remote
- redis
- cleartext
- hide_secret
- account.password
+
+ account.remote
+ account.remote_ip
diff --git a/seed/redis/templates/redis.conf b/seed/redis/templates/redis.conf
index 8448e98d..ece2dbdc 100644
--- a/seed/redis/templates/redis.conf
+++ b/seed/redis/templates/redis.conf
@@ -180,7 +180,9 @@ tcp-keepalive %%redis_tcp_keepalive
#
# tls-client-key-file-pass secret
-# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
+# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange,
+# required by older versions of OpenSSL (<3.0). Newer versions do not require
+# this configuration and recommend against it.
#
# tls-dh-params-file redis.dh
@@ -485,7 +487,10 @@ rdb-del-sync-files no
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
+#>GNUNUX
+#dir /var/lib/redis
dir /srv/redis
+#
+
+
+
+
+
+
+ smtp_relay_address
+ lmtp_relay_address
+
+
+
diff --git a/seed/relay-mail-client/dictionaries/20_smtp_client.xml b/seed/relay-mail-client/dictionaries/20_smtp_client.xml
index 75d6cd12..f04bfc3f 100644
--- a/seed/relay-mail-client/dictionaries/20_smtp_client.xml
+++ b/seed/relay-mail-client/dictionaries/20_smtp_client.xml
@@ -7,35 +7,23 @@
-
+
-
+
-
- zone_name_eth0
- SMTP
- smtp_relay_address
-
-
- smtp_relay_address
- mail
- domain_name_eth0
+
+ domain_name_eth0
smtp_relay_user
-
- smtp_relay_address
- mail_password
- smtp_relay_user
+
+ smtp_relay_address
+ domain_name_eth0
+ local authentification
+ cleartext
+ hide_secret
smtp_relay_password
-
- smtp_relay_address
- mail_ip
- ip_eth0
- smtp_relay_user
- smtp_relay_password
-
diff --git a/seed/reverse-proxy-client/dictionaries/21_nginx_client.xml b/seed/reverse-proxy-client/dictionaries/21_nginx_client.xml
index f937e143..a8e419e0 100644
--- a/seed/reverse-proxy-client/dictionaries/21_nginx_client.xml
+++ b/seed/reverse-proxy-client/dictionaries/21_nginx_client.xml
@@ -8,21 +8,20 @@
-
+
-
-
-
+
+
/
-
+
False
-
+
+
+
-
-
443
@@ -37,14 +36,9 @@
-
- zone_name_eth0
- ReverseProxy
- revprox_client_server_domainname
-
-
- revprox_client_external_domainnames
- revprox_client_external_domainname
+
+ revprox_client_server_domainname
+ revprox_client_server_ip
domain_name_eth0
@@ -64,23 +58,5 @@
/
revprox_key_file
-
- revprox_client_server_domainname
- revprox_clients
- revprox_client_external_domainnames
- revprox_location
- revprox_client_location
- revprox_is_websocket
- revprox_client_is_websocket
- revprox_max_body_size
- revprox_client_max_body_size
- revprox_url
- revprox_client_web_address
- revprox_client_server_ip
-
-
diff --git a/seed/reverse-proxy-client/tests/revprox.py b/seed/reverse-proxy-client/tests/revprox.py
new file mode 100644
index 00000000..bb9ab177
--- /dev/null
+++ b/seed/reverse-proxy-client/tests/revprox.py
@@ -0,0 +1,84 @@
+from requests import get, post, session
+from mookdns import MookDns
+
+
+class Authentication:
+ def __init__(self,
+ auth_url,
+ portal_server,
+ ip,
+ username,
+ password,
+ title,
+ ):
+ self.ip = ip
+ with session() as req:
+ with MookDns(self.ip):
+ self.is_lemonldap(req,
+ auth_url,
+ )
+ self.auth_lemonldap(req,
+ portal_server,
+ username,
+ password,
+ title,
+ )
+ self.cookies = dict(req.cookies)
+
+# @staticmethod
+ def is_lemonldap(self,
+ req,
+ url,
+ ):
+ ret = req.get(url)
+ code = ret.status_code
+ content = ret.content
+ assert code == 200
+ assert b'Authentication portal' in content, f'cannot find LemonLdap title: {content}'
+
+ def auth_lemonldap(self,
+ req,
+ portal_server,
+ username,
+ password,
+ title,
+ ):
+ # authentification
+ json = {'user': username,
+ 'password': password,
+ }
+ headers = {"Content-Type": "application/x-www-form-urlencoded",
+ "Accept": "application/json",
+ }
+ portal_url = f'https://{portal_server}/oauth2/'
+ ret = req.post(portal_url, data=json, headers=headers)
+ json = ret.json()
+ assert json['error']
+ assert json['result'] == 1
+ assert json['id'] == ret.cookies.get('lemonldap')
+ # authorization code
+ # curl -X POST -d user=dwho -d password=dwho -H 'Accept: application/json' 'https://oidctest.wsweet.org/oauth2/'
+ # curl -s -D - -o /dev/null -b lemonldap=0640f95827111f00ba7ad5863ba819fe46cfbcecdb18ce525836369fb4c8350b 'https://oidctest.wsweet.org/oauth2/authorize?response_type=code&client_id=private&scope=openid+profile+email&redirect_uri=http://localhost' | grep '^location'
+ authorize_url = f'{portal_url}authorize'
+ ret = req.get(authorize_url)
+ assert ret.status_code == 200
+ assert title in ret.content.decode()
+
+ def get(self,
+ url,
+ json=False,
+ ):
+ with MookDns(self.ip):
+ ret = get(url, cookies=self.cookies)
+ assert ret.status_code == 200, f'return code is {ret.status_code}'
+ if json:
+ return ret.json()
+ return ret.content.decode()
+
+ def post(self,
+ url,
+ data,
+ ):
+ with MookDns(self.ip):
+ ret = post(url, cookies=self.cookies, data=data)
+ assert ret.status_code == 200, f'return code is {ret.status_code}'
diff --git a/seed/roundcube/dictionaries/31_roundcube.xml b/seed/roundcube/dictionaries/31_roundcube.xml
index a5afc3d7..2ea5416a 100644
--- a/seed/roundcube/dictionaries/31_roundcube.xml
+++ b/seed/roundcube/dictionaries/31_roundcube.xml
@@ -46,10 +46,12 @@
/usr/share/roundcubemail/
-
- /
-
-
+
+
+
+ /
+
+
diff --git a/seed/server/applicationservice.yml b/seed/server/applicationservice.yml
deleted file mode 100644
index 70c73671..00000000
--- a/seed/server/applicationservice.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-format: '0.1'
-description: Extra files to allowed easily Server-Client configuration
diff --git a/seed/server/doc.md b/seed/server/doc.md
deleted file mode 100644
index 5b941fc7..00000000
--- a/seed/server/doc.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Providers
-=========
-
-- clients : nom de domaine des clients (générique)
-- client_password : mot de passe des clients
-- client_ip : adresse IP des clients
diff --git a/seed/unbound/applicationservice.yml b/seed/unbound/applicationservice.yml
index 1e21e9af..3b22bdc1 100644
--- a/seed/unbound/applicationservice.yml
+++ b/seed/unbound/applicationservice.yml
@@ -4,4 +4,3 @@ service: true
depends:
- base-fedora-36
- dns-external
-provider: ExternalDNS
diff --git a/seed/unbound/dictionaries/20_unbound.xml b/seed/unbound/dictionaries/20_unbound.xml
index aba7c4bf..120779a9 100644
--- a/seed/unbound/dictionaries/20_unbound.xml
+++ b/seed/unbound/dictionaries/20_unbound.xml
@@ -25,13 +25,13 @@
-
-
-
-
-
+
+
+
+
+
@@ -39,5 +39,9 @@
ip_eth0
ip_dns
+
+ unbound_forward_address
+ unbound_allowed_client
+
diff --git a/seed/vaultwarden/applicationservice.yml b/seed/vaultwarden/applicationservice.yml
index fd7e1ac3..7df9bb1b 100644
--- a/seed/vaultwarden/applicationservice.yml
+++ b/seed/vaultwarden/applicationservice.yml
@@ -1,7 +1,7 @@
format: '0.1'
description: Vaultwarden
depends:
- - base-fedora-35
+ - base-fedora-36
- postgresql-client
- relay-mail-client
- reverse-proxy-client
diff --git a/seed/vaultwarden/dictionaries/40_vaultwarden.xml b/seed/vaultwarden/dictionaries/40_vaultwarden.xml
index aeab9bcf..53562f04 100644
--- a/seed/vaultwarden/dictionaries/40_vaultwarden.xml
+++ b/seed/vaultwarden/dictionaries/40_vaultwarden.xml
@@ -6,34 +6,32 @@
/etc/pki/ca-trust/source/anchors/ca_InternalReverseProxy.crt
/tmpfiles.d/0vaultwarden.conf
/etc/vaultwarden/config.env
+ /tests/vaultwarden.yml
-
- /
- /notifications/hub
-
-
+
vaultwarden
+
risotto
-
20
Vaultwarden
+
@@ -50,8 +48,19 @@
vaultwarden_admin_password
hide_secret
-
- vaultwarden_device_identifier
+
+ domain_name_eth0
+ vaultwarden_test_device_identifier
+
+
+ vaultwarden_domainname
+ vaultwarden_domainname
+ True
+ revprox_client_external_domainnames
+
+
+
+ revprox_client_location
True
diff --git a/seed/vaultwarden/funcs/risotto_setting.py b/seed/vaultwarden/funcs/risotto_setting.py
deleted file mode 100644
index a400771c..00000000
--- a/seed/vaultwarden/funcs/risotto_setting.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from uuid import uuid4 as _uuid4
-
-
-def gen_uuid():
- return str(_uuid4())
-
diff --git a/seed/vaultwarden/funcs/vaultwarden.py b/seed/vaultwarden/funcs/vaultwarden.py
index a400771c..12f4263b 100644
--- a/seed/vaultwarden/funcs/vaultwarden.py
+++ b/seed/vaultwarden/funcs/vaultwarden.py
@@ -1,6 +1,30 @@
+import __main__
+from os.path import dirname as _dirname, abspath as _abspath, join as _join, isfile as _isfile, isdir as _isdir
+from os import makedirs as _makedirs
from uuid import uuid4 as _uuid4
-def gen_uuid():
- return str(_uuid4())
+_HERE = _dirname(_abspath(__main__.__file__))
+_PASSWORD_DIR = _join(_HERE, 'password')
+
+def get_uuid(server_name: str) -> str:
+ if not server_name:
+ return
+ dir_name = _join(_PASSWORD_DIR, server_name)
+ if not _isdir(dir_name):
+ _makedirs(dir_name)
+ file_name = _join(dir_name, 'uuid')
+ if not _isfile(file_name):
+ uuid = str(_uuid4())
+ with open(file_name, 'w') as fh:
+ fh.write(uuid)
+ with open(file_name, 'r') as fh:
+ file_content = fh.read().strip()
+ return file_content
+
+
+def calc_vaulwarden_location(index):
+ if not index:
+ return '/'
+ return '/notifications/hub'
diff --git a/seed/vaultwarden/templates/vaultwarden.yml b/seed/vaultwarden/templates/vaultwarden.yml
new file mode 100644
index 00000000..06c489b0
--- /dev/null
+++ b/seed/vaultwarden/templates/vaultwarden.yml
@@ -0,0 +1,8 @@
+%set %%domain = %%revprox_client_external_domainnames[0]
+url: https://%%domain%%domain.revprox_client_location
+%set %%username='rougail_test@silique.fr'
+username: %%username
+password: %%get_password(server_name=%%domain_name_eth0, username=%%username, description='test', type="cleartext", hide=%%hide_secret, temporary=False)
+privkey: %%srv_dir/vaultwarden/rsa_key.pem
+uuid: %%vaultwarden_test_device_identifier
+revprox_ip: %%revprox_client_server_ip
diff --git a/seed/vaultwarden/templates/vaultwarden_config.env b/seed/vaultwarden/templates/vaultwarden_config.env
index e87abe1c..69a3fdda 100644
--- a/seed/vaultwarden/templates/vaultwarden_config.env
+++ b/seed/vaultwarden/templates/vaultwarden_config.env
@@ -256,11 +256,11 @@ INVITATION_ORG_NAME=%%vaultwarden_org_name
## For U2F to work, the server must use HTTPS, you can use Let's Encrypt for free certs
# DOMAIN=https://bw.domain.tld:8443
#>GNUNUX
-%set %%location = %%str(%%revprox_client_location[0])
+%set %%location = %%revprox_client_external_domainnames[0].revprox_client_location
%if %%location.endswith('/')
%set %%location = %%location[:-1]
%end if
-DOMAIN=https://%%revprox_client_external_domainname%%location
+DOMAIN=https://%%{revprox_client_external_domainnames[0]}%%location
#.||"
+def cipher_string_from_str(cipher_string: str) -> CipherString:
+ enc_type, data = cipher_string.split('.', 1)
+ if enc_type == '2':
+ iv, ct, mac = (b64decode(sdata) for sdata in data.split('|', 2))
+
+ d = { k: fn(enc_type, iv, mac, ct) for k,fn in cipher_string_fields.items() }
+ else:
+ iv, mac = None, None
+ ct = b64decode(data)
+ d = { k: fn(enc_type, iv, mac, ct) for k, fn in cipher_string_fields.items() }
+ return CipherString(**d)
+
+
+class VaultWarden:
+ def __init__(self,
+ url: str,
+ email: str,
+ uuid: str,
+ vaultwarden_key: str,
+ ) -> None:
+ self.vaultwarden_url = url
+ self.vaultwarden_email = email.lower()
+ self.vaultwarden_uuid = uuid
+ self.vaultwarden_login = None
+ self.vaultwarden_organizations = None
+ self.vaultwarden_key = vaultwarden_key
+
+ def register(self,
+ password: str,
+ valid: bool=True,
+ ) -> None:
+ iterations = self.get_iterations()
+ master_key, hash_password = self.hash_password(password,
+ iterations,
+ )
+ # generate symmetric key
+ token = token_bytes(64)
+ enc, mac = self._get_enc_mac(master_key)
+ key = self.encrypt_symmetric(token,
+ enc=enc,
+ mac=mac,
+ )
+ # generate asymmetric key
+ asym_key = RSA.generate(2048)
+ enc_private_key = self.encrypt_symmetric(asym_key.exportKey('DER', pkcs=8),
+ enc=token[:32],
+ mac=token[32:],
+ )
+ public_key = b64encode(asym_key.publickey().exportKey('DER')).decode()
+ data = {'name': self.vaultwarden_email.split('@')[0],
+ 'email': self.vaultwarden_email,
+ 'masterPasswordHash': hash_password,
+ 'masterPasswordHint': None,
+ 'key': key,
+ 'kdf': 0,
+ 'kdfIterations': iterations,
+ 'referenceId': None,
+ 'keys': {
+ 'publicKey': public_key,
+ 'encryptedPrivateKey': enc_private_key
+ }
+ }
+ register = self._post('api/accounts/register',
+ dumps(data),
+ )
+ if 'Object' in register and register['Object'] == 'error':
+ if register["ErrorModel"]['Message'] == 'User already exists':
+ return
+ raise Exception(register["ErrorModel"]["Message"])
+ if valid and isfile(self.vaultwarden_key):
+ self.login(password)
+ # values = self.get('/api/sync')
+ # user_id = values['Profile']['Id']
+ user_id = jwt_decode(self.vaultwarden_login['access_token'],
+ algorithm="RS256",
+ #pyjwt 1
+ verify=False,
+ #pyjwt 2
+ options={"verify_signature": False},
+ )['sub']
+ now = int(time())
+ url = self.vaultwarden_url
+ if url[-1] == '/':
+ url = url[:-1]
+ data = {'nbf': now,
+ 'exp': now + 432000,
+ 'iss': f'{url}|verifyemail',
+ 'sub': user_id,
+ }
+ with open(self.vaultwarden_key, 'rb') as private_key_fh:
+ private_key = RSA.importKey(private_key_fh.read()).exportKey('PEM')
+ token = jwt_encode(data, private_key, algorithm="RS256")
+ if isinstance(token, bytes):
+ tocken = token.decode()
+ data = {'userId': user_id,
+ 'token': token,
+ }
+ self._post('api/accounts/verify-email-token', dumps(data))
+
+ def login(self,
+ password: str,
+ ) -> None:
+ iterations = self.get_iterations()
+ master_key, hash_password = self.hash_password(password,
+ iterations,
+ )
+ data = {'grant_type': 'password',
+ 'username': self.vaultwarden_email,
+ 'password': hash_password,
+ 'scope': 'api offline_access',
+ 'client_id': 'desktop',
+ 'device_type': 7,
+ 'device_identifier': self.vaultwarden_uuid,
+ 'device_name': 'risotto',
+ }
+ vaultwarden_login = self._post('identity/connect/token', data)
+ if 'Object' in vaultwarden_login and vaultwarden_login['Object'] == 'error':
+ raise Exception(f'unable to log to VaultWarden: {vaultwarden_login["ErrorModel"]["Message"]}')
+ self.vaultwarden_login = vaultwarden_login
+ self.vaultwarden_login['master_key'] = master_key
+ self.vaultwarden_login['hash_password'] = hash_password
+
+ def get_iterations(self):
+ data = self._post('api/accounts/prelogin', dumps({'email': self.vaultwarden_email}))
+ return data['KdfIterations']
+
+ def hash_password(self,
+ password: str,
+ iterations: int,
+ ) -> str:
+ master_key = pbkdf2_hmac('sha256',
+ password.encode(),
+ self.vaultwarden_email.encode(),
+ iterations,
+ )
+ passwd = pbkdf2_hmac('sha256',
+ master_key,
+ password.encode(),
+ 1,
+ )
+ return master_key, b64encode(passwd).decode()
+
+ def decrypt(self,
+ cipher_string: str,
+ organization_id: str=None,
+ ) -> None:
+ cipher = cipher_string_from_str(cipher_string)
+ if cipher.enc_type == 2:
+ return self.decrypt_symmetric(cipher,
+ organization_id,
+ )
+ elif cipher.enc_type == 4:
+ if organization_id:
+ raise Exception('cipher type {cipher.enc_type} cannot have organization_id')
+ return self.decrypt_asymmetric(cipher)
+ raise Exception(f'Unknown cipher type {cipher.enc_type}')
+
+ def decrypt_symmetric(self,
+ cipher: str,
+ organization_id: str=None,
+ enc: str=None,
+ mac: str=None,
+ ) -> bytes:
+ # i.e: AesCbc256_HmacSha256_B64 (jslib/src/enums/encryptionType.ts)
+ assert cipher.enc_type == 2
+ if enc is None:
+ enc = self.vaultwarden_organizations[organization_id]['key'][:32]
+ mac = self.vaultwarden_organizations[organization_id]['key'][32:]
+ # verify the MAC
+ cmac = hmac_new(mac,
+ cipher.iv + cipher.ct,
+ sha256,
+ )
+ assert cipher.mac == cmac.digest()
+
+ # decrypt the content
+ c = AES.new(enc,
+ AES.MODE_CBC,
+ cipher.iv,
+ )
+ plaintext = c.decrypt(cipher.ct)
+
+ # remove PKCS#7 padding from payload, see RFC 5652
+ # https://tools.ietf.org/html/rfc5652#section-6.3
+ pad_len = plaintext[-1]
+ padding = bytes([pad_len] * pad_len)
+ if plaintext[-pad_len:] == padding:
+ plaintext = plaintext[:-pad_len]
+ return plaintext
+
+ def decrypt_asymmetric(self,
+ cipher: str,
+ ) -> str:
+ private_key = self.decrypt(self.vaultwarden_login['PrivateKey'])
+ c = PKCS1_OAEP.new(RSA.importKey(private_key))
+ return c.decrypt(cipher.ct)
+
+ def encrypt_symmetric(self,
+ content: bytes,
+ organization_id: str=None,
+ enc: str=None,
+ mac: str=None,
+ ) -> None:
+ iv = token_bytes(16)
+ if enc is None:
+ enc = self.vaultwarden_organizations[organization_id]['key'][:32]
+ mac = self.vaultwarden_organizations[organization_id]['key'][32:]
+ c = AES.new(enc,
+ AES.MODE_CBC,
+ iv,
+ )
+ pad_len = 16 - len(content) % 16
+ padding = bytes([ pad_len ] * pad_len)
+ ct = c.encrypt(content + padding)
+ cmac = hmac_new(mac,
+ iv + ct,
+ sha256,
+ )
+ return f"2.{b64encode(iv).decode()}|{b64encode(ct).decode()}|{b64encode(cmac.digest()).decode()}"
+
+ def encrypt_asymmetric(self,
+ plaintext: str,
+ key: str,
+ ) -> str:
+ rsa_key = RSA.importKey(key)
+ cipher = PKCS1_OAEP.new(rsa_key).encrypt(plaintext)
+ b64_cipher = b64encode(cipher).decode()
+ return f"4.{b64_cipher}"
+
+ def get(self,
+ url: str,
+ ) -> None:
+ with session() as req:
+ resp = req.get(self.vaultwarden_url + url, headers=self._get_headers())
+ assert resp.status_code == 200
+ try:
+ response = resp.json()
+ except:
+ response = resp.text
+ return response
+
+ def _post(self,
+ url: str,
+ data: dict,
+ ) -> None:
+ with session() as req:
+ resp = req.post(self.vaultwarden_url + url,
+ data=data,
+ headers=self._get_headers(),
+ )
+ assert resp.status_code == 200, f'unable to post to url {self.vaultwarden_url}{url} with data {data}: {resp.text}'
+ try:
+ response = resp.json()
+ except:
+ response = resp.text
+ return response
+
+ def _put(self,
+ url: str,
+ data: dict,
+ ) -> None:
+ with session() as req:
+ resp = req.put(self.vaultwarden_url + url,
+ data=data,
+ headers=self._get_headers(),
+ )
+ try:
+ response = resp.json()
+ except:
+ response = resp.text
+ return response
+
+ def _get_headers(self,
+ ) -> None:
+ if self.vaultwarden_login == None:
+ return None
+ return {'Authorization': f'Bearer {self.vaultwarden_login["access_token"]}'}
+
+ def load_organizations(self,
+ only_default: bool=False,
+ ) -> None:
+ values = self.get('/api/sync')
+ enc, mac = self._get_enc_mac(self.vaultwarden_login['master_key'])
+ # 'decrypt' the user_key to produce the actual keys
+ cipher = cipher_string_from_str(self.vaultwarden_login['Key'])
+ plaintext_userkey = self.decrypt_symmetric(cipher,
+ enc=enc,
+ mac=mac,
+ )
+ assert len(plaintext_userkey) == 64
+ self.vaultwarden_organizations = {None: {'key': plaintext_userkey, 'name': 'default', 'collections': {}}}
+ if not only_default:
+ for organization in values['Profile']['Organizations']:
+ plaintext = self.decrypt(organization['Key'])
+ self._add_organization(plaintext,
+ organization,
+ )
+ for collection in values['Collections']:
+ name = self.decrypt(collection['Name'],
+ collection['OrganizationId'],
+ ).decode()
+ self.vaultwarden_organizations[collection['OrganizationId']]['collections'][name] = collection['Id']
+
+ def _get_enc_mac(self,
+ master_key: str,
+ ) -> tuple:
+ enc = hkdf_expand(master_key,
+ b'enc',
+ 32,
+ sha256,
+ )
+ mac = hkdf_expand(master_key,
+ b'mac',
+ 32,
+ sha256,
+ )
+ return enc, mac
+
+ def _add_organization(self,
+ plaintext: bytes,
+ organization: dict,
+ ) -> None:
+ organization_id = organization['Id']
+ self.vaultwarden_organizations[organization_id] = {'name': organization['Name'], 'key': plaintext, 'collections': {}}
+
+ def try_to_confirm(self,
+ organization_id,
+ email,
+ ) -> bool:
+ # user is now in organization
+ user = self.get_user_informations(organization_id,
+ email,
+ )
+
+ # if account exists now, confirm it
+ if user['public_key']:
+ key = self.encrypt_asymmetric(self.vaultwarden_organizations[organization_id]['key'],
+ user['public_key'],
+ )
+ data = {"key": key}
+ confirmed = self._post(f'api/organizations/{organization_id}/users/{user["user_id"]}/confirm',
+ dumps(data),
+ )
+ return user['user_id'], 'Object' not in confirmed or confirmed['Object'] != 'error'
+ return user['user_id'], False
+
+ def get_user_informations(self,
+ organization_id: str,
+ email: str,
+ ) -> None:
+ users = self.get(f'/api/organizations/{organization_id}/users')
+ for user in users['Data']:
+ if user['Email'] == email:
+ user_public_key = self.get(f'/api/users/{user["UserId"]}/public-key')
+ if not user_public_key['PublicKey']:
+ public_key = None
+ else:
+ public_key = b64decode(user_public_key['PublicKey'])
+ return {'user_id': user['Id'],
+ 'public_key': public_key,
+ }
+ raise Exception(f'unknow email {email} in organization id {organization_id}')
+
+ def create_organization(self,
+ email: str,
+ organization_name: str,
+ ) -> None:
+ private_key = self.decrypt(self.vaultwarden_login['PrivateKey'])
+ token = token_bytes(64)
+ key = self.encrypt_asymmetric(token,
+ private_key,
+ )
+ # defaut collection_name is organization_name
+ data = {
+ "key": key,
+ "collectionName": self.encrypt_symmetric(organization_name.encode(),
+ enc=token[:32],
+ mac=token[32:],
+ ),
+ "name": organization_name,
+ "billingEmail": email,
+ "planType": 0,
+ }
+ organization = self._post('api/organizations',
+ dumps(data),
+ )
+ self.load_organizations()
+ #self._add_organization(token,
+ # organization,
+ # )
+ return organization['Id']
+
+ def invite(self,
+ organization_id: str,
+ email: str,
+ ) -> bool:
+ data = {'emails': [email],
+ 'collections': [],
+ 'accessAll': False,
+ 'type': 2,
+ }
+ for collection_id in self.vaultwarden_organizations[organization_id]['collections'].values():
+ data['collections'].append({'id': collection_id,
+ 'readOnly': True,
+ 'hidePasswords': False,
+ })
+ self._post(f'api/organizations/{organization_id}/users/invite',
+ dumps(data),
+ )
+
+ def create_collection(self,
+ organization_id: str,
+ collection_name: str,
+ user_id: str=None,
+ ) -> None:
+ data = {"groups": [],
+ "name": self.encrypt_symmetric(collection_name.encode(),
+ organization_id,
+ ),
+ }
+ collection = self._post(f'api/organizations/{organization_id}/collections',
+ dumps(data),
+ )
+ self.vaultwarden_organizations[organization_id]['collections'][collection_name] = collection['Id']
+ if user_id:
+ self.inscript_collection(organization_id,
+ collection['Id'],
+ user_id,
+ )
+ return collection['Id']
+
+ def inscript_collection(self,
+ organization_id: str,
+ collection_id: str,
+ user_id: str,
+ ) -> None:
+ data = [{'id': user_id,
+ 'readOnly': True,
+ 'hidePasswords': False,
+ }]
+ self._put(f'api/organizations/{organization_id}/collections/{collection_id}/users',
+ dumps(data),
+ )
+
+ def store_password(self,
+ organization_id: str,
+ collection_id: str,
+ name: str,
+ username: str,
+ password: str,
+ uris: list=None,
+ ) -> None:
+ """create a cipher et store it in a share collection
+ """
+ # FIXME uris are encoded
+ data = {"cipher": {
+ "type": 1,
+ "folderId": None,
+ "organizationId": organization_id,
+ "name": self.encrypt_symmetric(name.encode(),
+ organization_id,
+ ),
+ "notes": None,
+ "favorite": False,
+ "login":
+ {"response": None,
+ "uris": uris,
+ "username": self.encrypt_symmetric(username.encode(),
+ organization_id,
+ ),
+ "password": self.encrypt_symmetric(password.encode(),
+ organization_id,
+ ),
+ "passwordRevisionDate": None,
+ "totp": None,
+ }
+ },
+ "collectionIds": [collection_id],
+ }
+ self._post('api/ciphers/admin',
+ dumps(data),
+ )
+
+ def get_password(self,
+ organization_id: str,
+ collection_name: str,
+ name: str,
+ username: str,
+ ) -> list:
+ if not collection_name in self.vaultwarden_organizations[organization_id]['collections']:
+ return
+ collection_id = self.vaultwarden_organizations[organization_id]['collections'][collection_name]
+ ciphers = self.get(f'api/ciphers/organization-details?organizationId={organization_id}')
+ for cipher in ciphers['Data']:
+ if collection_id in cipher['CollectionIds'] and \
+ self.decrypt(cipher['Data']['Name'], organization_id).decode() == name and \
+ self.decrypt(cipher['Data']['Username'], organization_id).decode() == username:
+ return self.decrypt(cipher['Data']['Password'], organization_id).decode()