Compare commits

..

480 commits

Author SHA1 Message Date
Alexandre Aubin
0496cadade Update changelog for 12.0.3 2024-08-31 19:55:46 +02:00
Alexandre Aubin
d6167c5586 Merge remote-tracking branch 'origin/dev' into bookworm 2024-08-31 19:44:48 +02:00
Alexandre Aubin
dd1853ee15
Merge pull request #1888 from YunoHost/fix_repo_extra_yarn
Add a workaround for warnings issued for yarn when adding extra repo …
2024-08-30 15:42:50 +02:00
Félix Piédallu
317a7f98b4 resources: Workaround for duplicate yarn repository 2024-08-30 15:41:36 +02:00
Alexandre Aubin
72a13c7b48 ja ja typing stuff gneugneugneu 2024-08-28 15:04:40 +02:00
Alexandre Aubin
2cc1e7224e apps/config panel: fix _compute_binds + _dump_options_types_and_binds because of the major changes in config panel workflow in bookworm 2024-08-28 15:00:06 +02:00
Alexandre Aubin
36ffa133de merp 2024-08-27 16:21:21 +02:00
Alexandre Aubin
70a5bbc76f
zblerg 2024-08-27 16:15:59 +02:00
Alexandre Aubin
f37bfca80b Merge remote-tracking branch 'origin/dev' into bookworm 2024-08-27 16:04:26 +02:00
Alexandre Aubin
16e8205367 quality/typing: missing type import 2024-08-20 18:20:20 +02:00
Alexandre Aubin
ff03885564 i18n: add i18n for domain and global config panel and sections 2024-08-20 18:17:02 +02:00
Alexandre Aubin
9973cc703d
Update config_domain.toml: propagate change in theme list from yunohost-portal 2024-08-20 16:35:33 +02:00
Alexandre Aubin
560dac1731 domain: reorder config panel sections 2024-08-19 01:23:09 +02:00
Alexandre Aubin
831131476e portal: add proper i18n string + help for new settings 2024-08-19 01:13:51 +02:00
Alexandre Aubin
748a20d864 portal: change the way the new 'public apps' page in the portal is configured: add a simple bool toggle instead of having the 'public apps page' as a default app option, which allows to still configure a default app while the portal has the public apps page 2024-08-19 01:02:28 +02:00
Alexandre Aubin
8f636561d9 portal: allow to configure custom CSS from the domain config panel 2024-08-19 00:46:00 +02:00
Alexandre Aubin
a6b7ba8435 portal/sso: with the public app page, fix the root of the domain not redirecting to /yunohost/sso 2024-08-17 18:40:33 +02:00
Alexandre Aubin
603c64e348 portal: fix extra app tiles not being displayed, gotta use the perm id as key, not just the app id (credit rodinux) 2024-08-17 15:11:01 +02:00
Alexandre Aubin
2ef5b24e40 tests: moar friskies 2024-08-15 20:40:22 +02:00
Alexandre Aubin
fe1c04fb2c quality: fix type confusion + mypy not actually checking the functions because of the is_unit_operation decorator, which requires typing wizardry to understan the signature change 2024-08-15 19:29:12 +02:00
Alexandre Aubin
efce7f9f05 Fix weird typing issue breaking runtime ~_~ 2024-08-15 11:58:32 +02:00
Alexandre Aubin
611846aa19 quality: similar to previous commit... 2024-08-15 02:53:26 +02:00
Alexandre Aubin
d4f39da20f quality: zblerg type must exist in runtime too, but using the classname as string works? 2024-08-15 02:16:09 +02:00
Alexandre Aubin
4a03cdcb4c quality: unused import é_è 2024-08-15 01:50:06 +02:00
Alexandre Aubin
1ba75df0e2 quality: add type hints to user.py 2024-08-15 01:46:55 +02:00
Alexandre Aubin
4ee8d4e8ca ci: fix mypy complains 2024-08-14 20:26:40 +02:00
Alexandre Aubin
2976e7bf60 ci: moar fixes to get the pytest import right ? x_x 2024-08-14 13:58:10 +02:00
Alexandre Aubin
ace7dd19b1 ci: remove tmp --debug, issue was due to hideproc failing to enable because the container wasnt running with the proper security options 2024-08-14 13:31:28 +02:00
Alexandre Aubin
0bbc14f54b ci: when running pytest, make sure that 'import yunohost' will load the code being tested, not the one from /usr/lib/python3/dist-packages ? 2024-08-14 13:19:46 +02:00
Alexandre Aubin
fd040b864e ci: drop unused option --yunodebug in pytest conftest 2024-08-14 13:14:29 +02:00
Alexandre Aubin
4fc929005d ci: speedup git clone for test_apps using --depth 1 2024-08-14 13:04:15 +02:00
Alexandre Aubin
2fe24424fb ci: try to fix coverage remembering absolute path instead of relative paths ? 2024-08-14 01:42:12 +02:00
Alexandre Aubin
87c30dd1bc ci: fix indent? 2024-08-14 00:14:26 +02:00
Alexandre Aubin
55e7e798fa ci: yoloattempt to combine the coverage reports 2024-08-14 00:02:59 +02:00
Alexandre Aubin
5a6a915afa ci: force coverage file to have a different name per job ? 2024-08-13 23:42:17 +02:00
Alexandre Aubin
98731ba9dc ci: merp? 2024-08-13 23:10:47 +02:00
Alexandre Aubin
4f3b9df3f8 ci: try to generate partial coverage data arterfact during tests, which we should be able to combine later? 2024-08-13 23:05:31 +02:00
Alexandre Aubin
33181596a1 ci: hmmm temporarily(?) enable debug during postinstall to investigate a hook failure 2024-08-13 22:21:59 +02:00
Alexandre Aubin
69b9055fee ci: also get rid of those old temporary nginx restart for debug 2024-08-08 20:37:14 +02:00
Alexandre Aubin
66049a2624 Merge remote-tracking branch 'origin/dev' into bookworm 2024-08-08 20:36:29 +02:00
Alexandre Aubin
cb6778cfe2 Merge remote-tracking branch 'origin/dev' into bookworm 2024-08-08 19:47:12 +02:00
Alexandre Aubin
d639e1c426 ci: ignore boring warning 'Could not identify correctly the dns zone for domain sub.domain.tld' 2024-08-08 19:24:16 +02:00
Alexandre Aubin
94594e5a3c ci: try skipping diagnosis during upgrade to speed things up a bit? 2024-08-08 19:14:05 +02:00
Alexandre Aubin
7f2da0af73 ci: tweaks to try to run build/lints into a 'permanent' container (not recreated every time) to speed up things 2024-08-08 16:46:24 +02:00
Alexandre Aubin
a9e71e88df ci: fix missing image name for i18n job 2024-08-08 16:20:33 +02:00
Alexandre Aubin
14ee49fc7b ci: really aleks 2024-08-07 19:05:45 +02:00
Alexandre Aubin
0598182541 ci: Merp, explicitly use python3 2024-08-07 18:18:14 +02:00
Alexandre Aubin
fe9a4fba5d ci: new image names, now using 'shared' image build process with the appci 2024-08-07 18:06:54 +02:00
Alexandre Aubin
44920d8914 ci: fix test_sso_basic_auth_header now that the default is with-password ? 2024-08-05 22:11:58 +02:00
Alexandre Aubin
9e93130672 ci: fix auth header test? 2024-08-05 22:10:12 +02:00
Alexandre Aubin
279f332883 domain settings: add a title to the Email section to have a separation w.r.t. the portal settings 2024-08-05 22:09:14 +02:00
Alexandre Aubin
24fb877255 portail/domain settings: Improve explanation about search engine 2024-08-05 22:08:31 +02:00
Alexandre Aubin
cdf443c86e portalapi: fix portal_user_intro not being sent when authenticated, hence not displayed at all 2024-08-05 22:07:56 +02:00
Alexandre Aubin
bc93a2e079 portalapi: we don't need absolute URLs for app logos ? (This ain't working when enabling the 'show other domains apps' because of CSP) 2024-08-05 21:08:06 +02:00
Alexandre Aubin
656e5c75d1 ci: Fix test_permission_propagation_on_ssowat ? 2024-08-05 20:53:32 +02:00
Alexandre Aubin
54fd311bec Update changelog for 12.0.2 2024-08-01 18:09:47 +02:00
Alexandre Aubin
7a45675f7a Merge remote-tracking branch 'origin/dev' into bookworm 2024-08-01 18:08:06 +02:00
Alexandre Aubin
0503a38a74 bullseye->bookworm: add a trick to flag the migration as done if it's still marked as pending 2024-07-31 00:06:33 +02:00
Alexandre Aubin
eb1d715764 Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-31 00:02:19 +02:00
Kayou
7b50c4eb6a
remove redis regen conf too 2024-07-30 15:38:58 +02:00
Alexandre Aubin
6f1e3411fc Update changelog for 12.0.1 2024-07-26 22:48:10 +02:00
Alexandre Aubin
a1e63a17bb Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-26 21:19:26 +02:00
Alexandre Aubin
ccfea312fe Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-26 21:00:17 +02:00
Alexandre Aubin
9a6ad19c0e apps/ssowat: fix default value for auth_header and protect_against_basic_auth_spoofing 2024-07-26 20:55:27 +02:00
Alexandre Aubin
d0ce8cce53 Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-25 19:29:59 +02:00
Alexandre Aubin
970e2e2dbc services: redis ain't installed by default anymore on yunohost 2024-07-25 18:53:26 +02:00
Alexandre Aubin
f547ac2515
Merge pull request #1919 from YunoHost/fix-appconfig-panel
appconfig panel: new values were replaced by default values
2024-07-23 17:31:35 +02:00
Kayou
a069cda08c
fix permission test 2024-07-23 12:17:16 +02:00
Kayou
03def5b109
appconfig panel: new values were replaced by default values 2024-07-23 11:58:35 +02:00
Alexandre Aubin
505e3db3bb ci: fallback to bookworm branch for build such that even on PR on top of bookworm the ci should work ... 2024-07-22 04:22:54 +02:00
Alexandre Aubin
42114978af
Update regenconf.py: cleanup legacy rspamd and metronome hash entries 2024-07-18 13:40:22 +02:00
Alexandre Aubin
88b11bee09 mail: fix opendkim assuming mails arent authenticated because of missing {auth_type} from postfix 2024-07-17 18:37:15 +02:00
Alexandre Aubin
b9178e750c Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-17 17:56:44 +02:00
Alexandre Aubin
609748e8c2 mail: Make sure to restart opendkim after regenconf 2024-07-17 17:45:32 +02:00
Alexandre Aubin
0ade9d0e6f Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-17 16:57:08 +02:00
Alexandre Aubin
6453d15011 bullseye->bookworm: fix the /var/log/yunohost/operations folder migration, in particular because the new folder will already exist when this code is triggered after the bullseye->bookworm (because 'yunohost tools regen-conf' will create a file in the new folder before actually ariving at this code) so we can't just rely on testing wether or not the new folder exists 2024-07-17 16:56:55 +02:00
9a0960dcf1 log.py: use os.path.join instead 2024-07-17 00:03:41 +02:00
91f747eaee log.py: fix listing of log files 2024-07-17 00:02:58 +02:00
03a5c3d144 Ensure postgresql is upgraded to 15.
Some apps required pg 16, so pg_upgradecluster tries to upgrade 13 to 16 and fails (cluster already exists)
2024-07-16 23:02:25 +02:00
Alexandre Aubin
443eac47ca Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-16 22:59:56 +02:00
Alexandre Aubin
946d2289d5 cleanup: we probably don't need to keep the super-legacy service entries anymore 2024-07-15 23:02:52 +02:00
Alexandre Aubin
daa4c39899 debian: remove dependency to redis-server 2024-07-15 22:30:17 +02:00
Alexandre Aubin
6638ee33b0 Remove trick to force checking for migration 0027 strings 2024-07-15 22:25:25 +02:00
Alexandre Aubin
52443174a0 Rename 0027_migrate_to_bookworm.py.disabled -> 0027_migrate_to_bookworm.py 2024-07-15 22:24:26 +02:00
Alexandre Aubin
f16011a138 Remove reference to rspamd, rspamd is to become an app 2024-07-15 22:23:59 +02:00
Alexandre Aubin
262a3c5cf2 Merge remote-tracking branch 'origin/dev' into bookworm 2024-07-15 16:54:21 +02:00
Alexandre Aubin
128df3d804
Update services.yml 2024-07-11 11:35:28 +02:00
Alexandre Aubin
3a06241956
Merge pull request #1905 from YunoHost/auth_header-to-false
ssowatconf: change auth_header from None to false
2024-07-11 11:14:47 +02:00
Kayou
4f7fa32f34
ssowatconf: change auth_header from None to false 2024-07-11 11:09:42 +02:00
Alexandre Aubin
c8183188ed Gotta depend on python3-jinja2 >= 3.0 to prevent issues with markupsaf 2024-07-06 17:25:35 +02:00
Alexandre Aubin
6120a5a8e4 YunoHost has a hard dependence on python 3.11 because of type hints syntax ... Also ${python3:Depends} and ${misc:Depends} don't do anything special in our context 2024-07-06 17:04:08 +02:00
Alexandre Aubin
7f7bafb339 Merge branch 'migrate-to-bookworm' into bookworm 2024-07-06 16:58:42 +02:00
Alexandre Aubin
5429885e0a Fix i18n strings? 2024-07-03 23:41:57 +02:00
Alexandre Aubin
30c512fc91 Fix i18n messages for bullseye->bookworm 2024-07-03 23:26:33 +02:00
Alexandre Aubin
edaffcf743 Adapt venv-rebuild migration for bookworm 2024-07-03 23:15:28 +02:00
Alexandre Aubin
3d05ff1d0e Cleanup old migrations 2024-07-03 22:07:06 +02:00
Alexandre Aubin
4b8506f71f Add migration for postgresql 13->15 after migrating to bookworm 2024-07-03 20:20:16 +02:00
Alexandre Aubin
1a85521f1f Didnt realize this line could be removed during previous merge 2024-07-03 19:09:04 +02:00
Alexandre Aubin
8366e4b7c4 Merge branch 'migrate-to-bookworm' into bookworm 2024-07-03 17:27:09 +02:00
Alexandre Aubin
b523089e48
Merge pull request #1887 from YunoHost/fix_yarn_test
Test resources with influxdb instead of yarn that is installed by default now
2024-06-30 17:42:23 +02:00
9cf8a7b6a7 Test resources with influxdb instead of yarn that is installed by default now 2024-06-30 17:38:25 +02:00
Alexandre Aubin
b661356783 Merge remote-tracking branch 'origin/dev' into bookworm 2024-06-30 00:23:58 +02:00
Alexandre Aubin
e13e9bc378 Merge remote-tracking branch 'origin/dev' into bookworm 2024-06-13 14:27:27 +02:00
Alexandre Aubin
bb4f9cc1da Merge remote-tracking branch 'origin/migrate-to-bookworm' into bookworm 2024-06-10 12:39:52 +02:00
Alexandre Aubin
caa26ee005 tests: remove old reliances on xmpp.main 2024-06-10 00:40:13 +02:00
Alexandre Aubin
2e59393943 ldap/permissions: add a migration to delete the legacy xmpp.main perm 2024-06-10 00:33:46 +02:00
Alexandre Aubin
cff89050df Remaining metronome trick to cleanup 2024-06-09 15:57:50 +02:00
Alexandre Aubin
37c99082f2
Update certificate.py: moar typo @_@ 2024-06-09 12:08:18 +02:00
Alexandre Aubin
d2259928ce
Update certificate.py: typo @_@ 2024-06-09 12:06:23 +02:00
Alexandre Aubin
1e527a8214
Merge pull request #1831 from YunoHost/handle-metronome-as-an-app
Handle metronome as an app
2024-06-09 10:55:41 +02:00
Alexandre Aubin
40778817ac Merge remote-tracking branch 'origin/bookworm' into handle-metronome-as-an-app 2024-06-08 16:35:53 +02:00
Alexandre Aubin
46372a0f22 dns+certs: add a new cert_alternate_names hook + improve custom_dns_rules hook 2024-06-08 16:35:44 +02:00
Alexandre Aubin
7c71bd8663 Merge remote-tracking branch 'origin/dev' into bookworm 2024-06-04 16:11:40 +02:00
Alexandre Aubin
b233ce1f15 Merge remote-tracking branch 'origin/dev' into bookworm 2024-05-22 13:14:04 +02:00
ljf (zamentur)
7674ba5ceb [fix] Forbidden keywords config panel 2024-05-22 07:14:17 +02:00
Alexandre Aubin
d4ef6461f3 Merge remote-tracking branch 'origin/dev' into bookworm 2024-05-19 21:01:34 +02:00
Alexandre Aubin
c02933cce4 Merge remote-tracking branch 'origin/dev' into bookworm 2024-05-19 20:52:43 +02:00
Alexandre Aubin
42d2f789cf Merge remote-tracking branch 'origin/dev' into bookworm 2024-05-19 20:47:27 +02:00
Alexandre Aubin
dc5c43255b
Merge pull request #1809 from orhtej2/fix_sso_domain
[bookworm] Return auth cookie valid for whole main domain.
2024-05-18 14:22:05 +02:00
Alexandre Aubin
4769242dc5 Yolodraft: handle metronome as an app 2024-05-11 14:50:46 +02:00
Alexandre Aubin
582350c145 mail: attempt to move to opendkim instead of rspamd for dkim signing/verify 2024-05-07 16:31:50 +02:00
Alexandre Aubin
2d8cd9f88a
Merge pull request #1822 from selfhoster1312/portal-without-apps
Allow users to access their own domain portal without app permission
2024-05-07 14:03:28 +02:00
selfhoster1312
5e406a55fa Allow users to access their own domain portal without app permission 2024-05-07 12:04:49 +02:00
tituspijean
3182aa85e7
Merge branch 'dev' into bookworm 2024-04-20 10:45:38 +02:00
Alexandre Aubin
7a077b5de9 Merge remote-tracking branch 'origin/dev' into bookworm 2024-04-11 15:51:19 +02:00
Alexandre Aubin
cc39fca559 Merge branch 'dev' into bookworm 2024-04-11 00:39:00 +02:00
Alexandre Aubin
2e3b4ae1f2 perf: add cache for system utils that fetch debian_version, debian_version_id, system_arch, system_virt 2024-04-10 23:08:31 +02:00
Alexandre Aubin
26bead701d Merge branch 'migrate-to-bookworm' into bookworm 2024-04-10 21:14:48 +02:00
Alexandre Aubin
458221e845 Merge remote-tracking branch 'origin/dev' into bookworm 2024-04-10 21:09:18 +02:00
orhtej2
10c6c807bf Return auth cookie valid for whole main domain. 2024-03-26 01:04:43 +01:00
Alexandre Aubin
2a4d5bfbc6 helpers/configpanel: fix assumption claquée au sol that $install_dir and $final_path always exist 2024-03-20 23:44:13 +01:00
Alexandre Aubin
7d640f84c6 ci: autoblack is now a github workflow 2024-03-14 09:22:32 +01:00
Alexandre Aubin
96494cbee4 apps: simplify env creation for config panel script, _make_environment_for_app_script already handles everything.. 2024-03-14 09:14:10 +01:00
Alexandre Aubin
11cabc0fe7 Merge remote-tracking branch 'origin/dev' into bookworm 2024-03-14 09:13:37 +01:00
Alexandre Aubin
76be0ee42c
Update 15-nginx: typo >_> 2024-02-05 19:30:51 +01:00
Alexandre Aubin
99e252107f Dafuq Aleks, we do need to send the SSO cookie on all route not just the portal API route 2024-02-04 15:18:48 +01:00
Alexandre Aubin
7f1dd865d7 Typo :| 2024-02-03 21:36:30 +01:00
Alexandre Aubin
c77d05e693 portal: store custom logos in a /usr/share/yunohost/customassets, and only store the file id which the front should then query as a regular asset 2024-02-03 21:07:45 +01:00
Alexandre Aubin
f248086fb6 nginx: nevermind applogos caching, nowadays nginx/browsers automatically cache static assets with etags etc ? 2024-02-03 20:28:41 +01:00
Alexandre Aubin
8823ef0a2c nginx: can't really delete the old yunohost_panel.conf.inc because it may be included by nginx app confs ~_~ 2024-02-03 20:06:10 +01:00
Alexandre Aubin
6958ea3b0f regenconf: more factorizing in yunohost hook 2024-02-03 20:00:13 +01:00
Alexandre Aubin
a47321e1bc Typo 2024-02-03 19:44:36 +01:00
Alexandre Aubin
402327d6ef regenconf: factorize all the dirs/perm stuff in the yunohost hook 2024-02-03 19:43:13 +01:00
Alexandre Aubin
ceace620da regenconf: factorize nginx regenconf a bit 2024-02-03 18:58:34 +01:00
Alexandre Aubin
ce529107be regenconf: remove unecessary/inconsistent check that script is ran as root... 2024-02-03 18:42:08 +01:00
Alexandre Aubin
41da1daf02 nginx: remove old 'yunohost_panel.conf' stuff 2024-02-03 18:39:25 +01:00
Alexandre Aubin
98701ebdab regenconf: the 'plain' subfolder thing for nginx conf is annoying, flatten things up 2024-02-03 18:36:55 +01:00
Alexandre Aubin
ca835534de portal: serve app logos from an sso-specific route (to keep separation of concern etc) 2024-02-03 18:24:31 +01:00
Alexandre Aubin
69cc6345a5 webadmin: add cache config for app logos 2024-02-03 18:21:02 +01:00
Alexandre Aubin
6a3c77eda1 auth: restrict cookies to the appropriate api 2024-02-03 17:29:19 +01:00
Alexandre Aubin
9a7ec301a6 configpanel: improve debugging/error message when pydantic fails to validate the options etc 2024-02-03 16:51:02 +01:00
Alexandre Aubin
b427783721 Make linter happy 2024-01-31 03:17:21 +01:00
Alexandre Aubin
ec8594a325 Zgrombpf 2024-01-31 03:16:07 +01:00
Alexandre Aubin
ddbd367085 Fix app resource test 2024-01-31 02:43:43 +01:00
Alexandre Aubin
133ce6e6b2 Fix permission test 2024-01-31 02:14:35 +01:00
Alexandre Aubin
b4b55fae54 Fix empty catalog test 2024-01-31 02:09:29 +01:00
Alexandre Aubin
e682fe9abd apps/portalconf: use description from local manifest + cases where the app aint in catalog or has no logo hash 2024-01-31 02:06:29 +01:00
Alexandre Aubin
c9a4838b5a tests/sso: add tests for subdomain and secondary 'main' domain 2024-01-31 01:35:37 +01:00
Alexandre Aubin
80d6a6f08d tests: fix/add tests for SSO 2024-01-31 00:16:26 +01:00
Alexandre Aubin
fc048f804e tests/sso: fix redirect test, gotta explicitly tell python to not follow 302s 2024-01-30 22:59:14 +01:00
Alexandre Aubin
61a43df169 Cleanup unused code 2024-01-30 19:53:32 +01:00
Alexandre Aubin
18df4bbc32 ssowatconf: replace use_remote_user_var_in_nginx_conf with protect_against_basic_auth_spoofing, and basic-with/without-password for the auth_header flag 2024-01-30 19:46:18 +01:00
Alexandre Aubin
d9daf111e6 helpers: fix pattern to wait for when fail2ban is starting/reloading 2024-01-30 19:18:06 +01:00
Alexandre Aubin
cc97efa033 helpers: remove pre-4.2 behavior for default file/folders permissions 2024-01-30 17:06:33 +01:00
Alexandre Aubin
6e85323a8f Unused imports 2024-01-27 18:08:25 +01:00
Alexandre Aubin
8bd2104722 Moar lazy loading 2024-01-27 16:16:14 +01:00
Alexandre Aubin
dda095dc15 Cleanup legacy 2024-01-27 16:13:57 +01:00
Alexandre Aubin
b33a47f27d broken imports ... + more lazy loading 2024-01-27 16:13:37 +01:00
Alexandre Aubin
6a01b657da appscatalog: this 'init' step is overengineered ... let's instead say the nominal case is that there's no apps_catalog.yml defined, and in this case we use the default catalog 2024-01-27 15:51:56 +01:00
Alexandre Aubin
2d42f59377 logs: remove the intermediate useless 'categories' directory, this is triggering me so much 2024-01-27 15:31:10 +01:00
Alexandre Aubin
902e65f9bc domain: don't regen the ssowat conf when changing the main domain (creates an issue during postinstall) 2024-01-27 15:23:22 +01:00
Alexandre Aubin
045869283c portalapi: don't leak the full list of users nor private apps 2024-01-22 21:39:54 +01:00
Alexandre Aubin
16d7f77f91 apps: checking that relevant services are up before app actions: the 'services' key is a super old thing from packaging v1 era, and nowadays mariadb/php/.. are likely to be installed on the fly, not pre-installed on the system anymore. This should be reworked someday 2024-01-22 21:19:56 +01:00
Alexandre Aubin
0796273be3 sso/portal: more tests 2024-01-22 19:52:25 +01:00
axolotle
7f53f56471 portal: add app's description + logo from catalog in portal settings 2024-01-19 13:33:24 +01:00
axolotle
56b5670e4c domaiiconfig: add portal search engine option 2024-01-19 13:31:21 +01:00
axolotle
3bb5702855 UrlOption: fix serialize HttpUrl to str in post validator so it can be saved 2024-01-19 13:22:31 +01:00
axolotle
94d16d6d76 configpanels: avoid settings being None when empty 2024-01-19 13:20:45 +01:00
Alexandre Aubin
13638b0aa8 Oopsies 2024-01-14 19:54:09 +01:00
Alexandre Aubin
d41d6549eb legacy: drop autopatching of PHP versions, nowadays Sury is here by default and offers more flexibility over which PHP versions may be installed e.g. even 5.6 can still be installed on Bookworm, but anyway only a ~handful of apps are still using 7.2 or lower 2024-01-14 19:41:54 +01:00
Alexandre Aubin
99ac76e7d6 Drop legacy snippet from the 3.x era 2024-01-14 19:36:03 +01:00
Alexandre Aubin
388c30c40f Drop a bunch of legacy stuff from the 4.x era 2024-01-14 18:58:57 +01:00
Alexandre Aubin
862e66c17e regenconf/dovecot: make sure the home folder for vmail user is created to prevent a warning 2024-01-14 18:47:17 +01:00
Alexandre Aubin
c9f2bb7911 debian: force the dependency of resolvconf to be about the concrete package, not openresolv/systemd-resolved which apparently nowadays 'provide' resolvconf but with different stuff under the hood ~_~ 2024-01-14 18:19:30 +01:00
Alexandre Aubin
d3b50db951 Unused import 2024-01-14 17:53:57 +01:00
Alexandre Aubin
e2aaf72076 Portal theme is no more as well, now handled on a per-main-domain basis 2024-01-14 17:36:05 +01:00
Alexandre Aubin
b6c55c9aa6 SSOwat's tile overlay is no more 2024-01-14 17:25:41 +01:00
Alexandre Aubin
0dcecf5f47 Remove rspamd from recommends, replace with a global setting similar to pop3 to enable/disable antispam 2024-01-14 17:19:52 +01:00
Alexandre Aubin
1cbc30e0d5 So the reason the 'enable pop3' thing is not working properly is because the config panel was spitting inconsistent boolean shit like 1, True or 'yes' depending on whatever... 2024-01-14 17:08:24 +01:00
Alexandre Aubin
5975dd4310 Merge remote-tracking branch 'origin/dev' into bookworm 2024-01-14 16:25:02 +01:00
Alexandre Aubin
3aa087ca04 debian/postinst: make sure /etc/yunohost/portal exists 2024-01-01 19:44:13 +01:00
Alexandre Aubin
0a01423408 Merge remote-tracking branch 'origin/dev' into bookworm 2023-12-28 02:57:12 +01:00
Alexandre Aubin
7eb1413d09 zgrmbl 2023-12-27 05:09:36 +01:00
Alexandre Aubin
570a22a31c Make linters happy 2023-12-27 04:28:08 +01:00
Alexandre Aubin
61551eb40d Fix tests in CI context / yunohost-api is down and it's expected 2023-12-27 04:24:37 +01:00
Alexandre Aubin
0856f27b46 Unused imports 2023-12-27 03:22:30 +01:00
Alexandre Aubin
665592374d user/password: move to passlib hash.sha512_crypt to generate password hashes to replace deprecated crypt lib 2023-12-27 03:18:48 +01:00
Alexandre Aubin
f505efc8bb password: utils/password.py is not used by ssowat anymore 2023-12-27 02:41:52 +01:00
Alexandre Aubin
7f02fcd985 portalapi/sso: add a first bunch of unit tests 2023-12-27 02:41:24 +01:00
Alexandre Aubin
29cac1791d tests: fix hardcoded domain / typo x_x 2023-12-26 19:00:44 +01:00
Alexandre Aubin
3070e504ad Make rspamd an optional dependency, because rspamd is only necessary when you really care about incoming mail, is resource-heavy, and for some reason some setups cant install libhyperscan which is required by rspamd 2023-12-26 17:52:08 +01:00
Alexandre Aubin
351d9361fa sso: use PCRE regexes instead of LUA regex 2023-12-23 20:40:20 +01:00
Alexandre Aubin
7e7a6845d5 portal-api: improve handling of ldap interfaces 2023-12-22 01:11:49 +01:00
Alexandre Aubin
2be1dccb91 mypy doesn't like the function attribute trick 2023-12-22 00:34:15 +01:00
Alexandre Aubin
6022be5ff1 Prevent unecessary import resulting in catastrophies + lazy-load the session secrets 2023-12-22 00:25:08 +01:00
Alexandre Aubin
38b3cfddd8 quality: make linter gods happy 2023-12-22 00:00:49 +01:00
Alexandre Aubin
c19e2b7b19 auth/portal/acl: allow admins to log on any main domain 2023-12-21 23:52:04 +01:00
Alexandre Aubin
746433c5d1 Whoopsies 2023-12-21 19:48:57 +01:00
Alexandre Aubin
d0f1d9201c auth/portal/acl : add 'user is allowed for domain X' mechanism, such that users can't log in or add mail aliases for a domain they aint allowed to access. The fact that they are able to access a domain is derived from the fact that they have access to at least one app on that domain (actually .. we may want to bypass this check for admins, otherwise this is gonna be hella confusing for fresh intalls). 2023-12-21 18:36:15 +01:00
Alexandre Aubin
20d914fd10 Update 'ssowat conf regenerated' message to reflect that SSO and portal are two slightly different thing 2023-12-21 17:28:38 +01:00
Alexandre Aubin
9a45a6ccf2 auth/quality: replace os.path stuff with Path from pathlib 2023-12-21 17:18:06 +01:00
Alexandre Aubin
c57a7a4cf0 Zrgrml 2023-12-19 20:08:41 +01:00
Alexandre Aubin
3922ba9c68 Implement similar cookie mechanism for admin api (compared to portal) with static secret (cookies aint invalidated by api restart) and rolling session validity 2023-12-19 20:01:40 +01:00
Alexandre Aubin
d1022b1a6c Merge remote-tracking branch 'origin/dev' into bookworm 2023-12-12 15:12:13 +01:00
Alexandre Aubin
05ea37d694 lazy-load crypt which is a deprecated lib to avoid having DepreciationWarnings all over the place 2023-12-12 15:11:32 +01:00
axolotle
1d734b5df5 form: fix SelectOption's choices coercing list to dict 2023-12-12 12:17:19 +01:00
Alexandre Aubin
9809de65f8 tests: fix permission tests 2023-12-08 09:10:03 +01:00
Alexandre Aubin
3765349436 tests: fix boring warning about domain_a.dev/domain_b.dev during permissions tests 2023-12-08 09:00:17 +01:00
Alexandre Aubin
fb52083b09 tests: more debug attempt + remove the boring 'other_domain' for many test where we don't really care about second domain yet this slow things stuff ... 2023-12-07 16:39:27 +01:00
Alexandre Aubin
8351d88da1 ci: yolotry to add more info to debug issue with sso check 2023-12-07 15:53:01 +01:00
axolotle
2db709e043 i18n: add domain configpanel help keys in expected keys + add/remove some keys 2023-12-07 14:28:08 +01:00
axolotle
544fe85773 i18n: rename pydantic errors + register in form file 2023-12-07 14:26:01 +01:00
axolotle
5563fd8ca0 tests:backuprestore: let hooks gracefully fake exec except 'restore' 2023-12-07 13:49:47 +01:00
axolotle
878944c7c1 form: fix pattern can be None 2023-12-02 17:24:42 +01:00
axolotle
dad366da61 Merge branch 'dev' into bookworm 2023-12-01 18:46:39 +01:00
axolotle
cdc703a48f form: add pydantic errors translations 2023-12-01 18:25:22 +01:00
axolotle
719ffe387e tests:options: add small test for pattern 2023-12-01 18:24:29 +01:00
axolotle
3f320a2358 portal:auth: samesite=None in dev mode 2023-11-30 14:04:13 +01:00
axolotle
246c513796 portal:auth: remove touch in delete_session_cookie 2023-11-29 14:32:11 +01:00
axolotle
6f9203c98c form:FileOption: fix cli file check 2023-11-29 14:09:36 +01:00
axolotle
c5758bcd30 tests:questions: reflect changes of patch_users 2023-11-29 14:08:42 +01:00
Alexandre Aubin
29a452c1d3 ci: warnings about 'not identify correctly the dns zone for domain' driving me crazy² 2023-11-29 00:28:17 +01:00
Alexandre Aubin
a7553501b0 ci: warnings about 'not identify correctly the dns zone for domain' driving me crazy 2023-11-29 00:26:19 +01:00
Alexandre Aubin
60b7f63afa ci: move lint stage as first stage, require invalidcode to pass before running builds 2023-11-29 00:11:33 +01:00
Alexandre Aubin
35c809a726 ci: simplify 'test actionsmap' job, move it to linting 2023-11-28 23:58:50 +01:00
Alexandre Aubin
a0d6c9a032 ci: remove tmp profiling stuff 2023-11-28 23:52:29 +01:00
Alexandre Aubin
e7379a7ec3 ci: whoopsies stages must be declared in the other file 2023-11-28 23:37:40 +01:00
Alexandre Aubin
aaaea0dce2 ci: rework the translation+doc stages, have a 'bot' stage for everything that is likely to create a PR instead 2023-11-28 23:30:25 +01:00
Alexandre Aubin
0eccb7e46b ci: disable 'full-test' which is a huge pain, always run every 'test' jobs instead 2023-11-28 23:20:08 +01:00
Alexandre Aubin
16391d7374 perf: lazyloading settings_get + pass all the global settings as an env variable to the regenconf to prevent having to 'yunohost settings get' like ten times ... reduces the regenconf runtime from 35s to 23s on my machine 2023-11-28 21:12:03 +01:00
Alexandre Aubin
9a4b0e422d quality: unused import, missing import 2023-11-28 20:19:06 +01:00
Alexandre Aubin
24741de4d9 portalapi: turns out ssowat (as www-data) needs to be able to validate that the session file exists 2023-11-28 19:15:33 +01:00
Alexandre Aubin
d7855fc9a7 portalapi: propagate changes to fail2ban config 2023-11-28 18:40:49 +01:00
Alexandre Aubin
356c081a4f portalapi: implement a proper expiration/prolong mechanism for session cookies 2023-11-28 18:40:49 +01:00
axolotle
213d6416b6 test:permissions: update sso url and credentials structure 2023-11-28 16:26:44 +01:00
axolotle
2459834c1c tests:permissions: add an admin user 2023-11-28 16:10:25 +01:00
axolotle
c80feaeed3 tests: show_tile + label no longer in ssowat conf file 2023-11-28 15:46:41 +01:00
Alexandre Aubin
ed7c626c28
Merge pull request #1744 from YunoHost/lazyloadconfigpanel
perf: improve perf for a bunch of operations by lazy import + lazy define of config-panel related stuff
2023-11-28 14:46:16 +01:00
axolotle
5b5527a279 app:config: fix indentation for file content 2023-11-28 14:41:38 +01:00
Alexandre Aubin
3dda3bc4d5 perf: improve perf for a bunch of operations by lazy import + lazy define of config-panel related stuff 2023-11-28 14:31:38 +01:00
axolotle
650c0136f2 form:UserOption: merge choices and default into root validator with first admin user as default 2023-11-26 18:29:43 +01:00
axolotle
4a270b88b6 quality: fix lint + some formatting 2023-11-26 17:42:48 +01:00
axolotle
45b36dae05 domain: reset portal_logo value even if file has already been removed 2023-11-26 17:12:58 +01:00
axolotle
d28b6e96c8 configpanel: raise when unknown filter key 2023-11-26 17:01:38 +01:00
axolotle
25e23ce963 portal: reflect changes of splitting user form into info and password 2023-11-25 21:39:31 +01:00
axolotle
c8d0990e7b fix: pop 'portal_logo' when resaving settings 2023-11-25 21:38:33 +01:00
axolotle
e5a593a4bb fix: FileOption return already saved and hashed filepath 2023-11-25 21:37:29 +01:00
Alexandre Aubin
321aea4171 yolo: try to profile what's taking up so much time in tests... 2023-11-25 02:03:42 +01:00
Alexandre Aubin
8293388975 Merge remote-tracking branch 'origin/dev' into bookworm 2023-11-24 22:52:06 +01:00
Alexandre Aubin
c216a3f246 Merge remote-tracking branch 'origin/dev' into bookworm 2023-11-24 22:35:25 +01:00
axolotle
7e6fea440e quality: fix FileOption validator typing 2023-11-24 16:23:04 +01:00
Alexandre Aubin
b7882c9323 quality: fix obvious linter issues 2023-11-24 16:12:54 +01:00
axolotle
91f4e5fa42 domainconfig: add portal custom intros 2023-11-23 15:58:23 +01:00
axolotle
0095549b18 i18n: add domain config portal translations 2023-11-23 15:57:26 +01:00
axolotle
b6f7967758 domain:config: handle "portal_logo" with "python" mode and save content in portal settings 2023-11-17 17:30:01 +01:00
axolotle
2b75c8f036 form:FileOption: add file "accept" validation handling 2023-11-17 17:28:25 +01:00
axolotle
4dc9d35304 form:FileOption: add mode "bash" versus "python" validators 2023-11-17 17:27:24 +01:00
axolotle
64e2c3177c form: add "mode" to BaseOption to distinguish "bash" and "python" serialization 2023-11-17 17:21:34 +01:00
Alexandre Aubin
566f5d29a7 domain/portal: try to re-implement portal logo 2023-11-13 19:02:25 +01:00
axolotle
90aa6b86f1 form:BaseChoicesOption: do not declare choices to allow other related fields to come first in subclasses 2023-11-13 16:51:25 +01:00
axolotle
d7166bf77f portal: more explicit errors with path 2023-11-13 16:48:56 +01:00
Alexandre Aubin
587d729d60 portalapi: tweak ldap management to handle anonymous queries, eg to fetch domain list as ynh-portal 2023-11-13 15:39:38 +01:00
Alexandre Aubin
7fe950d11e Merge remote-tracking branch 'origin/dev' into bookworm 2023-11-12 16:45:41 +01:00
axolotle
d36ca72887 fix: some test apps has empty string domain 2023-11-09 17:40:04 +01:00
axolotle
5e81579c31 form: TagOption: add icon prop 2023-11-09 17:32:02 +01:00
axolotle
03fc739b3c domain: add _get_raw_domain_settings to read settings directly + use it in app_info to avoid infinite recursion 2023-11-09 15:49:19 +01:00
axolotle
5a655ba8cf domain:configpanel: update recovery_password check to new configpanel syntax 2023-11-09 14:56:01 +01:00
axolotle
ec2ffe6813 configpanel: remove unused arg 'config' from _get_raw_settings() 2023-11-09 14:52:58 +01:00
axolotle
6bcc3dd1c0 typing: fix logger typing + ignore weird constructors 2023-10-31 15:07:03 +01:00
axolotle
a924379774 Merge remote-tracking branch 'origin/dev' into bookworm 2023-10-31 03:11:49 +01:00
axolotle
004f44ed2d configpanel: add bind prop to SectionModel + PanelModel 2023-10-31 03:08:53 +01:00
axolotle
bfba939927 Merge remote-tracking branch 'origin/dev' into bookworm 2023-10-31 02:57:26 +01:00
Alexandre Aubin
5a3fe5460b quality: unused import 2023-10-30 19:06:44 +01:00
Alexandre Aubin
1acaf2af2e
Merge pull request #1653 from YunoHost/options-doc
ConfigPanel and app install form Options documentation
2023-10-30 19:05:56 +01:00
Alexandre Aubin
0957d31c14 ci/doc: add call to new configpanel/form doc generator, similar to what's done for helpers and app resources 2023-10-30 18:50:48 +01:00
Alexandre Aubin
f02538cef0 doc: iterate on configpanel/form documentation 2023-10-30 18:39:31 +01:00
Alexandre Aubin
8aee337d0f regenconf/portal: fix attempt to chown before the user is created 2023-10-30 17:04:58 +01:00
Alexandre Aubin
093c707eb6
Merge pull request #1677 from YunoHost/pydantic
ConfigPanel: switch to pydantic 3/3
2023-10-30 15:19:17 +01:00
axolotle
9423168aaf configpanels: fix app is_default + dns alert style 2023-10-30 15:17:01 +01:00
Alexandre Aubin
e4182bb362 debian: require moulinette, ssowat, yunohost-portal to be >= 12.0 2023-10-30 15:12:51 +01:00
Alexandre Aubin
418df4c05f debian: move yunohost-portal to 'Recommends' ... mainly to bypass issue on the CI, but also because it sounds legit ... not 100% about this ? 2023-10-30 15:12:51 +01:00
Alexandre Aubin
7c89c2c995
Merge pull request #1721 from YunoHost/fix-dump_script_log_extract_for_debugging
Fix dump script log extract for debugging
2023-10-30 14:01:31 +01:00
Kay0u
7f954af6b6 fix an error in dump_script_log_extract_for_debugging 2023-10-30 13:59:59 +01:00
axolotle
3faa574267 configpanel: add proper schema definition 2023-10-25 15:07:31 +02:00
axolotle
c4c79c61fe configpanel: forbid extra props on BaseOption + accordingly fix tests 2023-10-25 15:06:10 +02:00
axolotle
02619e8284 doc:config fix missing aleks additions 2023-10-24 15:05:26 +02:00
axolotle
b80868e967 doc:config: misc cosmetics fix 2023-10-24 14:06:16 +02:00
axolotle
ee72d2f463 doc:config: add ljf's advanced config panel doc 2023-10-23 19:05:23 +02:00
axolotle
d676348d35 doc: fix page routes and inconsistencies 2023-10-23 17:14:44 +02:00
axolotle
b3167ba2e8 doc:configpanel: add ConfigPanel doc with part of lfj comment in app example_ynh 2023-10-23 14:15:25 +02:00
axolotle
900f462791 doc:form: complete with ljf comments found in app example 2023-10-23 14:15:25 +02:00
axolotle
1221fd1458 doc:options: add documentation and generator for configpanel/manifest options 2023-10-23 14:15:25 +02:00
axolotle
9134515604 domain:config: make 'registrar' info a frozen input since an alert has no value 2023-10-22 17:53:50 +02:00
axolotle
3a31984e3c configpanel: allow other ConfigPanels to have no settings defined 2023-10-22 17:51:04 +02:00
axolotle
e7b43c763c configpanel: do not raise error if no settings file 2023-10-22 17:49:08 +02:00
axolotle
66cb855c0c domain: type fix 2023-10-22 17:47:43 +02:00
axolotle
3cae07970e form: remove no longer used hydrate_option_type method 2023-10-22 15:50:32 +02:00
axolotle
3a5d353c4b form: force option type to 'select' if there's 'choices' 2023-10-22 15:50:32 +02:00
axolotle
3f417bb9b3 tests: update error instance in tests to YunohostError for packaging errors 2023-10-22 15:50:32 +02:00
axolotle
ef860ee6ee form: default type to "select" if choices in option 2023-10-22 15:50:32 +02:00
axolotle
6953a8bf15 configpanel: quick fix option typing 2023-10-22 15:50:30 +02:00
axolotle
2f4c88ec55 form: parse pydantic error in logging 2023-10-22 15:49:29 +02:00
axolotle
51d302bf18 configpanel: is_action_section as attr 2023-10-22 15:49:29 +02:00
axolotle
d370cb0b24 configpanel: add value in options dict for config get --full 2023-10-22 15:49:29 +02:00
axolotle
48f882ecd3 form+configpanel: reflect Section optional value to all its Options 2023-10-22 15:49:29 +02:00
axolotle
fccb291d78 form: readd pattern to path 2023-10-22 15:49:29 +02:00
axolotle
bd9bf29a88 debian: add python3-pydantic + python3-email-validator dependencies 2023-10-22 15:49:27 +02:00
axolotle
b778aaf780 form: remove ChoosableOptions for now 2023-10-22 15:48:07 +02:00
axolotle
6bef4b1e0e app: remove call of 'domain_config_get' to avoid infinite recursion 2023-10-22 15:48:07 +02:00
axolotle
2a28e289ad form: rework 'hydrate_questions...' with a new 'parse_raw_options' that parse and validate options 2023-10-22 15:48:07 +02:00
axolotle
37b4eb956d typing: add missing type + misc typing fixes 2023-10-22 15:48:06 +02:00
axolotle
54cc23c90c config: update SettingsConfigPanel.reset 2023-10-22 15:46:57 +02:00
axolotle
b45515049d config: fix wrong diff logic on settings apply 2023-10-22 15:46:57 +02:00
axolotle
98ec5448f2 form: cli retries as variable to be patched in tests 2023-10-22 15:46:57 +02:00
axolotle
98d3b4ffc8 form: rework context/values/hooks in prompt_or_validate_form 2023-10-22 15:46:57 +02:00
axolotle
73b795be1d config: readd value has been initialized + change test removed value from settings since boolean has a native default value 2023-10-22 15:46:57 +02:00
axolotle
f087a6b967 config: normalize get option value 2023-10-22 15:46:57 +02:00
axolotle
25ccbd5f78 configpanel: quickly update list_actions 2023-10-22 15:46:57 +02:00
axolotle
15c827908f configpanel: update run_action 2023-10-22 15:46:57 +02:00
axolotle
6b3691ce53 configpanel: update set 2023-10-22 15:46:57 +02:00
axolotle
5f9ea58313 configpanel: update _apply 2023-10-22 15:46:55 +02:00
axolotle
7a60703ef5 configpanel: update _ask 2023-10-22 15:30:02 +02:00
axolotle
2c35dcbb24 configpanel: update _reload_services 2023-10-22 15:29:13 +02:00
axolotle
f1038de56d form: fix entities validators order for filter and apply the right default 2023-10-22 15:29:12 +02:00
axolotle
dbaea019fe form+config: replace _parse_pre_answered method with generic function 2023-10-22 15:28:14 +02:00
axolotle
a92e22b653 config: rework get method 2023-10-22 15:27:16 +02:00
axolotle
80dbd6dac4 form: rework entities validators to avoid multiple calls to them 2023-10-22 15:21:21 +02:00
axolotle
02948ad49c config: rework config+settings getter methods 2023-10-22 15:19:32 +02:00
axolotle
564a66de2f configpanel: add config panel models 2023-10-22 15:15:14 +02:00
axolotle
bec34b92b0 form: add reserved "id" validator 2023-10-22 15:13:13 +02:00
axolotle
774b11cbbe form: add legacy "name" attr 2023-10-22 15:13:13 +02:00
axolotle
a574855a03 form: fix forbidden readonly type 2023-10-22 15:13:13 +02:00
axolotle
582b1ed311 form: add translating method 2023-10-22 15:13:13 +02:00
axolotle
3ff6e6ed96 app: update app_install 2023-10-22 15:13:12 +02:00
axolotle
c428ba616a test:options: update tests results to pydantic parsing 2023-10-22 15:07:54 +02:00
axolotle
ec5da99a79 form: rework pre + post options validators 2023-10-22 15:07:54 +02:00
axolotle
3943774811 form: add dynamic annotation getters 2023-10-22 15:07:52 +02:00
axolotle
89ae5e654d form: update asking flow, separate form and options 2023-10-22 15:06:51 +02:00
axolotle
f5c56db10e form: use pydantic BaseModel in Options and add some validators 2023-10-22 15:02:10 +02:00
axolotle
5bd8680847 domain:config: restrict portal options to topest domains 2023-10-19 18:39:15 +02:00
axolotle
163dd4d359 domain:config: remove 'portal_logo' for now 2023-10-19 18:28:29 +02:00
axolotle
6f085ad255 conf_regen:yunohost: repeat init portal setup in post hook 2023-10-19 14:33:52 +02:00
axolotle
2b5726f4a8 portal: update settings reading from new config file 2023-10-18 18:36:07 +02:00
axolotle
9d21501648 domain:config: update portal option saving 2023-10-18 18:34:25 +02:00
axolotle
8d366e67b0 app_ssowatconf: generate per domain portal config with available apps 2023-10-18 18:29:55 +02:00
axolotle
827fbe337d conf_regen:yunohost: setup /etc/yunohost/portal 2023-10-18 18:21:03 +02:00
axolotle
c577125363 portal: temp disable 'show_other_domains_apps' settings due to missing domain info in ldap 2023-10-17 14:46:16 +02:00
axolotle
d65cca5ab1 portal: fix decode error 2023-10-17 14:15:59 +02:00
axolotle
089e0001c2 portal: retreive app permissions from ldap 2023-10-17 14:15:22 +02:00
Alexandre Aubin
a0ce7c2d28 ssowatconf: drop unused redirected_regex mechanism + we don't need the label+show_tile info in ssowat conf anymore 2023-10-07 17:40:06 +02:00
Alexandre Aubin
8036226935 Typo 2023-10-07 17:12:26 +02:00
Alexandre Aubin
fae3b676ea Getting crazy about the ssowat/nginx stupid issue ... 2023-10-06 17:22:01 +02:00
Alexandre Aubin
0548af0c25 ci: add git status to debug what commit exactly is used during builds ... 2023-10-06 14:11:38 +02:00
Alexandre Aubin
2fc2acea51 portalapi: misc fixes related to logging, edgecases 2023-10-06 14:11:38 +02:00
Alexandre Aubin
385c131d0c regenconf: fix dummy warning 2023-09-29 16:53:18 +02:00
Alexandre Aubin
814696e9c1 portal: redirect to $host/yunohost/admin by default (cf recent commit in ssowat) 2023-09-29 14:34:33 +02:00
Alexandre Aubin
127b6121d1 meh 2023-09-27 22:29:27 +02:00
Alexandre Aubin
d3418479a2 fix remaining log.getActionLogger 2023-09-27 22:25:44 +02:00
Alexandre Aubin
f617b97d80
portal/ssowat: fix conf initialization 2023-09-27 22:08:26 +02:00
Alexandre Aubin
b61a16421b portal-api: fix cookie secret initialization 2023-09-27 20:51:45 +02:00
Alexandre Aubin
a0dbf6a5b0 portal: improve domain setting fetch + set show_other_domains_apps to false by default ? 2023-09-27 20:40:50 +02:00
Alexandre Aubin
a4366e88fc
Merge pull request #1387 from YunoHost/portal-api
WIP : New portal API to partially replace SSOwat
2023-09-27 19:33:07 +02:00
Alexandre Aubin
9e87ea88df portal-api: improve semantic for yunohost public portal stuff 2023-09-27 19:30:55 +02:00
Alexandre Aubin
db30b3acb8
Merge branch 'bookworm' into portal-api 2023-09-27 18:57:02 +02:00
Alexandre Aubin
883bb2b498 Merge remote-tracking branch 'origin/dev' into bookworm 2023-09-27 17:56:48 +02:00
Alexandre Aubin
8a72bac884
Merge pull request #1692 from YunoHost/logging-is-a-mess
Moulinette logging is an unecessarily complex mess, episode 57682
2023-09-27 17:45:21 +02:00
Kay0u
ea981ee4a6
Merge branch 'bookworm' of github.com:YunoHost/yunohost into bookworm 2023-09-08 23:50:37 +02:00
Alexandre Aubin
a16f54ea96
Merge pull request #1711 from YunoHost/update-fail2ban-jail-conf
Update fail2ban jail conf
2023-09-08 23:50:34 +02:00
Kay0u
142fad4b78
typo 2023-09-08 23:50:30 +02:00
Kay0u
aed8ecb645
do not skip tests from 11.2 2023-09-08 23:47:57 +02:00
Kay0u
e77e9a0a9a
backup/restore tests from 11.2 2023-09-08 23:13:38 +02:00
Kayou
2bd3dd2bba
set maxretry to 10 2023-09-08 22:31:08 +02:00
Kay0u
d0b65d5661
revert important variables in fail2ban jail.conf 2023-09-08 15:17:25 +02:00
Kay0u
8eb2e72282
Update Fail2ban jail.conf file from https://sources.debian.org/src/fail2ban/1.0.2-2/config/jail.conf/ 2023-09-08 15:13:20 +02:00
axolotle
c641f099c5 add temp messy file handling for portal custom logo 2023-09-07 17:57:08 +02:00
axolotle
bfedf144b3 add settings getter + /public route to get settings and public apps 2023-09-04 16:31:58 +02:00
axolotle
2136db32b6 return domain from _get_user_infos 2023-09-04 16:27:06 +02:00
axolotle
20d21b57e0 wip: save portal configpanel options in separate file .portal.yml 2023-09-04 16:24:01 +02:00
axolotle
a1a47e5221 update config_domain.toml with portal options 2023-09-04 16:21:50 +02:00
axolotle
5562b61db0 add 'list_portal' AppOption modifier to add portal as a possible choice 2023-09-04 16:20:29 +02:00
axolotle
0645d18e67 add host as session cookie info 2023-09-04 16:19:07 +02:00
axolotle
8f0f85b722 merge update_password with update 2023-08-29 16:28:32 +02:00
Alexandre Aubin
a7580df7a9
Merge pull request #1701 from selfhoster1312/sso-csp-inline
Allow inline scripts for yunohost-portal (nginx CSP)
2023-08-15 14:18:45 +02:00
selfhoster1312
26d4d9420c Allow inline scripts for yunohost-portal (nginx CSP) 2023-08-15 14:12:08 +02:00
Alexandre Aubin
741caebdda
Merge pull request #1700 from selfhoster1312/decode-jwt
Serialize the JWT token to a cookie string instead of failing
2023-08-15 12:27:16 +02:00
selfhoster1312
101b5704c4 Serialize the JWT token to a cookie string instead of failing 2023-08-15 12:23:56 +02:00
Alexandre Aubin
dafae50e8a
Merge pull request #1698 from selfhoster1312/portal-api
Handle both cookies in the same way (please let me logout)
2023-08-13 23:42:19 +02:00
selfhoster1312
6f8b3fd57f Handle both cookies in the same way (please let me logout) 2023-08-13 23:11:31 +02:00
axolotle
db1670ca5d add temp portal_update_password 2023-08-01 16:28:25 +02:00
axolotle
c9092b2aad add portal_update to update user infos 2023-08-01 15:43:39 +02:00
axolotle
c3a4b7dabb add _get_user_infos helper 2023-08-01 15:18:48 +02:00
axolotle
ca6eb2cbaf lint 2023-08-01 15:15:52 +02:00
Alexandre Aubin
afd7b37ebc Tweak nginx portal conf to serve html/css/js/assets from /usr/share/yunohost/portal, similar to webadmin 2023-07-30 23:53:43 +02:00
Alexandre Aubin
5fd1850f19 Add dependency to new yunohost-portal debian package 2023-07-30 23:53:04 +02:00
Alexandre Aubin
09c5a4cfb9 admin and portalapi: propagate new configurable CORS mechanism from moulinette 2023-07-29 19:15:30 +02:00
Alexandre Aubin
704e42a6af portalapi: fix cookie not being deleted because maxage=-1 or something 2023-07-29 19:13:00 +02:00
Tagada
4385c886a4 Merge branch 'dev' into bookworm 2023-07-20 15:49:45 +02:00
Alexandre Aubin
2ece3b65f6 Moulinette logging is an unecessarily complex mess, episode 57682 2023-07-18 00:19:16 +02:00
Alexandre Aubin
ae37b5fc24 portalapi: Add new yunohost-portal-api to yunohost services 2023-07-17 19:47:24 +02:00
Alexandre Aubin
f69f87fa65 Merge remote-tracking branch 'origin/dev' into portal-api 2023-07-17 18:56:05 +02:00
Alexandre Aubin
4561f900df portal refactoring: update ssowat conf format with a dict mapping domains to portal urls. For now, let's have one portal per main/parent domain (which is anyway imposed by cookie management unless we reintroduce complex cross-domain authentication...) 2023-07-15 21:20:15 +02:00
Alexandre Aubin
ec96558c81 portalapi: add FIXMEs about auth layer 2023-07-15 20:07:18 +02:00
Alexandre Aubin
f4dfb56006 portal refactoring: the 'yunohost tile' thingy won't work anymore, gotta discuss what we want to do exactly 2023-07-15 16:01:03 +02:00
Alexandre Aubin
0cb673c125 portalapi: woopsies 2023-07-14 19:35:05 +02:00
Alexandre Aubin
5104c2a79f portalapi: add CORS headers ... though gotta revisit this later, I don't know what I'm doing 2023-07-14 19:11:32 +02:00
Alexandre Aubin
5e1d69a2cb portalapi: harden systemd service configuration 2023-07-14 18:55:33 +02:00
Alexandre Aubin
2c0f49cef3 portalapi: add groups and apps list in infos returned by GET /me 2023-07-14 04:44:03 +02:00
Alexandre Aubin
6c6dd318fb portalapi: implement encrypted password storage in the user's cookie using AES256 2023-07-11 22:39:22 +02:00
Alexandre Aubin
236e85eece apt: add signed-by clause to sury and yarn repo 2023-07-11 18:12:34 +02:00
Alexandre Aubin
f1200b81dc apt: always add yarn repo because it's annoying to have to deal with an extra repo in each nodejs app just to install a single package.. 2023-07-11 18:12:34 +02:00
Alexandre Aubin
9a5080ea16 portalapi: fix split or user/password in auth code 2023-07-11 17:49:25 +02:00
Alexandre Aubin
a1cf770e1b Merge branch 'dev' into portal-api 2023-07-11 17:11:02 +02:00
Alexandre Aubin
2f2ff6eb19
Simplify fpm add config helper (Bookworm) (#1685)
* Simplify ynh_add_fpm_config helper

* helpers: drop dedicated_service option in ynh_add_fpm_config
2023-07-11 15:58:59 +02:00
Alexandre Aubin
7ba6c37eb8 Merge branch '11.2' into bookworm 2023-07-11 15:56:52 +02:00
Alexandre Aubin
cab7667dcc misc: more boring irrelevant postgresql warnings to filter out 2023-07-04 19:48:55 +02:00
Alexandre Aubin
bdc296f858 Merge remote-tracking branch 'origin/dev' into bookworm 2023-07-04 18:10:25 +02:00
Alexandre Aubin
8a865daddd apps: add YNH_DEFAULT_PHP_VERSION in app's dict as a boring workaround/fix for apps using YNH_DEFAULT_PHP_VERSION in _common.sh *before* sourcing helpers ... 2023-06-19 16:04:31 +02:00
Alexandre Aubin
ced6d5c975 apps: fix version.parse now refusing to parse legacy version numbers 2023-06-18 15:48:14 +02:00
Alexandre Aubin
a673b3ed42 Postgresql is now version 15 2023-06-14 10:28:58 +02:00
Alexandre Aubin
8ac48ee09e Drop deprecated firstname/lastname in user_create/update + also drop old deprecated cert- commands 2023-06-14 08:04:16 +02:00
Alexandre Aubin
f6ab380730 helpers/php: Default PHP version in bookworm is now 8.2 2023-06-14 08:02:35 +02:00
Alexandre Aubin
efe3ace98d
Merge pull request #1619 from YunoHost/legacy_cleanup
WIP : Legacy cleanup (meant for Bookworm)
2023-06-13 21:36:37 +02:00
Alexandre Aubin
81b96ad6d4 tests: tmp tweaks to adapt for removed deprecated features 2023-06-13 21:30:20 +02:00
Alexandre Aubin
b4dcd0fb22 Merge branch 'bookworm' into legacy_cleanup 2023-06-13 21:30:01 +02:00
Alexandre Aubin
b6ae711dd7
Merge pull request #1666 from eldertek/fix-2090
[fix] Remove deprecated domain_dns_conf
2023-06-13 20:43:35 +02:00
Alexandre Aubin
194eb9c6c7 conf: Update ciphers for nginx, postfix, dovecot 2023-06-13 20:39:56 +02:00
Alexandre Aubin
c4c353843c Unused vars, unhappy linter gods 2023-06-13 14:57:55 +02:00
Alexandre Aubin
8728b2030c Remove migrations/0027_migrate_to_bookworm for now because it's triggering errors on the CI, at least half of it should be reworked, and it should be in a separated PR to target dev(=bullseye) 2023-06-13 14:57:55 +02:00
Alexandre Aubin
d1d6da8fcb Merge branch 'dev' into bookworm 2023-06-13 14:57:44 +02:00
Tagada
5a2570a5d6 Merge branch 'dev' into bookworm 2023-06-11 00:00:38 +02:00
Kayou
78cd79ec48
Update debian/changelog 2023-06-05 10:11:50 +02:00
ElderTek
23eaf609da remove deprecated 2023-05-25 00:00:07 +04:00
Alexandre Aubin
c319303612 Merge remote-tracking branch 'origin/dev' into bookworm 2023-05-22 20:58:20 +02:00
Kay0u
85b08e44c9
ci: preinstall more package 2023-05-22 15:29:21 +02:00
Alexandre Aubin
3b75485923 tests: somehow using 'Domain' as http header aint supported anymore, gotta use Host 2023-05-16 15:21:27 +02:00
Alexandre Aubin
5b9721eb23 tests: fix bad regex permission test because python3.11 now accepts ++ quantifier, so change the 'bad regex' trick 2023-05-16 15:17:03 +02:00
Alexandre Aubin
5564f7dc12 tests: fix remaining funky mocker.spy 2023-05-16 15:11:51 +02:00
Kay0u
734db1994c
ci: don't install any package with pip, it's supposed to be preinstalled 2023-05-16 11:30:56 +02:00
Alexandre Aubin
bed9ecc09e py39->py311 in tox 2023-05-15 22:12:55 +02:00
Alexandre Aubin
1af88b0c55 ci: force tox install during lint tasks 2023-05-15 19:06:32 +02:00
Alexandre Aubin
031c641b77 tests: fix spy on m18n.n which in some cases doesnt work anymore ... not sure why, bit confused ... 2023-05-15 18:51:18 +02:00
Alexandre Aubin
482f8f3443 backup: fix again backup hook for xmpp when data folder dont exist 2023-05-15 17:37:11 +02:00
Alexandre Aubin
774ac64770 Merge remote-tracking branch 'origin/dev' into bookworm 2023-05-15 17:26:17 +02:00
Alexandre Aubin
d0838cbe58 Merge remote-tracking branch 'origin/dev' into bookworm 2023-05-13 19:20:33 +02:00
Tagadda
691ce5eace fix: python3.11 now supports Possessive Quantifiers regex 2023-05-13 14:37:32 +00:00
Tagadda
f617287eb2 woops thanks codeql i guess 2023-05-05 23:58:30 +00:00
Tagadda
2dbe34c030 aaand mariadb-server ofc... 2023-05-05 23:44:46 +00:00
Tagadda
de3dd9436c create migration : s/bullseye/bookworm
s/buster/bullseye
yolo, needs some more cleanup
2023-05-05 23:40:17 +00:00
Tagadda
ea7bdb62ed mariadb-client needed by tests/test_app_resources.py 2023-05-05 22:24:43 +00:00
Tagadda
1135cf1b62 php-cli is needed for ynhtest_config.sh 2023-05-05 21:23:23 +00:00
Tagadda
8fb225f3ad remove some legacy pre-bullseye workarounds 2023-05-05 19:16:21 +00:00
Alexandre Aubin
0901298935 bookworm: add php7.x -> php8.2 autopatch 2023-05-05 20:18:19 +02:00
Alexandre Aubin
7fc7d188ad Fix XMPP stuff that may not exists 2023-05-05 20:11:16 +02:00
Alexandre Aubin
1fb3965e51 improve previous commit, use ynh_apt to have non-interactive apt etc 2023-05-05 20:10:52 +02:00
Alexandre Aubin
191520ebdd mysql: dirty/ugly patch to autoinstall mariadb-server when calling ynhmysql_setup_db when it's not installed 2023-05-05 19:22:37 +02:00
Kayou
30bd0e05b2
Update test.gitlab-ci.yml
oopsie
2023-05-05 16:38:53 +02:00
Kayou
859f9c05a5
Update test.gitlab-ci.yml
fix tests for bookworm, don't try this at home²
2023-05-05 16:37:33 +02:00
Alexandre Aubin
224f1b1730 firewall: fix upnpc.discover() behavior that somehow now trigger an exception when cant talk to upnp device 2023-05-05 00:09:09 +02:00
Alexandre Aubin
37eac5e121 Update changelog for 12.0.0 2023-05-04 20:31:14 +02:00
Alexandre Aubin
0ab7c952f1 bookworm/debian: adapt control file to bookworm 2023-05-04 20:11:10 +02:00
Alexandre Aubin
04eadd715c Kill old --package option and .ini for PHP FPM config 2023-02-28 18:55:47 +01:00
Alexandre Aubin
22681a4f24 Kill the old 'unprotected/protected/skipped' setting hell 2023-02-28 18:50:31 +01:00
Alexandre Aubin
58ffff556c Merge remote-tracking branch 'origin/dev' into portal-api 2022-08-09 18:22:32 +02:00
Alexandre Aubin
76eba6fc88 Fix log permission issue for yunohost-portal-api 2021-12-27 13:05:11 +01:00
Alexandre Aubin
bd564e6a53 Add systemd conf for new service yunohost-portal-api 2021-12-27 12:44:20 +01:00
Alexandre Aubin
45baaead36 Fix typo + unused import 2021-12-26 18:22:33 +01:00
Alexandre Aubin
62808152ee Cookie handling for the new portal API 2021-12-26 16:52:48 +01:00
Alexandre Aubin
1efb50c7ab Iterate on new portal API design: nginx config, cookie format, be able to open a non-root ldap session, 2021-12-25 15:44:14 +01:00
Alexandre Aubin
c01042b51d Merge remote-tracking branch 'origin/moar_session_management_changes' into portal-api 2021-12-25 15:42:02 +01:00
Alexandre Aubin
2845914d44 WIP: foundation for a new portal API to partially replace SSOwat 2021-12-04 03:27:23 +01:00
134 changed files with 6233 additions and 6024 deletions

View file

@ -1,2 +1,7 @@
[coverage:run]
relative_files = True
source = src/
branch = True
[report]
omit=src/tests/*,src/vendor/*,/usr/lib/moulinette/yunohost/*,/usr/lib/python3/dist-packages/yunohost/tests/*,/usr/lib/python3/dist-packages/yunohost/vendor/*

View file

@ -1,11 +1,10 @@
---
stages:
- lint
- build
- install
- test
- lint
- doc
- translation
- bot
default:
tags:
@ -47,7 +46,7 @@ workflow:
variables:
GIT_CLONE_PATH: '$CI_BUILDS_DIR/$CI_COMMIT_SHA/$CI_JOB_ID'
YNH_SOURCE: "https://github.com/yunohost"
YNH_DEBIAN: "bullseye"
YNH_DEBIAN: "bookworm"
YNH_SKIP_DIAGNOSIS_DURING_UPGRADE: "true"
include:

View file

@ -0,0 +1,53 @@
generate-helpers-doc:
stage: bot
image: "build-and-lint"
needs: []
before_script:
- git config --global user.email "yunohost@yunohost.org"
- git config --global user.name "$GITHUB_USER"
script:
- cd doc
- python3 generate_helper_doc.py 2
- python3 generate_helper_doc.py 2.1
- python3 generate_resource_doc.py > resources.md
- python3 generate_configpanel_and_formoptions_doc.py > forms.md
- hub clone https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/doc.git doc_repo
- cp helpers.v2.md doc_repo/pages/06.contribute/10.packaging_apps/20.scripts/10.helpers/packaging_app_scripts_helpers.md
- cp helpers.v2.1.md doc_repo/pages/06.contribute/10.packaging_apps/20.scripts/12.helpers21/packaging_app_scripts_helpers_v21.md
- cp resources.md doc_repo/pages/06.contribute/10.packaging_apps/10.manifest/10.appresources/packaging_app_manifest_resources.md
- cp forms doc_repo/pages/06.contribute/15.dev/03.forms/forms.md
- cd doc_repo
# replace ${CI_COMMIT_REF_NAME} with ${CI_COMMIT_TAG} ?
- hub checkout -b "${CI_COMMIT_REF_NAME}"
- hub commit -am "[CI] Update app helpers/resources for ${CI_COMMIT_REF_NAME}"
- hub pull-request -m "[CI] Update app helpers/resources for ${CI_COMMIT_REF_NAME}" -p # GITHUB_USER and GITHUB_TOKEN registered here https://gitlab.com/yunohost/yunohost/-/settings/ci_cd
artifacts:
paths:
- doc/helpers.md
- doc/resources.md
only:
- tags
autofix-translated-strings:
stage: bot
image: "build-and-lint"
needs: []
before_script:
- git config --global user.email "yunohost@yunohost.org"
- git config --global user.name "$GITHUB_USER"
- hub clone --branch ${CI_COMMIT_REF_NAME} "https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/yunohost.git" github_repo
- cd github_repo
script:
# create a local branch that will overwrite distant one
- git checkout -b "ci-autofix-translated-strings-${CI_COMMIT_REF_NAME}" --no-track
- python3 maintenance/missing_i18n_keys.py --fix
- python3 maintenance/autofix_locale_format.py
- '[ $(git diff --ignore-blank-lines --ignore-all-space --ignore-space-at-eol --ignore-cr-at-eol | wc -l) != 0 ] || exit 0' # stop if there is nothing to commit
- git commit -am "[CI] Reformat / remove stale translated strings" || true
- git push -f origin "ci-autofix-translated-strings-${CI_COMMIT_REF_NAME}":"ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}"
- hub pull-request -m "[CI] Reformat / remove stale translated strings" -b Yunohost:$CI_COMMIT_REF_NAME -p || true # GITHUB_USER and GITHUB_TOKEN registered here https://gitlab.com/yunohost/yunohost/-/settings/ci_cd
only:
variables:
- $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
changes:
- locales/*

View file

@ -1,9 +1,14 @@
.build-stage:
stage: build
needs:
- job: actionsmap
- job: invalidcode311
image: "build-and-lint"
variables:
YNH_BUILD_DIR: "$GIT_CLONE_PATH/build"
before_script:
- echo $PWD
- echo $CI_PROJECT_DIR
- mkdir -p $YNH_BUILD_DIR
artifacts:
paths:
@ -31,7 +36,7 @@ build-yunohost:
- mkdir -p $YNH_BUILD_DIR/$PACKAGE
- cat archive.tar.gz | tar -xz -C $YNH_BUILD_DIR/$PACKAGE
- rm archive.tar.gz
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE || { apt-get update && DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE; }
- *build_script
build-ssowat:
@ -40,7 +45,7 @@ build-ssowat:
PACKAGE: "ssowat"
script:
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $YNH_DEBIAN $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE || { apt-get update && DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE; }
- *build_script
build-moulinette:
@ -49,5 +54,5 @@ build-moulinette:
PACKAGE: "moulinette"
script:
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $YNH_DEBIAN $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE || { apt-get update && DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE; }
- *build_script

View file

@ -1,31 +0,0 @@
########################################
# DOC
########################################
generate-helpers-doc:
stage: doc
image: "build-and-lint"
needs: []
before_script:
- git config --global user.email "yunohost@yunohost.org"
- git config --global user.name "$GITHUB_USER"
script:
- cd doc
- python3 generate_helper_doc.py 2
- python3 generate_helper_doc.py 2.1
- python3 generate_resource_doc.py > resources.md
- hub clone https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/doc.git doc_repo
- cp helpers.v2.md doc_repo/pages/06.contribute/10.packaging_apps/20.scripts/10.helpers/packaging_app_scripts_helpers.md
- cp helpers.v2.1.md doc_repo/pages/06.contribute/10.packaging_apps/20.scripts/12.helpers21/packaging_app_scripts_helpers_v21.md
- cp resources.md doc_repo/pages/06.contribute/10.packaging_apps/10.manifest/10.appresources/packaging_app_manifest_resources.md
- cd doc_repo
# replace ${CI_COMMIT_REF_NAME} with ${CI_COMMIT_TAG} ?
- hub checkout -b "${CI_COMMIT_REF_NAME}"
- hub commit -am "[CI] Update app helpers/resources for ${CI_COMMIT_REF_NAME}"
- hub pull-request -m "[CI] Update app helpers/resources for ${CI_COMMIT_REF_NAME}" -p # GITHUB_USER and GITHUB_TOKEN registered here https://gitlab.com/yunohost/yunohost/-/settings/ci_cd
artifacts:
paths:
- doc/helpers.md
- doc/resources.md
only:
- tags

View file

@ -3,24 +3,39 @@
########################################
# later we must fix lint and format-check jobs and remove "allow_failure"
lint39:
actionsmap:
stage: lint
image: "build-and-lint"
needs: []
script:
- python3 -c 'import yaml; yaml.safe_load(open("share/actionsmap.yml"))'
- python3 -c 'import yaml; yaml.safe_load(open("share/actionsmap-portal.yml"))'
lint311:
stage: lint
image: "build-and-lint"
needs: []
allow_failure: true
script:
- tox -e py39-lint
- tox -e py311-lint
invalidcode39:
invalidcode311:
stage: lint
image: "build-and-lint"
needs: []
script:
- tox -e py39-invalidcode
- tox -e py311-invalidcode
mypy:
stage: lint
image: "build-and-lint"
needs: []
script:
- tox -e py39-mypy
- tox -e py311-mypy
i18n-keys:
stage: lint
image: "build-and-lint"
needs: []
script:
- python3 maintenance/missing_i18n_keys.py --check

View file

@ -5,13 +5,11 @@
stage: test
image: "core-tests"
variables:
PYTEST_ADDOPTS: "--color=yes"
PYTEST_ADDOPTS: "--color=yes --cov=src"
COVERAGE_FILE: ".coverage_$CI_JOB_NAME"
before_script:
- *install_debs
cache:
paths:
- src/tests/apps
key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG"
- ln -s src yunohost
needs:
- job: build-yunohost
artifacts: true
@ -20,42 +18,14 @@
- job: build-moulinette
artifacts: true
- job: upgrade
artifacts:
paths:
- ./.coverage_*
########################################
# TESTS
########################################
full-tests:
stage: test
image: "before-install"
variables:
PYTEST_ADDOPTS: "--color=yes"
before_script:
- *install_debs
- pip install mock pip pyOpenSSL pytest pytest-cov pytest-mock pytest-sugar requests-mock "packaging<22"
- yunohost tools postinstall -d domain.tld -u syssa -F 'Syssa Mine' -p the_password --ignore-dyndns --force-diskspace
script:
- python3 -m pytest --cov=yunohost tests/ src/tests/ --junitxml=report.xml
needs:
- job: build-yunohost
artifacts: true
- job: build-ssowat
artifacts: true
- job: build-moulinette
artifacts: true
coverage: '/TOTAL.*\s+(\d+%)/'
artifacts:
reports:
junit: report.xml
test-actionmap:
extends: .test-stage
script:
- python3 -m pytest tests/test_actionmap.py
only:
changes:
- share/actionsmap.yml
test-helpers2:
extends: .test-stage
script:
@ -72,129 +42,130 @@ test-domains:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_domains.py
only:
changes:
- src/domain.py
test-dns:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_dns.py
only:
changes:
- src/dns.py
- src/utils/dns.py
test-apps:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_apps.py
only:
changes:
- src/app.py
test-appscatalog:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_app_catalog.py
only:
changes:
- src/app_calalog.py
test-appurl:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_appurl.py
only:
changes:
- src/app.py
test-questions:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_questions.py
only:
changes:
- src/utils/config.py
test-app-config:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_app_config.py
only:
changes:
- src/app.py
- src/utils/config.py
test-app-resources:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_app_resources.py
only:
changes:
- src/app.py
- src/utils/resources.py
test-changeurl:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_changeurl.py
only:
changes:
- src/app.py
test-backuprestore:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_backuprestore.py
only:
changes:
- src/backup.py
test-permission:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_permission.py
only:
changes:
- src/permission.py
test-settings:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_settings.py
only:
changes:
- src/settings.py
test-user-group:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_user-group.py
only:
changes:
- src/user.py
test-regenconf:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_regenconf.py
only:
changes:
- src/regenconf.py
test-service:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_service.py
only:
changes:
- src/service.py
test-ldapauth:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_ldapauth.py
only:
changes:
- src/authenticators/*.py
test-sso-and-portalapi:
extends: .test-stage
script:
- python3 -m pytest src/tests/test_sso_and_portalapi.py
########################################
# COVERAGE REPORT
########################################
coverage:
stage: test
image: "core-tests"
needs:
# Yeah ... gotta list all of those individually ... https://gitlab.com/gitlab-org/gitlab/-/issues/332326
- job: test-domains
artifacts: true
- job: test-dns
artifacts: true
- job: test-apps
artifacts: true
- job: test-appscatalog
artifacts: true
- job: test-appurl
artifacts: true
- job: test-questions
artifacts: true
- job: test-app-config
artifacts: true
- job: test-app-resources
artifacts: true
- job: test-changeurl
artifacts: true
- job: test-backuprestore
artifacts: true
- job: test-permission
artifacts: true
- job: test-settings
artifacts: true
- job: test-user-group
artifacts: true
- job: test-regenconf
artifacts: true
- job: test-service
artifacts: true
- job: test-ldapauth
artifacts: true
- job: test-sso-and-portalapi
artifacts: true
script:
- coverage combine ./.coverage_*
- coverage report

View file

@ -1,36 +0,0 @@
########################################
# TRANSLATION
########################################
test-i18n-keys:
stage: translation
script:
- python3 maintenance/missing_i18n_keys.py --check
only:
changes:
- locales/en.json
- src/*.py
- src/diagnosers/*.py
autofix-translated-strings:
stage: translation
image: "build-and-lint"
needs: []
before_script:
- git config --global user.email "yunohost@yunohost.org"
- git config --global user.name "$GITHUB_USER"
- hub clone --branch ${CI_COMMIT_REF_NAME} "https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/yunohost.git" github_repo
- cd github_repo
script:
# create a local branch that will overwrite distant one
- git checkout -b "ci-autofix-translated-strings-${CI_COMMIT_REF_NAME}" --no-track
- python3 maintenance/missing_i18n_keys.py --fix
- python3 maintenance/autofix_locale_format.py
- '[ $(git diff --ignore-blank-lines --ignore-all-space --ignore-space-at-eol --ignore-cr-at-eol | wc -l) != 0 ] || exit 0' # stop if there is nothing to commit
- git commit -am "[CI] Reformat / remove stale translated strings" || true
- git push -f origin "ci-autofix-translated-strings-${CI_COMMIT_REF_NAME}":"ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}"
- hub pull-request -m "[CI] Reformat / remove stale translated strings" -b Yunohost:$CI_COMMIT_REF_NAME -p || true # GITHUB_USER and GITHUB_TOKEN registered here https://gitlab.com/yunohost/yunohost/-/settings/ci_cd
only:
variables:
- $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
changes:
- locales/*

53
bin/yunohost-portal-api Executable file
View file

@ -0,0 +1,53 @@
#! /usr/bin/python3
# -*- coding: utf-8 -*-
import argparse
import yunohost
# Default server configuration
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 6788
def _parse_api_args():
"""Parse main arguments for the api"""
parser = argparse.ArgumentParser(
add_help=False,
description="Run the YunoHost API to manage your server.",
)
srv_group = parser.add_argument_group("server configuration")
srv_group.add_argument(
"-h",
"--host",
action="store",
default=DEFAULT_HOST,
help="Host to listen on (default: %s)" % DEFAULT_HOST,
)
srv_group.add_argument(
"-p",
"--port",
action="store",
default=DEFAULT_PORT,
type=int,
help="Port to listen on (default: %d)" % DEFAULT_PORT,
)
glob_group = parser.add_argument_group("global arguments")
glob_group.add_argument(
"--debug",
action="store_true",
default=False,
help="Set log level to DEBUG",
)
glob_group.add_argument(
"--help",
action="help",
help="Show this help message and exit",
)
return parser.parse_args()
if __name__ == "__main__":
opts = _parse_api_args()
# Run the server
yunohost.portalapi(debug=opts.debug, host=opts.host, port=opts.port)

View file

@ -132,12 +132,8 @@ def main() -> bool:
)
continue
# Only broadcast IPv4 because IPv6 is buggy ... because we ain't using python3-ifaddr >= 0.1.7
# Buster only ships 0.1.6
# Bullseye ships 0.1.7
# To be re-enabled once we're on bullseye...
# ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"]
ips: List[str] = interfaces[interface]["ipv4"]
# Broadcast IPv4 and IPv6
ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"]
# If at least one IP is listed
if not ips:

View file

@ -1,13 +1,9 @@
{% set interfaces_list = interfaces.split(' ') %}
{% for interface in interfaces_list %}
interface-name={{ domain }},{{ interface }}
interface-name=xmpp-upload.{{ domain }},{{ interface }}
{% endfor %}
{% if ipv6 %}
host-record={{ domain }},{{ ipv6 }}
host-record=xmpp-upload.{{ domain }},{{ ipv6 }}
{% endif %}
txt-record={{ domain }},"v=spf1 mx a -all"
mx-host={{ domain }},{{ domain }},5
srv-host=_xmpp-client._tcp.{{ domain }},{{ domain }},5222,0,5
srv-host=_xmpp-server._tcp.{{ domain }},{{ domain }},5269,0,5

View file

@ -13,9 +13,8 @@ protocols = imap sieve {% if pop3_enabled == "True" %}pop3{% endif %}
mail_plugins = $mail_plugins quota notify push_notification
###############################################################################
# generated 2020-08-18, Mozilla Guideline v5.6, Dovecot 2.3.4, OpenSSL 1.1.1d, intermediate configuration
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.4&config=intermediate&openssl=1.1.1d&guideline=5.6
# generated 2023-06-13, Mozilla Guideline v5.7, Dovecot 2.3.19, OpenSSL 3.0.9, intermediate configuration
# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.19&config=intermediate&openssl=3.0.9&guideline=5.7
ssl = required
@ -32,7 +31,7 @@ ssl_dh = </usr/share/yunohost/ffdhe2048.pem
# intermediate configuration
ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
ssl_prefer_server_ciphers = no
###############################################################################
@ -142,18 +141,6 @@ plugin {
sieve_before = /etc/dovecot/global_script/
}
plugin {
antispam_debug_target = syslog
antispam_verbose_debug = 0
antispam_backend = pipe
antispam_spam_pattern_ignorecase = junk;spam
antispam_trash_pattern_ignorecase = trash;papierkorb;deleted messages
antispam_pipe_program = /usr/bin/rspamc
antispam_pipe_program_args = -h;localhost:11334;-P;q1
antispam_pipe_program_spam_arg = learn_spam
antispam_pipe_program_notspam_arg = learn_ham
}
plugin {
quota = maildir:User quota
quota_rule2 = SPAM:ignore

View file

@ -18,7 +18,7 @@
# See man 5 jail.conf for details.
#
# [DEFAULT]
# bantime = 3600
# bantime = 1h
#
# [sshd]
# enabled = true
@ -44,10 +44,52 @@ before = paths-debian.conf
# MISCELLANEOUS OPTIONS
#
# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not
# ban a host which matches an address in this list. Several addresses can be
# defined using space (and/or comma) separator.
ignoreip = 127.0.0.1/8
# "bantime.increment" allows to use database for searching of previously banned ip's to increase a
# default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32...
#bantime.increment = true
# "bantime.rndtime" is the max number of seconds using for mixing with random time
# to prevent "clever" botnets calculate exact time IP can be unbanned again:
#bantime.rndtime =
# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further)
#bantime.maxtime =
# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier,
# default value of factor is 1 and with default value of formula, the ban time
# grows by 1, 2, 4, 8, 16 ...
#bantime.factor = 1
# "bantime.formula" used by default to calculate next value of ban time, default value below,
# the same ban time growing will be reached by multipliers 1, 2, 4, 8, 16, 32...
#bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor
#
# more aggressive example of formula has the same values only for factor "2.0 / 2.885385" :
#bantime.formula = ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor)
# "bantime.multipliers" used to calculate next value of ban time instead of formula, corresponding
# previously ban count and given "bantime.factor" (for multipliers default is 1);
# following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count,
# always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours
#bantime.multipliers = 1 2 4 8 16 32 64
# following example can be used for small initial ban time (bantime=60) - it grows more aggressive at begin,
# for bantime=60 the multipliers are minutes and equal: 1 min, 5 min, 30 min, 1 hour, 5 hour, 12 hour, 1 day, 2 day
#bantime.multipliers = 1 5 30 60 300 720 1440 2880
# "bantime.overalljails" (if true) specifies the search of IP in the database will be executed
# cross over all jails, if false (default), only current jail of the ban IP will be searched
#bantime.overalljails = false
# --------------------
# "ignoreself" specifies whether the local resp. own IP addresses should be ignored
# (default is true). Fail2ban will not ban a host which matches such addresses.
#ignoreself = true
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
#ignoreip = 127.0.0.1/8 ::1
# External command that will take an tagged arguments to ignore, e.g. <ip>,
# and return true if the IP is to be ignored. False otherwise.
@ -56,15 +98,18 @@ ignoreip = 127.0.0.1/8
ignorecommand =
# "bantime" is the number of seconds that a host is banned.
bantime = 600
bantime = 10m
# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 600
findtime = 10m
# "maxretry" is the number of failures before a host get banned.
maxretry = 10
# "maxmatches" is the number of matches stored in ticket (resolvable via tag <matches> in actions).
maxmatches = %(maxretry)s
# "backend" specifies the backend used to get files modification.
# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".
# This option can be overridden in each jail as well.
@ -113,10 +158,13 @@ logencoding = auto
enabled = false
# "mode" defines the mode of the filter (see corresponding filter implementation for more info).
mode = normal
# "filter" defines the filter to use by the jail.
# By default jails have names matching their filter name
#
filter = %(__name__)s
filter = %(__name__)s[mode=%(mode)s]
#
@ -140,7 +188,7 @@ mta = sendmail
# Default protocol
protocol = tcp
# Specify chain where jumps would need to be added in iptables-* actions
# Specify chain where jumps would need to be added in ban-actions expecting parameter chain
chain = INPUT
# Ports to be banned
@ -161,51 +209,53 @@ banaction = iptables-multiport
banaction_allports = iptables-allports
# The simplest action to take: ban only
action_ = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report to the destemail.
action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
action_mw = %(action_)s
%(mta)s-whois[sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report and relevant log lines
# to the destemail.
action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
action_mwl = %(action_)s
%(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]
# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
#
# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
# to the destemail.
action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
action_xarf = %(action_)s
xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath="%(logpath)s", port="%(port)s"]
# ban & send a notification to one or more of the 50+ services supported by Apprise.
# See https://github.com/caronc/apprise/wiki for details on what is supported.
#
# You may optionally over-ride the default configuration line (containing the Apprise URLs)
# by using 'apprise[config="/alternate/path/to/apprise.cfg"]' otherwise
# /etc/fail2ban/apprise.conf is sourced for your supported notification configuration.
# action = %(action_)s
# apprise
# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
# to the destemail.
action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
%(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
%(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"]
# Report block via blocklist.de fail2ban reporting service API
#
# See the IMPORTANT note in action.d/blocklist_de.conf for when to
# use this action. Create a file jail.d/blocklist_de.local containing
# [Init]
# blocklist_de_apikey = {api key from registration]
# See the IMPORTANT note in action.d/blocklist_de.conf for when to use this action.
# Specify expected parameters in file action.d/blocklist_de.local or if the interpolation
# `action_blocklist_de` used for the action, set value of `blocklist_de_apikey`
# in your `jail.local` globally (section [DEFAULT]) or per specific jail section (resp. in
# corresponding jail.d/my-jail.local file).
#
action_blocklist_de = blocklist_de[email="%(sender)s", service=%(filter)s, apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]
action_blocklist_de = blocklist_de[email="%(sender)s", service="%(__name__)s", apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]
# Report ban via badips.com, and use as blacklist
# Report ban via abuseipdb.com.
#
# See BadIPsAction docstring in config/action.d/badips.py for
# documentation for this action.
# See action.d/abuseipdb.conf for usage example and details.
#
# NOTE: This action relies on banaction being present on start and therefore
# should be last action defined for a jail.
#
action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"]
#
# Report ban via badips.com (uses action.d/badips.conf for reporting only)
#
action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"]
action_abuseipdb = abuseipdb
# Choose default action. To change, just override value of 'action' with the
# interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local
@ -223,15 +273,10 @@ action = %(action_)s
[sshd]
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
[sshd-ddos]
# This jail corresponds to the standard configuration in Fail2ban.
# The mail-whois action send a notification e-mail with a whois request
# in the body.
# To use more aggressive sshd modes set filter parameter "mode" in jail.local:
# normal (default), ddos, extra or aggressive (combines all).
# See "tests/files/logs/sshd" or "filter.d/sshd.conf" for usage example and details.
#mode = normal
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
@ -265,7 +310,7 @@ logpath = %(apache_error_log)s
# for email addresses. The mail outputs are buffered.
port = http,https
logpath = %(apache_access_log)s
bantime = 172800
bantime = 48h
maxretry = 1
@ -301,7 +346,7 @@ maxretry = 2
port = http,https
logpath = %(apache_access_log)s
maxretry = 1
ignorecommand = %(ignorecommands_dir)s/apache-fakegooglebot <ip>
ignorecommand = %(fail2ban_confpath)s/filter.d/ignorecommands/apache-fakegooglebot <ip>
[apache-modsecurity]
@ -321,12 +366,15 @@ maxretry = 1
[openhab-auth]
filter = openhab
action = iptables-allports[name=NoAuthFailures]
banaction = %(banaction_allports)s
logpath = /opt/openhab/logs/request.log
# To use more aggressive http-auth modes set filter parameter "mode" in jail.local:
# normal (default), aggressive (combines all), auth or fallback
# See "tests/files/logs/nginx-http-auth" or "filter.d/nginx-http-auth.conf" for usage example and details.
[nginx-http-auth]
# mode = normal
port = http,https
logpath = %(nginx_error_log)s
@ -342,8 +390,10 @@ logpath = %(nginx_error_log)s
port = http,https
logpath = %(nginx_error_log)s
maxretry = 2
[nginx-bad-request]
port = http,https
logpath = %(nginx_access_log)s
# Ban attackers that try to use PHP's URL-fopen() functionality
# through GET/POST variables. - Experimental, with more than a year
@ -377,6 +427,8 @@ logpath = %(lighttpd_error_log)s
port = http,https
logpath = %(roundcube_errors_log)s
# Use following line in your jail.local if roundcube logs to journal.
#backend = %(syslog_backend)s
[openwebmail]
@ -426,11 +478,13 @@ backend = %(syslog_backend)s
port = http,https
logpath = /var/log/tomcat*/catalina.out
#logpath = /var/log/guacamole.log
[monit]
#Ban clients brute-forcing the monit gui login
port = 2812
logpath = /var/log/monit
/var/log/monit.log
[webmin-auth]
@ -513,27 +567,29 @@ logpath = %(vsftpd_log)s
# ASSP SMTP Proxy Jail
[assp]
port = smtp,submission
port = smtp,465,submission
logpath = /root/path/to/assp/logs/maillog.txt
[courier-smtp]
port = smtp,submission
port = smtp,465,submission
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
[postfix]
port = smtp,submission
logpath = %(postfix_log)s
backend = %(postfix_backend)s
# To use another modes set filter parameter "mode" in jail.local:
mode = more
port = smtp,465,submission
logpath = %(postfix_log)s
backend = %(postfix_backend)s
[postfix-rbl]
port = smtp,submission
filter = postfix[mode=rbl]
port = smtp,465,submission
logpath = %(postfix_log)s
backend = %(postfix_backend)s
maxretry = 1
@ -541,14 +597,17 @@ maxretry = 1
[sendmail-auth]
port = submission,smtp
port = submission,465,smtp
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
[sendmail-reject]
port = smtp,submission
# To use more aggressive modes set filter parameter "mode" in jail.local:
# normal (default), extra or aggressive
# See "tests/files/logs/sendmail-reject" or "filter.d/sendmail-reject.conf" for usage example and details.
#mode = normal
port = smtp,465,submission
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
@ -556,7 +615,7 @@ backend = %(syslog_backend)s
[qmail-rbl]
filter = qmail
port = smtp,submission
port = smtp,465,submission
logpath = /service/qmail/log/main/current
@ -564,14 +623,14 @@ logpath = /service/qmail/log/main/current
# but can be set by syslog_facility in the dovecot configuration.
[dovecot]
port = pop3,pop3s,imap,imaps,submission,sieve
port = pop3,pop3s,imap,imaps,submission,465,sieve
logpath = %(dovecot_log)s
backend = %(dovecot_backend)s
[sieve]
port = smtp,submission
port = smtp,465,submission
logpath = %(dovecot_log)s
backend = %(dovecot_backend)s
@ -583,20 +642,21 @@ logpath = %(solidpop3d_log)s
[exim]
port = smtp,submission
# see filter.d/exim.conf for further modes supported from filter:
#mode = normal
port = smtp,465,submission
logpath = %(exim_main_log)s
[exim-spam]
port = smtp,submission
port = smtp,465,submission
logpath = %(exim_main_log)s
[kerio]
port = imap,smtp,imaps
port = imap,smtp,imaps,465
logpath = /opt/kerio/mailserver/store/logs/security.log
@ -607,14 +667,15 @@ logpath = /opt/kerio/mailserver/store/logs/security.log
[courier-auth]
port = smtp,submission,imaps,pop3,pop3s
port = smtp,465,submission,imap,imaps,pop3,pop3s
logpath = %(syslog_mail)s
backend = %(syslog_backend)s
[postfix-sasl]
port = smtp,submission,imap,imaps,pop3,pop3s
filter = postfix[mode=auth]
port = smtp,465,submission,imap,imaps,pop3,pop3s
# You might consider monitoring /var/log/mail.warn instead if you are
# running postfix since it would provide the same log lines at the
# "warn" level but overall at the smaller filesize.
@ -631,7 +692,7 @@ backend = %(syslog_backend)s
[squirrelmail]
port = smtp,submission,imap,imap2,imaps,pop3,pop3s,http,https,socks
port = smtp,465,submission,imap,imap2,imaps,pop3,pop3s,http,https,socks
logpath = /var/lib/squirrelmail/prefs/squirrelmail_access_log
@ -684,8 +745,8 @@ logpath = /var/log/named/security.log
[nsd]
port = 53
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/nsd.log
@ -696,9 +757,8 @@ logpath = /var/log/nsd.log
[asterisk]
port = 5060,5061
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s"]
action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/asterisk/messages
maxretry = 10
@ -706,16 +766,22 @@ maxretry = 10
[freeswitch]
port = 5060,5061
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s"]
action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/freeswitch.log
maxretry = 10
# enable adminlog; it will log to a file inside znc's directory by default.
[znc-adminlog]
port = 6667
logpath = /var/lib/znc/moddata/adminlog/znc.log
# To log wrong MySQL access attempts add to /etc/my.cnf in [mysqld] or
# equivalent section:
# log-warning = 2
# log-warnings = 2
#
# for syslog (daemon facility)
# [mysqld_safe]
@ -731,6 +797,14 @@ logpath = %(mysql_log)s
backend = %(mysql_backend)s
[mssql-auth]
# Default configuration for Microsoft SQL Server for Linux
# See the 'mssql-conf' manpage how to change logpath or port
logpath = /var/opt/mssql/log/errorlog
port = 1433
filter = mssql-auth
# Log wrong MongoDB auth (for details see filter 'filter.d/mongodb-auth.conf')
[mongodb-auth]
# change port when running with "--shardsvr" or "--configsvr" runtime operation
@ -749,8 +823,8 @@ logpath = /var/log/mongodb/mongodb.log
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime = 604800 ; 1 week
findtime = 86400 ; 1 day
bantime = 1w
findtime = 1d
# Generic filter for PAM. Has to be used with action which bans all
@ -786,11 +860,31 @@ logpath = /var/log/ejabberd/ejabberd.log
[counter-strike]
logpath = /opt/cstrike/logs/L[0-9]*.log
# Firewall: http://www.cstrike-planet.com/faq/6
tcpport = 27030,27031,27032,27033,27034,27035,27036,27037,27038,27039
udpport = 1200,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010,27011,27012,27013,27014,27015
action = %(banaction)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp", chain="%(chain)s", actname=%(banaction)s-tcp]
%(banaction)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp", chain="%(chain)s", actname=%(banaction)s-udp]
action_ = %(default/action_)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp"]
%(default/action_)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp"]
[softethervpn]
port = 500,4500
protocol = udp
logpath = /usr/local/vpnserver/security_log/*/sec.log
[gitlab]
port = http,https
logpath = /var/log/gitlab/gitlab-rails/application.log
[grafana]
port = http,https
logpath = /var/log/grafana/grafana.log
[bitwarden]
port = http,https
logpath = /home/*/bwdata/logs/identity/Identity/log.txt
[centreon]
port = http,https
logpath = /var/log/centreon/login.log
# consider low maxretry and a long bantime
# nobody except your own Nagios server should ever probe nrpe
@ -824,7 +918,9 @@ filter = apache-pass[knocking_url="%(knocking_url)s"]
logpath = %(apache_access_log)s
blocktype = RETURN
returntype = DROP
bantime = 3600
action = %(action_)s[blocktype=%(blocktype)s, returntype=%(returntype)s,
actionstart_on_demand=false, actionrepair_on_unban=true]
bantime = 1h
maxretry = 1
findtime = 1
@ -832,8 +928,8 @@ findtime = 1
[murmur]
# AKA mumble-server
port = 64738
action = %(banaction)s[name=%(__name__)s-tcp, port="%(port)s", protocol=tcp, chain="%(chain)s", actname=%(banaction)s-tcp]
%(banaction)s[name=%(__name__)s-udp, port="%(port)s", protocol=udp, chain="%(chain)s", actname=%(banaction)s-udp]
action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"]
%(default/action_)s[name=%(__name__)s-udp, protocol="udp"]
logpath = /var/log/mumble-server/mumble-server.log
@ -851,5 +947,34 @@ logpath = /var/log/haproxy.log
[slapd]
port = ldap,ldaps
filter = slapd
logpath = /var/log/slapd.log
[domino-smtp]
port = smtp,ssmtp
logpath = /home/domino01/data/IBM_TECHNICAL_SUPPORT/console.log
[phpmyadmin-syslog]
port = http,https
logpath = %(syslog_authpriv)s
backend = %(syslog_backend)s
[zoneminder]
# Zoneminder HTTP/HTTPS web interface auth
# Logs auth failures to apache2 error log
port = http,https
logpath = %(apache_error_log)s
[traefik-auth]
# to use 'traefik-auth' filter you have to configure your Traefik instance,
# see `filter.d/traefik-auth.conf` for details and service example.
port = http,https
logpath = /var/log/traefik/access.log
[scanlogd]
logpath = %(syslog_local0)s
banaction = %(banaction_allports)s
[monitorix]
port = 8080
logpath = /var/log/monitorix-httpd

View file

@ -31,3 +31,12 @@ protocol = tcp
filter = yunohost
logpath = /var/log/nginx/*error.log
/var/log/nginx/*access.log
[yunohost-portal]
enabled = true
port = http,https
protocol = tcp
filter = yunohost-portal
logpath = /var/log/nginx/*error.log
/var/log/nginx/*access.log
maxretry = 20

View file

@ -0,0 +1,3 @@
[Definition]
failregex = ^<HOST> -.*\"POST /yunohost/portalapi/login HTTP/\d.\d\" 401
ignoreregex =

View file

@ -1,24 +1,3 @@
# Fail2Ban configuration file
#
# Author: Adrien Beudin
#
# $Revision: 2 $
#
[Definition]
# Option: failregex
# Notes.: regex to match the password failure messages in the logfile. The
# host must be matched by a group named "host". The tag "<HOST>" can
# be used for standard IP/hostname matching and is only an alias for
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
# Values: TEXT
#
failregex = helpers.lua:[0-9]+: authenticate\(\): Connection failed for: .*, client: <HOST>
^<HOST> -.*\"POST /yunohost/api/login HTTP/\d.\d\" 401
# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
failregex = ^<HOST> -.*\"POST /yunohost/api/login HTTP/\d.\d\" 401
ignoreregex =

View file

@ -1,75 +0,0 @@
VirtualHost "{{ domain }}"
enable = true
ssl = {
key = "/etc/yunohost/certs/{{ domain }}/key.pem";
certificate = "/etc/yunohost/certs/{{ domain }}/crt.pem";
}
authentication = "ldap2"
ldap = {
hostname = "localhost",
user = {
basedn = "ou=users,dc=yunohost,dc=org",
filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=xmpp.main,ou=permission,dc=yunohost,dc=org))",
usernamefield = "mail",
namefield = "cn",
},
}
-- Discovery items
disco_items = {
{ "muc.{{ domain }}" },
{ "pubsub.{{ domain }}" },
{ "jabber.{{ domain }}" },
{ "vjud.{{ domain }}" },
{ "xmpp-upload.{{ domain }}" },
};
-- contact_info = {
-- abuse = { "mailto:abuse@{{ domain }}", "xmpp:admin@{{ domain }}" };
-- admin = { "mailto:root@{{ domain }}", "xmpp:admin@{{ domain }}" };
-- };
------ Components ------
-- You can specify components to add hosts that provide special services,
-- like multi-user conferences, and transports.
---Set up a MUC (multi-user chat) room server
Component "muc.{{ domain }}" "muc"
name = "{{ domain }} Chatrooms"
modules_enabled = {
"muc_limits";
"muc_log";
"muc_log_mam";
"muc_log_http";
"muc_vcard";
}
muc_event_rate = 0.5
muc_burst_factor = 10
room_default_config = {
logging = true,
persistent = true
};
---Set up a PubSub server
Component "pubsub.{{ domain }}" "pubsub"
name = "{{ domain }} Publish/Subscribe"
unrestricted_node_creation = true -- Anyone can create a PubSub node (from any server)
---Set up a HTTP Upload service
Component "xmpp-upload.{{ domain }}" "http_upload"
name = "{{ domain }} Sharing Service"
http_file_path = "/var/xmpp-upload/{{ domain }}/upload"
http_external_url = "https://xmpp-upload.{{ domain }}:443"
http_file_base_path = "/upload"
http_file_size_limit = 6*1024*1024
http_file_quota = 60*1024*1024
http_upload_file_size_limit = 100 * 1024 * 1024 -- bytes
http_upload_quota = 10 * 1024 * 1024 * 1024 -- bytes
---Set up a VJUD service
Component "vjud.{{ domain }}" "vjud"
vjud_disco_name = "{{ domain }} User Directory"

View file

@ -1,123 +0,0 @@
-- ** Metronome's config file example **
--
-- The format is exactly equal to Prosody's:
--
-- Lists are written { "like", "this", "one" }
-- Lists can also be of { 1, 2, 3 } numbers, etc.
-- Either commas, or semi-colons; may be used as seperators.
--
-- A table is a list of values, except each value has a name. An
-- example would be:
--
-- ssl = { key = "keyfile.key", certificate = "certificate.cert" }
--
-- Tip: You can check that the syntax of this file is correct when you have finished
-- by running: luac -p metronome.cfg.lua
-- If there are any errors, it will let you know what and where they are, otherwise it
-- will keep quiet.
-- Global settings go in this section
-- This is the list of modules Metronome will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
modules_enabled = {
-- Generally required
"roster"; -- Allow users to have a roster. Recommended.
"saslauth"; -- Authentication for clients. Recommended if you want to log in.
"tls"; -- Add support for secure TLS on c2s/s2s connections
"disco"; -- Service discovery
-- Not essential, but recommended
"private"; -- Private XML storage (for room bookmarks, etc.)
"vcard"; -- Allow users to set vCards
"pep"; -- Allows setting of mood, tune, etc.
"pubsub"; -- Publish-subscribe XEP-0060
"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
"bidi"; -- Enables Bidirectional Server-to-Server Streams.
-- Nice to have
"version"; -- Replies to server version requests
"uptime"; -- Report how long server has been running
"time"; -- Let others know the time here on this server
"ping"; -- Replies to XMPP pings with pongs
"register"; -- Allow users to register on this server using a client and change passwords
"stream_management"; -- Allows clients and servers to use Stream Management
"stanza_optimizations"; -- Allows clients to use Client State Indication and SIFT
"message_carbons"; -- Allows clients to enable carbon copies of messages
"mam"; -- Enable server-side message archives using Message Archive Management
"push"; -- Enable Push Notifications via PubSub using XEP-0357
"lastactivity"; -- Enables clients to know the last presence status of an user
"adhoc_cm"; -- Allow to set client certificates to login through SASL External via adhoc
"admin_adhoc"; -- administration adhoc commands
"bookmarks"; -- XEP-0048 Bookmarks synchronization between PEP and Private Storage
"sec_labels"; -- Allows to use a simplified version XEP-0258 Security Labels and related ACDFs.
"privacy"; -- Add privacy lists and simple blocking command support
-- Other specific functionality
--"admin_telnet"; -- administration console, telnet to port 5582
--"admin_web"; -- administration web interface
"bosh"; -- Enable support for BOSH clients, aka "XMPP over Bidirectional Streams over Synchronous HTTP"
--"compression"; -- Allow clients to enable Stream Compression
--"spim_block"; -- Require authorization via OOB form for messages from non-contacts and block unsollicited messages
--"gate_guard"; -- Enable config-based blacklisting and hit-based auto-banning features
--"incidents_handling"; -- Enable Incidents Handling support (can be administered via adhoc commands)
--"server_presence"; -- Enables Server Buddies extension support
--"service_directory"; -- Enables Service Directories extension support
--"public_service"; -- Enables Server vCard support for public services in directories and advertises in features
--"register_api"; -- Provides secure API for both Out-Of-Band and In-Band registration for E-Mail verification
"websocket"; -- Enable support for WebSocket clients, aka "XMPP over WebSockets"
};
-- Server PID
pidfile = "/var/run/metronome/metronome.pid"
-- HTTP server
http_ports = { 5290 }
http_interfaces = { "127.0.0.1", "::1" }
--https_ports = { 5291 }
--https_interfaces = { "127.0.0.1", "::1" }
-- Enable IPv6
use_ipv6 = true
-- BOSH configuration (mod_bosh)
consider_bosh_secure = true
cross_domain_bosh = true
-- WebSocket configuration (mod_websocket)
consider_websocket_secure = true
cross_domain_websocket = true
-- Disable account creation by default, for security
allow_registration = false
-- Use LDAP storage backend for all stores
storage = "ldap"
-- stanza optimization
csi_config_queue_all_muc_messages_but_mentions = false;
-- Logging configuration
log = {
info = "/var/log/metronome/metronome.log"; -- Change 'info' to 'debug' for verbose logging
error = "/var/log/metronome/metronome.err";
-- "*syslog"; -- Uncomment this for logging to syslog
-- "*console"; -- Log to the console, useful for debugging with daemonize=false
}
------ Components ------
-- You can specify components to add hosts that provide special services,
-- like multi-user conferences, and transports.
---Set up a local BOSH service
Component "localhost" "http"
modules_enabled = { "bosh" }
----------- Virtual hosts -----------
-- You need to add a VirtualHost entry for each domain you wish Metronome to serve.
-- Settings under each VirtualHost entry apply *only* to that host.
Include "conf.d/*.cfg.lua"

View file

@ -1,270 +0,0 @@
-- vim:sts=4 sw=4
-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
-- Copyright (C) 2012 Rob Hoelz
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local ldap;
local connection;
local params = module:get_option("ldap");
local format = string.format;
local tconcat = table.concat;
local _M = {};
local config_params = {
hostname = 'string',
user = {
basedn = 'string',
namefield = 'string',
filter = 'string',
usernamefield = 'string',
},
groups = {
basedn = 'string',
namefield = 'string',
memberfield = 'string',
_member = {
name = 'string',
admin = 'boolean?',
},
},
admin = {
_optional = true,
basedn = 'string',
namefield = 'string',
filter = 'string',
}
}
local function run_validation(params, config, prefix)
prefix = prefix or '';
-- verify that every required member of config is present in params
for k, v in pairs(config) do
if type(k) == 'string' and k:sub(1, 1) ~= '_' then
local is_optional;
if type(v) == 'table' then
is_optional = v._optional;
else
is_optional = v:sub(-1) == '?';
end
if not is_optional and params[k] == nil then
return nil, prefix .. k .. ' is required';
end
end
end
for k, v in pairs(params) do
local expected_type = config[k];
local ok, err = true;
if type(k) == 'string' then
-- verify that this key is present in config
if k:sub(1, 1) == '_' or expected_type == nil then
return nil, 'invalid parameter ' .. prefix .. k;
end
-- type validation
if type(expected_type) == 'string' then
if expected_type:sub(-1) == '?' then
expected_type = expected_type:sub(1, -2);
end
if type(v) ~= expected_type then
return nil, 'invalid type for parameter ' .. prefix .. k;
end
else -- it's a table (or had better be)
if type(v) ~= 'table' then
return nil, 'invalid type for parameter ' .. prefix .. k;
end
-- recurse into child
ok, err = run_validation(v, expected_type, prefix .. k .. '.');
end
else -- it's an integer (or had better be)
if not config._member then
return nil, 'invalid parameter ' .. prefix .. tostring(k);
end
ok, err = run_validation(v, config._member, prefix .. tostring(k) .. '.');
end
if not ok then
return ok, err;
end
end
return true;
end
local function validate_config()
if true then
return true; -- XXX for now
end
-- this is almost too clever (I mean that in a bad
-- maintainability sort of way)
--
-- basically this allows a free pass for a key in group members
-- equal to params.groups.namefield
setmetatable(config_params.groups._member, {
__index = function(_, k)
if k == params.groups.namefield then
return 'string';
end
end
});
local ok, err = run_validation(params, config_params);
setmetatable(config_params.groups._member, nil);
if ok then
-- a little extra validation that doesn't fit into
-- my recursive checker
local group_namefield = params.groups.namefield;
for i, group in ipairs(params.groups) do
if not group[group_namefield] then
return nil, format('groups.%d.%s is required', i, group_namefield);
end
end
-- fill in params.admin if you can
if not params.admin and params.groups then
local admingroup;
for _, groupconfig in ipairs(params.groups) do
if groupconfig.admin then
admingroup = groupconfig;
break;
end
end
if admingroup then
params.admin = {
basedn = params.groups.basedn,
namefield = params.groups.memberfield,
filter = group_namefield .. '=' .. admingroup[group_namefield],
};
end
end
end
return ok, err;
end
-- what to do if connection isn't available?
local function connect()
return ldap.open_simple(params.hostname, params.bind_dn, params.bind_password, params.use_tls);
end
-- this is abstracted so we can maintain persistent connections at a later time
function _M.getconnection()
return connect();
end
function _M.getparams()
return params;
end
-- XXX consider renaming this...it doesn't bind the current connection
function _M.bind(username, password)
local conn = _M.getconnection();
local filter = format('%s=%s', params.user.usernamefield, username);
if params.user.usernamefield == 'mail' then
filter = format('mail=%s@*', username);
end
if filter then
filter = _M.filter.combine_and(filter, params.user.filter);
end
local who = _M.singlematch {
attrs = params.user.usernamefield,
base = params.user.basedn,
filter = filter,
};
if who then
who = who.dn;
module:log('debug', '_M.bind - who: %s', who);
else
module:log('debug', '_M.bind - no DN found for username = %s', username);
return nil, format('no DN found for username = %s', username);
end
local conn, err = ldap.open_simple(params.hostname, who, password, params.use_tls);
if conn then
conn:close();
return true;
end
return conn, err;
end
function _M.singlematch(query)
local ld = _M.getconnection();
query.sizelimit = 1;
query.scope = 'subtree';
for dn, attribs in ld:search(query) do
attribs.dn = dn;
return attribs;
end
end
_M.filter = {};
function _M.filter.combine_and(...)
local parts = { '(&' };
local arg = { ... };
for _, filter in ipairs(arg) do
if filter:sub(1, 1) ~= '(' and filter:sub(-1) ~= ')' then
filter = '(' .. filter .. ')'
end
parts[#parts + 1] = filter;
end
parts[#parts + 1] = ')';
return tconcat(parts, '');
end
do
local ok, err;
metronome.unlock_globals();
ok, ldap = pcall(require, 'lualdap');
metronome.lock_globals();
if not ok then
module:log("error", "Failed to load the LuaLDAP library for accessing LDAP: %s", ldap);
module:log("error", "More information on install LuaLDAP can be found at http://www.keplerproject.org/lualdap");
return;
end
if not params then
module:log("error", "LDAP configuration required to use the LDAP storage module");
return;
end
ok, err = validate_config();
if not ok then
module:log("error", "LDAP configuration is invalid: %s", tostring(err));
return;
end
end
return _M;

View file

@ -1,90 +0,0 @@
-- vim:sts=4 sw=4
-- Metronome IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
-- Copyright (C) 2012 Rob Hoelz
-- Copyright (C) 2015 YUNOHOST.ORG
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
-- https://github.com/YunoHost/yunohost-config-metronome/blob/unstable/lib/modules/mod_auth_ldap2.lua
-- adapted to use common LDAP store on Metronome
local ldap = module:require 'ldap';
local new_sasl = require 'util.sasl'.new;
local jsplit = require 'util.jid'.split;
local log = module._log
if not ldap then
return;
end
function new_default_provider(host)
local provider = { name = "ldap2" };
log("debug", "initializing ldap2 authentication provider for host '%s'", host);
function provider.test_password(username, password)
return ldap.bind(username, password);
end
function provider.user_exists(username)
local params = ldap.getparams()
local filter = ldap.filter.combine_and(params.user.filter, params.user.usernamefield .. '=' .. username);
if params.user.usernamefield == 'mail' then
filter = ldap.filter.combine_and(params.user.filter, 'mail=' .. username .. '@*');
end
return ldap.singlematch {
base = params.user.basedn,
filter = filter,
};
end
function provider.get_password(username)
return nil, "Passwords unavailable for LDAP.";
end
function provider.set_password(username, password)
return nil, "Passwords unavailable for LDAP.";
end
function provider.create_user(username, password)
return nil, "Account creation/modification not available with LDAP.";
end
function provider.get_sasl_handler(session)
local testpass_authentication_profile = {
session = session,
plain_test = function(sasl, username, password, realm)
return provider.test_password(username, password), true;
end,
order = { "plain_test" },
};
return new_sasl(module.host, testpass_authentication_profile);
end
function provider.is_admin(jid)
local admin_config = ldap.getparams().admin;
if not admin_config then
return;
end
local ld = ldap:getconnection();
local username = jsplit(jid);
local filter = ldap.filter.combine_and(admin_config.filter, admin_config.namefield .. '=' .. username);
return ldap.singlematch {
base = admin_config.basedn,
filter = filter,
};
end
return provider;
end
module:add_item("auth-provider", new_default_provider(module.host));

View file

@ -1,86 +0,0 @@
-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local st = require "util.stanza";
local t_concat = table.concat;
local secure_auth_only = module:get_option("c2s_require_encryption")
or module:get_option("require_encryption")
or not(module:get_option("allow_unencrypted_plain_auth"));
local sessionmanager = require "core.sessionmanager";
local usermanager = require "core.usermanager";
local nodeprep = require "util.encodings".stringprep.nodeprep;
local resourceprep = require "util.encodings".stringprep.resourceprep;
module:add_feature("jabber:iq:auth");
module:hook("stream-features", function(event)
local origin, features = event.origin, event.features;
if secure_auth_only and not origin.secure then
-- Sorry, not offering to insecure streams!
return;
elseif not origin.username then
features:tag("auth", {xmlns='http://jabber.org/features/iq-auth'}):up();
end
end);
module:hook("stanza/iq/jabber:iq:auth:query", function(event)
local session, stanza = event.origin, event.stanza;
if session.type ~= "c2s_unauthed" then
(session.sends2s or session.send)(st.error_reply(stanza, "cancel", "service-unavailable", "Legacy authentication is only allowed for unauthenticated client connections."));
return true;
end
if secure_auth_only and not session.secure then
session.send(st.error_reply(stanza, "modify", "not-acceptable", "Encryption (SSL or TLS) is required to connect to this server"));
return true;
end
local username = stanza.tags[1]:child_with_name("username");
local password = stanza.tags[1]:child_with_name("password");
local resource = stanza.tags[1]:child_with_name("resource");
if not (username and password and resource) then
local reply = st.reply(stanza);
session.send(reply:query("jabber:iq:auth")
:tag("username"):up()
:tag("password"):up()
:tag("resource"):up());
else
username, password, resource = t_concat(username), t_concat(password), t_concat(resource);
username = nodeprep(username);
resource = resourceprep(resource)
if not (username and resource) then
session.send(st.error_reply(stanza, "modify", "bad-request"));
return true;
end
if usermanager.test_password(username, session.host, password) then
-- Authentication successful!
local success, err = sessionmanager.make_authenticated(session, username);
if success then
local err_type, err_msg;
success, err_type, err, err_msg = sessionmanager.bind_resource(session, resource);
if not success then
session.send(st.error_reply(stanza, err_type, err, err_msg));
session.username, session.type = nil, "c2s_unauthed"; -- FIXME should this be placed in sessionmanager?
return true;
elseif resource ~= session.resource then -- server changed resource, not supported by legacy auth
session.send(st.error_reply(stanza, "cancel", "conflict", "The requested resource could not be assigned to this session."));
session:close(); -- FIXME undo resource bind and auth instead of closing the session?
return true;
end
end
session.send(st.reply(stanza));
else
session.send(st.error_reply(stanza, "auth", "not-authorized"));
end
end
return true;
end);

View file

@ -1,243 +0,0 @@
-- vim:sts=4 sw=4
-- Metronome IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
-- Copyright (C) 2012 Rob Hoelz
-- Copyright (C) 2015 YUNOHOST.ORG
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
----------------------------------------
-- Constants and such --
----------------------------------------
local setmetatable = setmetatable;
local get_config = require "core.configmanager".get;
local ldap = module:require 'ldap';
local vcardlib = module:require 'vcard';
local st = require 'util.stanza';
local gettime = require 'socket'.gettime;
local log = module._log
if not ldap then
return;
end
local CACHE_EXPIRY = 300;
----------------------------------------
-- Utility Functions --
----------------------------------------
local function ldap_record_to_vcard(record, format)
return vcardlib.create {
record = record,
format = format,
}
end
local get_alias_for_user;
do
local user_cache;
local last_fetch_time;
local function populate_user_cache()
local user_c = get_config(module.host, 'ldap').user;
if not user_c then return; end
local ld = ldap.getconnection();
local usernamefield = user_c.usernamefield;
local namefield = user_c.namefield;
user_cache = {};
for _, attrs in ld:search { base = user_c.basedn, scope = 'onelevel', filter = user_c.filter } do
user_cache[attrs[usernamefield]] = attrs[namefield];
end
last_fetch_time = gettime();
end
function get_alias_for_user(user)
if last_fetch_time and last_fetch_time + CACHE_EXPIRY < gettime() then
user_cache = nil;
end
if not user_cache then
populate_user_cache();
end
return user_cache[user];
end
end
----------------------------------------
-- Base LDAP store class --
----------------------------------------
local function ldap_store(config)
local self = {};
local config = config;
function self:get(username)
return nil, "Data getting is not available for this storage backend";
end
function self:set(username, data)
return nil, "Data setting is not available for this storage backend";
end
return self;
end
local adapters = {};
----------------------------------------
-- Roster Storage Implementation --
----------------------------------------
adapters.roster = function (config)
-- Validate configuration requirements
if not config.groups then return nil; end
local self = ldap_store(config)
function self:get(username)
local ld = ldap.getconnection();
local contacts = {};
local memberfield = config.groups.memberfield;
local namefield = config.groups.namefield;
local filter = memberfield .. '=' .. tostring(username);
local groups = {};
for _, config in ipairs(config.groups) do
groups[ config[namefield] ] = config.name;
end
log("debug", "Found %d group(s) for user %s", select('#', groups), username)
-- XXX this kind of relies on the way we do groups at INOC
for _, attrs in ld:search { base = config.groups.basedn, scope = 'onelevel', filter = filter } do
if groups[ attrs[namefield] ] then
local members = attrs[memberfield];
for _, user in ipairs(members) do
if user ~= username then
local jid = user .. '@' .. module.host;
local record = contacts[jid];
if not record then
record = {
subscription = 'both',
groups = {},
name = get_alias_for_user(user),
};
contacts[jid] = record;
end
record.groups[ groups[ attrs[namefield] ] ] = true;
end
end
end
end
return contacts;
end
function self:set(username, data)
log("warn", "Setting data in Roster LDAP storage is not supported yet")
return nil, "not supported";
end
return self;
end
----------------------------------------
-- vCard Storage Implementation --
----------------------------------------
adapters.vcard = function (config)
-- Validate configuration requirements
if not config.vcard_format or not config.user then return nil; end
local self = ldap_store(config)
function self:get(username)
local ld = ldap.getconnection();
local filter = config.user.usernamefield .. '=' .. tostring(username);
log("debug", "Retrieving vCard for user '%s'", username);
local match = ldap.singlematch {
base = config.user.basedn,
filter = filter,
};
if match then
match.jid = username .. '@' .. module.host
return st.preserialize(ldap_record_to_vcard(match, config.vcard_format));
else
return nil, "username not found";
end
end
function self:set(username, data)
log("warn", "Setting data in vCard LDAP storage is not supported yet")
return nil, "not supported";
end
return self;
end
----------------------------------------
-- Driver Definition --
----------------------------------------
cache = {};
local driver = { name = "ldap" };
function driver:open(store)
log("debug", "Opening ldap storage backend for host '%s' and store '%s'", module.host, store);
if not cache[module.host] then
log("debug", "Caching adapters for the host '%s'", module.host);
local ad_config = get_config(module.host, "ldap");
local ad_cache = {};
for k, v in pairs(adapters) do
ad_cache[k] = v(ad_config);
end
cache[module.host] = ad_cache;
end
local adapter = cache[module.host][store];
if not adapter then
log("info", "Unavailable adapter for store '%s'", store);
return nil, "unsupported-store";
end
return adapter;
end
function driver:stores(username, type, pattern)
return nil, "not implemented";
end
function driver:store_exists(username, type)
return nil, "not implemented";
end
function driver:purge(username)
return nil, "not implemented";
end
function driver:nodes(type)
return nil, "not implemented";
end
module:add_item("data-driver", driver);

View file

@ -1,162 +0,0 @@
-- vim:sts=4 sw=4
-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
-- Copyright (C) 2012 Rob Hoelz
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local st = require 'util.stanza';
local VCARD_NS = 'vcard-temp';
local builder_methods = {};
local base64_encode = require('util.encodings').base64.encode;
function builder_methods:addvalue(key, value)
self.vcard:tag(key):text(value):up();
end
function builder_methods:addphotofield(tagname, format_section)
local record = self.record;
local format = self.format;
local vcard = self.vcard;
local config = format[format_section];
if not config then
return;
end
if config.extval then
if record[config.extval] then
local tag = vcard:tag(tagname);
tag:tag('EXTVAL'):text(record[config.extval]):up();
end
elseif config.type and config.binval then
if record[config.binval] then
local tag = vcard:tag(tagname);
tag:tag('TYPE'):text(config.type):up();
tag:tag('BINVAL'):text(base64_encode(record[config.binval])):up();
end
else
module:log('error', 'You have an invalid %s config section', tagname);
return;
end
vcard:up();
end
function builder_methods:addregularfield(tagname, format_section)
local record = self.record;
local format = self.format;
local vcard = self.vcard;
if not format[format_section] then
return;
end
local tag = vcard:tag(tagname);
for k, v in pairs(format[format_section]) do
tag:tag(string.upper(k)):text(record[v]):up();
end
vcard:up();
end
function builder_methods:addmultisectionedfield(tagname, format_section)
local record = self.record;
local format = self.format;
local vcard = self.vcard;
if not format[format_section] then
return;
end
for k, v in pairs(format[format_section]) do
local tag = vcard:tag(tagname);
if type(k) == 'string' then
tag:tag(string.upper(k)):up();
end
for k2, v2 in pairs(v) do
if type(v2) == 'boolean' then
tag:tag(string.upper(k2)):up();
else
tag:tag(string.upper(k2)):text(record[v2]):up();
end
end
vcard:up();
end
end
function builder_methods:build()
local record = self.record;
local format = self.format;
self:addvalue( 'VERSION', '2.0');
self:addvalue( 'FN', record[format.displayname]);
self:addregularfield( 'N', 'name');
self:addvalue( 'NICKNAME', record[format.nickname]);
self:addphotofield( 'PHOTO', 'photo');
self:addvalue( 'BDAY', record[format.birthday]);
self:addmultisectionedfield('ADR', 'address');
self:addvalue( 'LABEL', nil); -- we don't support LABEL...yet.
self:addmultisectionedfield('TEL', 'telephone');
self:addmultisectionedfield('EMAIL', 'email');
self:addvalue( 'JABBERID', record.jid);
self:addvalue( 'MAILER', record[format.mailer]);
self:addvalue( 'TZ', record[format.timezone]);
self:addregularfield( 'GEO', 'geo');
self:addvalue( 'TITLE', record[format.title]);
self:addvalue( 'ROLE', record[format.role]);
self:addphotofield( 'LOGO', 'logo');
self:addvalue( 'AGENT', nil); -- we don't support AGENT...yet.
self:addregularfield( 'ORG', 'org');
self:addvalue( 'CATEGORIES', nil); -- we don't support CATEGORIES...yet.
self:addvalue( 'NOTE', record[format.note]);
self:addvalue( 'PRODID', nil); -- we don't support PRODID...yet.
self:addvalue( 'REV', record[format.rev]);
self:addvalue( 'SORT-STRING', record[format.sortstring]);
self:addregularfield( 'SOUND', 'sound');
self:addvalue( 'UID', record[format.uid]);
self:addvalue( 'URL', record[format.url]);
self:addvalue( 'CLASS', nil); -- we don't support CLASS...yet.
self:addregularfield( 'KEY', 'key');
self:addvalue( 'DESC', record[format.description]);
return self.vcard;
end
local function new_builder(params)
local vcard_tag = st.stanza('vCard', { xmlns = VCARD_NS });
local object = {
vcard = vcard_tag,
__index = builder_methods,
};
for k, v in pairs(params) do
object[k] = v;
end
setmetatable(object, object);
return object;
end
local _M = {};
function _M.create(params)
local builder = new_builder(params);
return builder:build();
end
return _M;

View file

@ -1,8 +0,0 @@
# Insert YunoHost button + portal overlay
sub_filter </head> '<script type="text/javascript" src="/ynh_portal.js"></script><link type="text/css" rel="stylesheet" href="/ynh_overlay.css"><script type="text/javascript" src="/ynhtheme/custom_portal.js"></script><link type="text/css" rel="stylesheet" href="/ynhtheme/custom_overlay.css"></head>';
sub_filter_once on;
# Apply to other mime types than text/html
sub_filter_types application/xhtml+xml;
# Prevent YunoHost panel files from being blocked by specific app rules
location ~ (ynh_portal.js|ynh_overlay.css|ynh_userinfo.json|ynhtheme/custom_portal.js|ynhtheme/custom_overlay.css) {
}

View file

@ -1,7 +0,0 @@
# Avoid the nginx path/alias traversal weakness ( #1037 )
rewrite ^/yunohost/sso$ /yunohost/sso/ permanent;
location /yunohost/sso/ {
# This is an empty location, only meant to avoid other locations
# from matching /yunohost/sso, such that it's correctly handled by ssowat
}

View file

@ -3,16 +3,16 @@ ssl_session_cache shared:SSL:50m; # about 200000 sessions
ssl_session_tickets off;
{% if compatibility == "modern" %}
# generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, modern configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=modern&openssl=1.1.1d&guideline=5.6
# generated 2023-06-13, Mozilla Guideline v5.7, nginx 1.22.1, OpenSSL 3.0.9, modern configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.22.1&config=modern&openssl=3.0.9&guideline=5.7
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
{% else %}
# Ciphers with intermediate compatibility
# generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, intermediate configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=intermediate&openssl=1.1.1d&guideline=5.6
# generated 2023-06-13, Mozilla Guideline v5.7, nginx 1.22.1, OpenSSL 3.0.9, intermediate configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.22.1&config=intermediate&openssl=3.0.9&guideline=5.7
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# Pre-defined FFDHE group (RFC 7919)

View file

@ -6,7 +6,7 @@ map $http_upgrade $connection_upgrade {
server {
listen 80;
listen [::]:80;
server_name {{ domain }}{% if xmpp_enabled == "True" %} xmpp-upload.{{ domain }} muc.{{ domain }}{% endif %};
server_name {{ domain }};
access_by_lua_file /usr/share/ssowat/access.lua;
@ -78,48 +78,3 @@ server {
access_log /var/log/nginx/{{ domain }}-access.log;
error_log /var/log/nginx/{{ domain }}-error.log;
}
{% if xmpp_enabled == "True" %}
# vhost dedicated to XMPP http_upload
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name xmpp-upload.{{ domain }};
root /dev/null;
location /upload/ {
alias /var/xmpp-upload/{{ domain }}/upload/;
# Pass all requests to metronome, except for GET and HEAD requests.
limit_except GET HEAD {
proxy_pass http://localhost:5290;
}
include proxy_params;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'HEAD, GET, PUT, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization';
add_header 'Access-Control-Allow-Credentials' 'true';
client_max_body_size 105M; # Choose a value a bit higher than the max upload configured in XMPP server
}
include /etc/nginx/conf.d/security.conf.inc;
ssl_certificate /etc/yunohost/certs/{{ domain }}/crt.pem;
ssl_certificate_key /etc/yunohost/certs/{{ domain }}/key.pem;
{% if domain_cert_ca != "selfsigned" %}
more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
{% endif %}
{% if domain_cert_ca == "letsencrypt" %}
# OCSP settings
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/yunohost/certs/{{ domain }}/crt.pem;
resolver 1.1.1.1 9.9.9.9 valid=300s;
resolver_timeout 5s;
{% endif %}
access_log /var/log/nginx/xmpp-upload.{{ domain }}-access.log;
error_log /var/log/nginx/xmpp-upload.{{ domain }}-error.log;
}
{% endif %}

View file

@ -23,3 +23,24 @@ location = /yunohost/api/error/502 {
add_header Content-Type text/plain;
internal;
}
location /yunohost/portalapi/ {
proxy_read_timeout 5s;
proxy_pass http://127.0.0.1:6788/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# Custom 502 error page
error_page 502 /yunohost/portalapi/error/502;
}
# Yunohost admin output complete 502 error page, so use only plain text.
location = /yunohost/portalapi/error/502 {
return 502 '502 - Bad Gateway';
add_header Content-Type text/plain;
internal;
}

View file

@ -0,0 +1,28 @@
# Avoid the nginx path/alias traversal weakness ( #1037 )
rewrite ^/yunohost/sso$ /yunohost/sso/ permanent;
location /yunohost/sso/ {
alias /usr/share/yunohost/portal/;
default_type text/html;
index index.html;
try_files $uri $uri/ /index.html;
location = /yunohost/sso/index.html {
etag off;
expires off;
more_set_headers "Cache-Control: no-store, no-cache, must-revalidate";
}
location /yunohost/sso/applogos/ {
alias /usr/share/yunohost/applogos/;
}
location = /yunohost/sso/customassets/custom.css {
alias /usr/share/yunohost/portal/customassets/$host.custom.css;
etag off;
expires off;
more_set_headers "Cache-Control: no-store, no-cache, must-revalidate";
}
more_set_headers "Content-Security-Policy: upgrade-insecure-requests; default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none'; img-src 'self' data:;";
}

View file

@ -0,0 +1,31 @@
# General daemon config
Socket inet:8891@localhost
PidFile /run/opendkim/opendkim.pid
UserID opendkim
UMask 007
AutoRestart yes
AutoRestartCount 10
AutoRestartRate 10/1h
# Logging
Syslog yes
SyslogSuccess yes
LogWhy yes
# Common signing and verification parameters. In Debian, the "From" header is
# oversigned, because it is often the identity key used by reputation systems
# and thus somewhat security sensitive.
Canonicalization relaxed/simple
Mode sv
OversignHeaders From
#On-BadSignature reject
# Key / signing table
KeyTable file:/etc/dkim/keytable
SigningTable refile:/etc/dkim/signingtable
# The trust anchor enables DNSSEC. In Debian, the trust anchor file is provided
# by the package dns-root-data.
TrustAnchorFile /usr/share/dns/root.key
#Nameservers 127.0.0.1

View file

@ -30,8 +30,8 @@ smtpd_tls_chain_files =
tls_server_sni_maps = hash:/etc/postfix/sni
{% if compatibility == "intermediate" %}
# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, intermediate configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=intermediate&openssl=1.1.1d&guideline=5.6
# generated 2023-06-13, Mozilla Guideline v5.7, Postfix 3.7.5, OpenSSL 3.0.9, intermediate configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.7.5&config=intermediate&openssl=3.0.9&guideline=5.7
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
@ -41,10 +41,10 @@ smtpd_tls_mandatory_ciphers = medium
# not actually 1024 bits, this applies to all DHE >= 1024 bits
smtpd_tls_dh1024_param_file = /usr/share/yunohost/ffdhe2048.pem
tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
{% else %}
# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, modern configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=modern&openssl=1.1.1d&guideline=5.6
# generated 2023-06-13, Mozilla Guideline v5.7, Postfix 3.7.5, OpenSSL 3.0.9, modern configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.7.5&config=modern&openssl=3.0.9&guideline=5.7
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2
@ -182,9 +182,10 @@ smtp_header_checks = regexp:/etc/postfix/header_checks
smtp_reply_filter = pcre:/etc/postfix/smtp_reply_filter
# Rmilter
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen} {auth_type}
milter_protocol = 6
smtpd_milters = inet:localhost:11332
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
# Skip email without checking if milter has died
milter_default_action = accept

View file

@ -1,16 +0,0 @@
allow_envfrom_empty = true;
allow_hdrfrom_mismatch = false;
allow_hdrfrom_multiple = false;
allow_username_mismatch = true;
auth_only = true;
path = "/etc/dkim/$domain.$selector.key";
selector = "mail";
sign_local = true;
symbol = "DKIM_SIGNED";
try_fallback = true;
use_domain = "header";
use_esld = false;
use_redis = false;
key_prefix = "DKIM_KEYS";

View file

@ -1,8 +0,0 @@
# Metrics settings
# This define overridden options.
actions {
reject = 21;
add_header = 8;
greylist = 4;
}

View file

@ -1,9 +0,0 @@
use = ["spam-header"];
routines {
spam-header {
header = "X-Spam";
value = "Yes";
remove = 1;
}
}

View file

@ -1,2 +0,0 @@
# set redis server
servers = "127.0.0.1";

View file

@ -1,4 +0,0 @@
require ["fileinto"];
if header :is "X-Spam" "Yes" {
fileinto "Junk";
}

View file

@ -56,7 +56,6 @@ objectClass: groupOfNamesYnh
gidNumber: 4002
cn: all_users
permission: cn=mail.main,ou=permission,dc=yunohost,dc=org
permission: cn=xmpp.main,ou=permission,dc=yunohost,dc=org
dn: cn=visitors,ou=groups,dc=yunohost,dc=org
objectClass: posixGroup
@ -75,17 +74,6 @@ gidNumber: 5001
showTile: FALSE
authHeader: FALSE
dn: cn=xmpp.main,ou=permission,dc=yunohost,dc=org
groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org
cn: xmpp.main
objectClass: posixGroup
objectClass: permissionYnh
isProtected: TRUE
label: XMPP
gidNumber: 5002
showTile: FALSE
authHeader: FALSE
dn: cn=ssh.main,ou=permission,dc=yunohost,dc=org
cn: ssh.main
objectClass: posixGroup

View file

@ -192,7 +192,7 @@ authorityKeyIdentifier=keyid,issuer
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName=DNS:yunohost.org,DNS:www.yunohost.org,DNS:ns.yunohost.org,DNS:xmpp-upload.yunohost.org
subjectAltName=DNS:yunohost.org,DNS:www.yunohost.org,DNS:ns.yunohost.org
[ v3_ca ]

View file

@ -8,11 +8,6 @@ fail2ban:
log: /var/log/fail2ban.log
category: security
test_conf: fail2ban-server --test
metronome:
log: [/var/log/metronome/metronome.log,/var/log/metronome/metronome.err]
needs_exposed_ports: [5222, 5269]
category: xmpp
ignore_if_package_is_not_installed: metronome
mysql:
log: [/var/log/mysql.log,/var/log/mysql.err,/var/log/mysql/error.log]
actual_systemd_service: mariadb
@ -28,21 +23,22 @@ nginx:
# log: /var/log/php7.4-fpm.log
# test_conf: php-fpm7.4 --test
# category: web
opendkim:
category: email
test_conf: opendkim -n
postfix:
log: [/var/log/mail.log,/var/log/mail.err]
actual_systemd_service: postfix@-
needs_exposed_ports: [25, 587]
category: email
postgresql:
actual_systemd_service: 'postgresql@13-main'
actual_systemd_service: 'postgresql@15-main'
category: database
ignore_if_package_is_not_installed: postgresql-13
ignore_if_package_is_not_installed: postgresql-15
redis-server:
log: /var/log/redis/redis-server.log
category: database
rspamd:
log: /var/log/rspamd/rspamd.log
category: email
ignore_if_package_is_not_installed: redis-server
slapd:
category: database
test_conf: slapd -Tt
@ -51,6 +47,9 @@ ssh:
test_conf: sshd -t
needs_exposed_ports: [22]
category: admin
yunohost-portal-api:
log: /var/log/yunohost-portal-api.log
category: userportal
yunohost-api:
log: /var/log/yunohost/yunohost-api.log
category: admin
@ -60,21 +59,6 @@ yunohost-firewall:
category: security
yunomdns:
category: mdns
glances: null
nsswitch: null
ssl: null
yunohost: null
bind9: null
tahoe-lafs: null
memcached: null
udisks2: null
udisk-glue: null
amavis: null
postgrey: null
spamassassin: null
rmilter: null
php5-fpm: null
php7.0-fpm: null
php7.3-fpm: null
nslcd: null
avahi-daemon: null

View file

@ -0,0 +1,48 @@
[Unit]
Description=YunoHost Portal API
After=network.target
[Service]
User=ynh-portal
Group=ynh-portal
Type=simple
ExecStart=/usr/bin/yunohost-portal-api
Restart=always
RestartSec=5
TimeoutStopSec=30
# Sandboxing options to harden security
# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
RestrictNamespaces=yes
RestrictRealtime=yes
DevicePolicy=closed
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
ProtectSystem=full
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
LockPersonality=yes
SystemCallArchitectures=native
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap @cpu-emulation @privileged
# Denying access to capabilities that should not be relevant
# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html
CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT
CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM
CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE
CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG
[Install]
WantedBy=multi-user.target

62
debian/changelog vendored
View file

@ -1,3 +1,65 @@
yunohost (12.0.3) testing; urgency=low
- (sync bullseye changes since 12.0.2)
- apps: magically handle yarn as a regular package instead of an 'extra' repo now that yarn's repo is in the core ([#1888](http://github.com/YunoHost/yunohost/pull/1888))
- portalapi: we don't need absolute URLs for app logos ? (This ain't working when enabling the 'show other domains apps' because of CSP) (bc93a2e07)
- portalapi: fix portal_user_intro not being sent when authenticated, hence not displayed at all (cdf443c86)
- portal/domain settings: Improve explanation about search engine (24fb87725)
- portal/domain settings: Reduce theme list because there were too many (cf change in yunohost-portal) (9973cc703)
- portal/domain settings: add proper i18n string + help for new settings (831131476, ff0388556)
- domain settings: add a title to the Email section to have a separation w.r.t. the portal settings (279f33288)
- portal: fix extra app tiles not being displayed, gotta use the perm id as key, not just the app id (credit rodinux) (603c64e34)
- portal/sso: with the public app page, fix the root of the domain not redirecting to /yunohost/sso (a6b7ba843)
- portal: allow to configure custom CSS from the domain config panel (8f636561d)
- portal: change the way the new 'public apps' page in the portal is configured: add a simple bool toggle instead of having the 'public apps page' as a default app option, which allows to still configure a default app while the portal has the public apps page (748a20d86)
- ci: fix test_permission_propagation_on_ssowat, auth header tests (656e5c75d, 9e9313067, 44920d891)
- ci: optimizations, cleanups, partial refactor because of new CI image build process (fe9a4fba5, 059818254, a9e71e88d, 7f2da0af7, 94594e5a3, d639e1c42, 4f3b9df3f, 5a6a915af, 55e7e798f, 2fe24424f, 4fc929005, fd040b864, 0bbc14f54, 2976e7bf6)
- quality: add type hints to user.py (1ba75df0e, d4f39da20, 611846aa1, efce7f9f0, fe1c04fb2)
- i18n: Translations updated for Basque, French, Galician, Greek, Indonesian, Russian, Turkish
Thanks to all contributors <3 ! (Ali Çıır, cjdw, craftrac, Emmanuel Averty, Félix Piédallu, Ivan Davydov, José M, Josué Tille, ljf, OniriCorpe, ppr, selfhoster1312, Tagada, tituspijean, xabirequejo)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 31 Aug 2024 19:45:00 +0200
yunohost (12.0.2) testing; urgency=low
- Cleanup redis regen conf since redis ain't installed by default anymore (7b50c4eb6)
- bullseye->bookworm: add a trick to flag the migration as done if it's still marked as pending (0503a38a7)
- Sync with main branch
Thanks to all contributors <3 ! (Kayou)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 01 Aug 2024 18:08:33 +0200
yunohost (12.0.1) testing; urgency=low
- The user portal and SSO system have been reworked and split into three distinct pieces
- SSOwat only handling only the SSO/ACL logic (nginx lua middleware)
- A new “portal API” (yunohost-portal-api) service delivering authentication cookies and allowing users to retrieve/update infos
- A new portal front end (yunohost-portal)
- More information on the release note on the forum
- The base system does not install Mysql/Mariadb and PHP anymore
- Rspamd (antispam system) and Metronome (XMPP server) are not part of the core anymore. Instead, they are now separate applications : rspamd_ynh and metronome_ynh
- webadmin: rework cookie/session expiration mechanism. Cookies are now still valid after restarting the API (preventing clumsy disconnect during self-upgrades) and the cookie validity is automatically extended every time an API request is performed.
- mail: DKIM email signing is now done using opendkim instead of rspamd
- various compatibility tweakings for Bookworm
- regenconf: update nginx and dovecot ciphers according to Mozilla recommendation
- regenconf: update fail2ban config
- configpanels: refactor to use pydantic for more typing and consistency, add proper autogenerated doc
- apps: Yarn third-party repo is now available by default in apt config just like Sury, no need for an extra apt resource thingy
- various legacy cleanups (more info on the release note on the forum)
- perf: minimize regen-conf calls to yunohost settings get, and other misc lazy-loading optimizations
- quality: simplify the logging mess
- quality: rework ci tests workflow
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 26 Jul 2024 22:40:16 +0200
yunohost (12.0.0) unstable; urgency=low
- Tmp changelog to prepare Bookworm
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 04 May 2023 20:30:19 +0200
yunohost (11.2.30) stable; urgency=low
- helpers v2.1: check if patches dir exists before getting realpath ([#1938](http://github.com/YunoHost/yunohost/pull/1938))

40
debian/control vendored
View file

@ -2,21 +2,22 @@ Source: yunohost
Section: utils
Priority: extra
Maintainer: YunoHost Contributors <contrib@yunohost.org>
Build-Depends: debhelper (>=9), debhelper-compat (= 13), dh-python, python3-all (>= 3.7), python3-yaml, python3-jinja2
Build-Depends: debhelper (>=9), debhelper-compat (= 13), dh-python, python3-all (>= 3.11), python3-yaml, python3-jinja2 (>= 3.0)
Standards-Version: 3.9.6
Homepage: https://yunohost.org/
Package: yunohost
Essential: yes
Architecture: all
Depends: ${python3:Depends}, ${misc:Depends}
, moulinette (>= 11.1), moulinette (<< 12.0), ssowat (>= 11.1), ssowat (<< 12.0)
Depends: python3-all (>= 3.11),
, moulinette (>= 12.0), ssowat (>= 12.0),
, python3-psutil, python3-requests, python3-dnspython, python3-openssl
, python3-miniupnpc, python3-dbus, python3-jinja2
, python3-miniupnpc, python3-dbus, python3-jinja2 (>= 3.0)
, python3-toml, python3-packaging, python3-publicsuffix2
, python3-ldap, python3-zeroconf (>= 0.36), python3-lexicon,
, python-is-python3
, nginx, nginx-extras (>=1.18)
, python3-ldap, python3-zeroconf (>= 0.47), python3-lexicon,
, python3-cryptography, python3-jwt, python3-passlib, python3-magic
, python-is-python3, python3-pydantic, python3-email-validator
, nginx, nginx-extras (>=1.22)
, apt, apt-transport-https, apt-utils, aptitude, dirmngr
, openssh-server, iptables, fail2ban, bind9-dnsutils
, openssl, ca-certificates, netcat-openbsd, iproute2
@ -24,31 +25,26 @@ Depends: ${python3:Depends}, ${misc:Depends}
, dnsmasq, resolvconf, libnss-myhostname
, postfix, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre
, dovecot-core, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved, dovecot-antispam
, rspamd, opendkim-tools, postsrsd, procmail, mailutils
, redis-server
, opendkim-tools, opendkim, postsrsd, procmail, mailutils
, acl
, git, curl, wget, cron, unzip, jq, bc, at, procps, j2cli
, lsb-release, haveged, fake-hwclock, lsof, whois
Recommends: yunohost-admin
Recommends: yunohost-admin, yunohost-portal (>= 12.0)
, ntp, inetutils-ping | iputils-ping
, bash-completion, rsyslog
, php7.4-common, php7.4-fpm, php7.4-ldap, php7.4-intl
, mariadb-server, php7.4-mysql
, php7.4-gd, php7.4-curl, php-php-gettext
, python3-pip
, unattended-upgrades
, libdbd-ldap-perl, libnet-dns-perl
, metronome (>=3.14.0)
Conflicts: iptables-persistent
, apache2
, bind9
, nginx-extras (>= 1.19)
, openssl (>= 3.0)
, slapd (>= 2.4.58)
, dovecot-core (>= 1:2.3.14)
, redis-server (>= 5:6.1)
, fail2ban (>= 0.11.3)
, iptables (>= 1.8.8)
, openresolv
, systemd-resolved
, nginx-extras (>= 1.23)
, openssl (>= 3.1)
, slapd (>= 2.6)
, dovecot-core (>= 1:2.4)
, fail2ban (>= 1.1)
, iptables (>= 1.8.10)
Description: manageable and configured self-hosting server
YunoHost aims to make self-hosting accessible to everyone. It configures
an email, Web and IM server alongside a LDAP base. It also provides

1
debian/install vendored
View file

@ -6,5 +6,4 @@ conf/* /usr/share/yunohost/conf/
locales/* /usr/share/yunohost/locales/
doc/yunohost.8.gz /usr/share/man/man8/
doc/bash_completion.d/* /etc/bash_completion.d/
conf/metronome/modules/* /usr/lib/metronome/modules/
src/* /usr/lib/python3/dist-packages/yunohost/

6
debian/postinst vendored
View file

@ -4,6 +4,10 @@ set -e
do_configure() {
mkdir -p /etc/yunohost
mkdir -p /etc/yunohost/apps
mkdir -p /etc/yunohost/portal
if [ ! -f /etc/yunohost/installed ]; then
# If apps/ is not empty, we're probably already installed in the past and
# something funky happened ...
@ -33,6 +37,8 @@ do_configure() {
yunohost tools update apps --output-as none || true
fi
systemctl restart yunohost-portal-api
# Trick to let yunohost handle the restart of the API,
# to prevent the webadmin from cutting the branch it's sitting on
if systemctl is-enabled yunohost-api --quiet

View file

@ -0,0 +1,181 @@
import ast
import datetime
import subprocess
version = open("../debian/changelog").readlines()[0].split()[1].strip("()")
today = datetime.datetime.now().strftime("%d/%m/%Y")
def get_current_commit():
p = subprocess.Popen(
"git rev-parse --verify HEAD",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
stdout, stderr = p.communicate()
current_commit = stdout.strip().decode("utf-8")
return current_commit
current_commit = get_current_commit()
def print_config_panel_docs():
fname = "../src/utils/configpanel.py"
content = open(fname).read()
# NB: This magic is because we want to be able to run this script outside of a YunoHost context,
# in which we cant really 'import' the file because it will trigger a bunch of moulinette/yunohost imports...
tree = ast.parse(content)
ConfigPanelClasses = reversed(
[
c
for c in tree.body
if isinstance(c, ast.ClassDef)
and c.name in {"SectionModel", "PanelModel", "ConfigPanelModel"}
]
)
print("## Configuration panel structure")
for c in ConfigPanelClasses:
doc = ast.get_docstring(c)
print("")
print(f"### {c.name.replace('Model', '')}")
print("")
print(doc)
print("")
print("---")
def print_form_doc():
fname = "../src/utils/form.py"
content = open(fname).read()
# NB: This magic is because we want to be able to run this script outside of a YunoHost context,
# in which we cant really 'import' the file because it will trigger a bunch of moulinette/yunohost imports...
tree = ast.parse(content)
OptionClasses = [
c
for c in tree.body
if isinstance(c, ast.ClassDef) and c.name.endswith("Option")
]
OptionDocString = {}
print("## List of all option types")
for c in OptionClasses:
if not isinstance(c.body[0], ast.Expr):
continue
option_type = None
if c.name in {"BaseOption", "BaseInputOption"}:
option_type = c.name
elif c.body[1].target.id == "type":
option_type = c.body[1].value.attr
generaltype = (
c.bases[0].id.replace("Option", "").replace("Base", "").lower()
if c.bases
else None
)
docstring = ast.get_docstring(c)
if docstring:
if "#### Properties" not in docstring:
docstring += """
#### Properties
- [common properties](#common-properties)"""
OptionDocString[option_type] = {
"doc": docstring,
"generaltype": generaltype,
}
# Dirty hack to have "BaseOption" as first and "BaseInputOption" as 2nd in list
base = OptionDocString.pop("BaseOption")
baseinput = OptionDocString.pop("BaseInputOption")
OptionDocString2 = {
"BaseOption": base,
"BaseInputOption": baseinput,
}
OptionDocString2.update(OptionDocString)
for option_type, infos in OptionDocString2.items():
if option_type == "display_text":
# display_text is kind of legacy x_x
continue
print("")
if option_type == "BaseOption":
print("### Common properties")
elif option_type == "BaseInputOption":
print("### Common inputs properties")
else:
print(
f"### `{option_type}`"
+ (f" ({infos['generaltype']})" if infos["generaltype"] else "")
)
print("")
print(infos["doc"])
print("")
print("---")
print(
rf"""---
title: Technical details for config panel structure and form option types
template: docs
taxonomy:
category: docs
routes:
default: '/dev/forms'
---
Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{current_commit}/doc/generate_options_doc.py) on {today} (YunoHost version {version})
## Glossary
You may encounter some named types which are used for simplicity.
- `Translation`: a translated property
- used for properties: `ask`, `help` and `Pattern.error`
- a `dict` with locales as keys and translations as values:
```toml
ask.en = "The text in english"
ask.fr = "Le texte en français"
```
It is not currently possible for translators to translate those string in weblate.
- a single `str` for a single english default string
```toml
help = "The text in english"
```
- `JSExpression`: a `str` JS expression to be evaluated to `true` or `false`:
- used for properties: `visible` and `enabled`
- operators availables: `==`, `!=`, `>`, `>=`, `<`, `<=`, `!`, `&&`, `||`, `+`, `-`, `*`, `/`, `%` and `match()`
- `Binding`: bind a value to a file/property/variable/getter/setter/validator
- save the value in `settings.yaml` when not defined
- nothing at all with `"null"`
- a custom getter/setter/validator with `"null"` + a function starting with `get__`, `set__`, `validate__` in `scripts/config`
- a variable/property in a file with `:__FINALPATH__/my_file.php`
- a whole file with `__FINALPATH__/my_file.php`
- `Pattern`: a `dict` with a regex to match the value against and an error message
```toml
pattern.regexp = '^[A-F]\d\d$'
pattern.error = "Provide a room number such as F12: one uppercase and 2 numbers"
# or with translated error
pattern.error.en = "Provide a room number such as F12: one uppercase and 2 numbers"
pattern.error.fr = "Entrez un numéro de salle comme F12: une lettre majuscule et deux chiffres."
```
- IMPORTANT: your `pattern.regexp` should be between simple quote, not double.
"""
)
print_config_panel_docs()
print_form_doc()

View file

@ -0,0 +1,4 @@
from yunohost.utils.configpanel import ConfigPanelModel
print(ConfigPanelModel.schema_json(indent=2))

View file

@ -113,7 +113,7 @@ ignoreregex =
chown -R "$app:$app" "/var/log/$app"
chmod -R u=rwX,g=rX,o= "/var/log/$app"
ynh_systemd_action --service_name=fail2ban --action=reload --line_match="(Started|Reloaded) Fail2Ban Service" --log_path=systemd
ynh_systemd_action --service_name=fail2ban --action=reload --line_match="(Started|Reloaded) fail2ban.service" --log_path=systemd
local fail2ban_error="$(journalctl --no-hostname --unit=fail2ban | tail --lines=50 | grep "WARNING.*$app.*")"
if [[ -n "$fail2ban_error" ]]; then

View file

@ -227,6 +227,9 @@ ynh_mysql_setup_db() {
# If $db_pwd is not provided, use new_db_pwd instead for db_pwd
db_pwd="${db_pwd:-$new_db_pwd}"
# Dirty patch for super-legacy apps
dpkg --list | grep -q "^ii mariadb-server" || { ynh_print_warn "Packager: you called ynh_mysql_setup_db without declaring a dependency to mariadb-server. Please add it to your apt dependencies !"; ynh_apt install mariadb-server; }
ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd"
ynh_app_setting_set --app=$app --key=mysqlpwd --value=$db_pwd
}

View file

@ -1,6 +1,6 @@
#!/bin/bash
readonly YNH_DEFAULT_PHP_VERSION=7.4
readonly YNH_DEFAULT_PHP_VERSION=8.2
# Declare the actual PHP version to use.
# A packager willing to use another version of PHP can override the variable into its _common.sh.
YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION}
@ -70,17 +70,14 @@ YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION}
ynh_add_fpm_config() {
local _globalphpversion=${phpversion-:}
# Declare an array to define the options of this helper.
local legacy_args=vufpdg
local -A args_array=([v]=phpversion= [u]=usage= [f]=footprint= [p]=package= [d]=dedicated_service [g]=group=)
local legacy_args=vufg
local -A args_array=([v]=phpversion= [u]=usage= [f]=footprint= [g]=group=)
local group
local phpversion
local usage
local footprint
local package
local dedicated_service
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
package=${package:-}
group=${group:-}
# The default behaviour is to use the template.
@ -105,8 +102,6 @@ ynh_add_fpm_config() {
fi
fi
# Do not use a dedicated service by default
dedicated_service=${dedicated_service:-0}
# Set the default PHP-FPM version by default
if dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} lt 2; then
@ -129,45 +124,16 @@ ynh_add_fpm_config() {
fi
fi
# Legacy args (packager should just list their php dependency as regular apt dependencies...
if [ -n "$package" ]; then
# Install the additionnal packages from the default repository
ynh_print_warn --message "Argument --package of ynh_add_fpm_config is deprecated and to be removed in the future"
ynh_install_app_dependencies "$package"
fi
if [ $dedicated_service -eq 1 ]; then
ynh_print_warn --message "Argument --dedicated_service of ynh_add_fpm_config is deprecated and to be removed in the future"
local fpm_service="${app}-phpfpm"
local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm"
else
local fpm_service="php${phpversion}-fpm"
local fpm_config_dir="/etc/php/$phpversion/fpm"
fi
local fpm_service="php${phpversion}-fpm"
local fpm_config_dir="/etc/php/$phpversion/fpm"
# Create the directory for FPM pools
mkdir --parents "$fpm_config_dir/pool.d"
ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir"
ynh_app_setting_set --app=$app --key=fpm_service --value="$fpm_service"
ynh_app_setting_set --app=$app --key=fpm_dedicated_service --value="$dedicated_service"
ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion
# Migrate from mutual PHP service to dedicated one.
if [ $dedicated_service -eq 1 ]; then
local old_fpm_config_dir="/etc/php/$phpversion/fpm"
# If a config file exist in the common pool, move it.
if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ]; then
ynh_print_info --message="Migrate to a dedicated php-fpm service for $app."
# Create a backup of the old file before migration
ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf"
# Remove the old PHP config file
ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf"
# Reload PHP to release the socket and allow the dedicated service to use it
ynh_systemd_action --service_name=php${phpversion}-fpm --action=reload
fi
fi
if [ $autogenconf == "false" ]; then
# Usage 1, use the template in conf/php-fpm.conf
local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf"
@ -221,56 +187,14 @@ pm.process_idle_timeout = 10s
local finalphpconf="$fpm_config_dir/pool.d/$app.conf"
ynh_add_config --template="$phpfpm_path" --destination="$finalphpconf"
if [ -e "$YNH_APP_BASEDIR/conf/php-fpm.ini" ]; then
ynh_print_warn --message="Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead."
ynh_add_config --template="php-fpm.ini" --destination="$fpm_config_dir/conf.d/20-$app.ini"
# Validate that the new php conf doesn't break php-fpm entirely
if ! php-fpm${phpversion} --test 2>/dev/null; then
php-fpm${phpversion} --test || true
ynh_secure_remove --file="$finalphpconf"
ynh_die --message="The new configuration broke php-fpm?"
fi
if [ $dedicated_service -eq 1 ]; then
# Create a dedicated php-fpm.conf for the service
local globalphpconf=$fpm_config_dir/php-fpm-$app.conf
echo "[global]
pid = /run/php/php__PHPVERSION__-fpm-__APP__.pid
error_log = /var/log/php/fpm-php.__APP__.log
syslog.ident = php-fpm-__APP__
include = __FINALPHPCONF__
" > $YNH_APP_BASEDIR/conf/php-fpm-$app.conf
ynh_add_config --template="php-fpm-$app.conf" --destination="$globalphpconf"
# Create a config for a dedicated PHP-FPM service for the app
echo "[Unit]
Description=PHP __PHPVERSION__ FastCGI Process Manager for __APP__
After=network.target
[Service]
Type=notify
PIDFile=/run/php/php__PHPVERSION__-fpm-__APP__.pid
ExecStart=/usr/sbin/php-fpm__PHPVERSION__ --nodaemonize --fpm-config __GLOBALPHPCONF__
ExecReload=/bin/kill -USR2 \$MAINPID
[Install]
WantedBy=multi-user.target
" > $YNH_APP_BASEDIR/conf/$fpm_service
# Create this dedicated PHP-FPM service
ynh_add_systemd_config --service=$fpm_service --template=$fpm_service
# Integrate the service in YunoHost admin panel
yunohost service add $fpm_service --log /var/log/php/fpm-php.$app.log --description "Php-fpm dedicated to $app"
# Configure log rotate
ynh_use_logrotate --logfile=/var/log/php
# Restart the service, as this service is either stopped or only for this app
ynh_systemd_action --service_name=$fpm_service --action=restart
else
# Validate that the new php conf doesn't break php-fpm entirely
if ! php-fpm${phpversion} --test 2> /dev/null; then
php-fpm${phpversion} --test || true
ynh_secure_remove --file="$finalphpconf"
ynh_die --message="The new configuration broke php-fpm?"
fi
ynh_systemd_action --service_name=$fpm_service --action=reload
fi
ynh_systemd_action --service_name=$fpm_service --action=reload
}
# Remove the dedicated PHP-FPM config
@ -281,8 +205,6 @@ WantedBy=multi-user.target
ynh_remove_fpm_config() {
local fpm_config_dir=$(ynh_app_setting_get --app=$app --key=fpm_config_dir)
local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service)
local dedicated_service=$(ynh_app_setting_get --app=$app --key=fpm_dedicated_service)
dedicated_service=${dedicated_service:-0}
# Get the version of PHP used by this app
local phpversion=$(ynh_app_setting_get --app=$app --key=phpversion)
@ -296,69 +218,7 @@ ynh_remove_fpm_config() {
fi
ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf"
if [ -e $fpm_config_dir/conf.d/20-$app.ini ]; then
ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini"
fi
if [ $dedicated_service -eq 1 ]; then
# Remove the dedicated service PHP-FPM service for the app
ynh_remove_systemd_config --service=$fpm_service
# Remove the global PHP-FPM conf
ynh_secure_remove --file="$fpm_config_dir/php-fpm-$app.conf"
# Remove the service from the list of services known by YunoHost
yunohost service remove $fpm_service
elif ynh_package_is_installed --package="php${phpversion}-fpm"; then
ynh_systemd_action --service_name=$fpm_service --action=reload
fi
# If the PHP version used is not the default version for YunoHost
# The second part with YNH_APP_PURGE is an ugly hack to guess that we're inside the remove script
# (we don't actually care about its value, we just check its not empty hence it exists)
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ] && [ -n "${YNH_APP_PURGE:-}" ] && dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} lt 2; then
# Remove app dependencies ... but ideally should happen via an explicit call from packager
ynh_remove_app_dependencies
fi
}
# Install another version of PHP.
#
# [internal]
#
# Legacy, to be remove on bullseye
#
# usage: ynh_install_php --phpversion=phpversion [--package=packages]
# | arg: -v, --phpversion= - Version of PHP to install.
# | arg: -p, --package= - Additionnal PHP packages to install
#
# Requires YunoHost version 3.8.1 or higher.
ynh_install_php() {
# Declare an array to define the options of this helper.
local legacy_args=vp
local -A args_array=([v]=phpversion= [p]=package=)
local phpversion
local package
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
package=${package:-}
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]; then
ynh_die --message="Do not use ynh_install_php to install php$YNH_DEFAULT_PHP_VERSION"
fi
ynh_install_app_dependencies "$package"
}
# Remove the specific version of PHP used by the app.
#
# [internal]
#
# Legacy, to be remove on bullseye
#
# usage: ynh_remove_php
#
# Requires YunoHost version 3.8.1 or higher.
ynh_remove_php() {
ynh_remove_app_dependencies
ynh_systemd_action --service_name=$fpm_service --action=reload
}
# Define the values to configure PHP-FPM

View file

@ -1,7 +1,7 @@
#!/bin/bash
PSQL_ROOT_PWD_FILE=/etc/yunohost/psql
PSQL_VERSION=13
PSQL_VERSION=15
# Open a connection as a user
#

View file

@ -18,11 +18,7 @@ ynh_app_setting_get() {
ynh_handle_getopts_args "$@"
app="${app:-$_globalapp}"
if [[ $key =~ (unprotected|protected|skipped)_ ]]; then
yunohost app setting $app $key
else
ynh_app_setting "get" "$app" "$key"
fi
ynh_app_setting "get" "$app" "$key"
}
# Set an application setting
@ -45,11 +41,7 @@ ynh_app_setting_set() {
ynh_handle_getopts_args "$@"
app="${app:-$_globalapp}"
if [[ $key =~ (unprotected|protected|skipped)_ ]]; then
yunohost app setting $app $key -v $value
else
ynh_app_setting "set" "$app" "$key" "$value"
fi
ynh_app_setting "set" "$app" "$key" "$value"
}
# Set an application setting but only if the "$key" variable ain't set yet
@ -106,11 +98,7 @@ ynh_app_setting_delete() {
ynh_handle_getopts_args "$@"
app="${app:-$_globalapp}"
if [[ "$key" =~ (unprotected|skipped|protected)_ ]]; then
yunohost app setting $app $key -d
else
ynh_app_setting "delete" "$app" "$key"
fi
ynh_app_setting "delete" "$app" "$key"
}
# Small "hard-coded" interface to avoid calling "yunohost app" directly each

View file

@ -367,15 +367,11 @@ ynh_compare_current_package_version() {
_ynh_apply_default_permissions() {
local target=$1
local ynh_requirement=$(ynh_read_manifest --manifest_key="requirements.yunohost" | tr -d '<>= ')
if dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} ge 2 || [ -z "$ynh_requirement" ] || [ "$ynh_requirement" == "null" ] || dpkg --compare-versions $ynh_requirement ge 4.2; then
chmod o-rwx $target
chmod g-w $target
chown -R root:root $target
if ynh_system_user_exists $app; then
chown $app:$app $target
fi
chmod o-rwx $target
chmod g-w $target
chown -R root:root $target
if ynh_system_user_exists $app; then
chown $app:$app $target
fi
# Crons should be owned by root
@ -388,7 +384,7 @@ _ynh_apply_default_permissions() {
}
int_to_bool() {
sed -e 's/^1$/True/g' -e 's/^0$/False/g'
sed -e 's/^1$/True/g' -e 's/^0$/False/g' -e 's/^true$/True/g' -e 's/^false$/False/g'
}
toml_to_json() {

View file

@ -1,13 +0,0 @@
#!/bin/bash
# Exit hook on subcommand error or unset variable
set -eu
# Source YNH helpers
source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/data/xmpp"
ynh_backup /var/lib/metronome "${backup_dir}/var_lib_metronome"
ynh_backup /var/xmpp-upload/ "${backup_dir}/var_xmpp-upload"

View file

@ -2,15 +2,167 @@
set -e
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
base_folder_and_perm_init() {
#############################
# Base yunohost conf folder #
#############################
mkdir -p /etc/yunohost
# NB: x permission for 'others' is important for ssl-cert (and maybe mdns), otherwise slapd will fail to start because can't access the certs
chmod 755 /etc/yunohost
################
# Logs folders #
################
mkdir -p /var/log/yunohost
chown root:root /var/log/yunohost
chmod 750 /var/log/yunohost
##################
# Portal folders #
##################
getent passwd ynh-portal &>/dev/null || useradd --no-create-home --shell /usr/sbin/nologin --system --user-group ynh-portal
mkdir -p /etc/yunohost/portal
chmod 500 /etc/yunohost/portal
chown ynh-portal:ynh-portal /etc/yunohost/portal
mkdir -p /usr/share/yunohost/portal/customassets
chmod 775 /usr/share/yunohost/portal/customassets
chown root:root /usr/share/yunohost/portal/customassets
touch /var/log/yunohost-portalapi.log
chown ynh-portal:root /var/log/yunohost-portalapi.log
chmod 600 /var/log/yunohost-portalapi.log
###############################
# Sessions folder and secrets #
###############################
# Portal
mkdir -p /var/cache/yunohost-portal/sessions
chown ynh-portal:www-data /var/cache/yunohost-portal
chmod 510 /var/cache/yunohost-portal
chown ynh-portal:www-data /var/cache/yunohost-portal/sessions
chmod 710 /var/cache/yunohost-portal/sessions
# Webadmin
mkdir -p /var/cache/yunohost/sessions
chown root:root /var/cache/yunohost/sessions
chmod 700 /var/cache/yunohost/sessions
if test -e /etc/yunohost/installed
then
# Initialize session secrets
# Obviously we only do this in the post_regen, ie during the postinstall, because we don't want every pre-installed instance to have the same secret
if [ ! -e /etc/yunohost/.admin_cookie_secret ]; then
dd if=/dev/urandom bs=1 count=1000 2>/dev/null | tr --complement --delete 'A-Za-z0-9' | head -c 64 > /etc/yunohost/.admin_cookie_secret
fi
chown root:root /etc/yunohost/.admin_cookie_secret
chmod 400 /etc/yunohost/.admin_cookie_secret
if [ ! -e /etc/yunohost/.ssowat_cookie_secret ]; then
# NB: we need this to be exactly 32 char long, because it is later used as a key for AES256
dd if=/dev/urandom bs=1 count=1000 2>/dev/null | tr --complement --delete 'A-Za-z0-9' | head -c 32 > /etc/yunohost/.ssowat_cookie_secret
fi
chown ynh-portal:root /etc/yunohost/.ssowat_cookie_secret
chmod 400 /etc/yunohost/.ssowat_cookie_secret
fi
##################
# Domain folders #
##################
mkdir -p /etc/yunohost/domains
chown root /etc/yunohost/domains
chmod 700 /etc/yunohost/domains
###############
# App folders #
###############
mkdir -p /etc/yunohost/apps
chown root /etc/yunohost/apps
chmod 700 /etc/yunohost/apps
#####################
# Apps data folders #
#####################
mkdir -p /home/yunohost.app
chmod 755 /home/yunohost.app
################
# Certs folder #
################
mkdir -p /etc/yunohost/certs
chown -R root:ssl-cert /etc/yunohost/certs
chmod 750 /etc/yunohost/certs
# We do this with find because there could be a lot of them...
find /etc/yunohost/certs/ -type f -exec chmod 640 {} \;
find /etc/yunohost/certs/ -type d -exec chmod 750 {} \;
##################
# Backup folders #
##################
mkdir -p /home/yunohost.backup/archives
chmod 770 /home/yunohost.backup
chmod 770 /home/yunohost.backup/archives
if test -e /etc/yunohost/installed
then
# The admins group only exist after the postinstall
chown root:admins /home/yunohost.backup
chown root:admins /home/yunohost.backup/archives
else
chown root:root /home/yunohost.backup
chown root:root /home/yunohost.backup/archives
fi
########
# Misc #
########
mkdir -p /etc/yunohost/hooks.d
chown root /etc/yunohost/hooks.d
chmod 700 /etc/yunohost/hooks.d
mkdir -p /var/cache/yunohost/repo
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
[ ! -e /var/www/.well-known/ynh-diagnosis/ ] || chmod 775 /var/www/.well-known/ynh-diagnosis/
if test -e /etc/yunohost/installed
then
setfacl -m g:all_users:--- /var/www
setfacl -m g:all_users:--- /var/log/nginx
setfacl -m g:all_users:--- /etc/yunohost
setfacl -m g:all_users:--- /etc/ssowat
fi
}
do_init_regen() {
cd /usr/share/yunohost/conf/yunohost
[[ -d /etc/yunohost ]] || mkdir -p /etc/yunohost
base_folder_and_perm_init
# Empty ssowat json persistent conf
echo "{}" >'/etc/ssowat/conf.json.persistent'
chmod 644 /etc/ssowat/conf.json.persistent
chown root:root /etc/ssowat/conf.json.persistent
echo "{}" >'/etc/ssowat/conf.json'
chmod 644 /etc/ssowat/conf.json
chown root:root /etc/ssowat/conf.json
# Empty service conf
touch /etc/yunohost/services.yml
# set default current_host
[[ -f /etc/yunohost/current_host ]] \
@ -24,39 +176,9 @@ do_init_regen() {
[[ -d /etc/skel/media ]] \
|| (mkdir -p /media && ln -s /media /etc/skel/media)
# Cert folders
mkdir -p /etc/yunohost/certs
chown -R root:ssl-cert /etc/yunohost/certs
chmod 750 /etc/yunohost/certs
# App folders
mkdir -p /etc/yunohost/apps
chmod 700 /etc/yunohost/apps
mkdir -p /home/yunohost.app
chmod 755 /home/yunohost.app
# Domain settings
mkdir -p /etc/yunohost/domains
chmod 700 /etc/yunohost/domains
# Backup folders
mkdir -p /home/yunohost.backup/archives
chmod 750 /home/yunohost.backup/archives
chown root:root /home/yunohost.backup/archives # This is later changed to root:admins once the admins group exists
# Empty ssowat json persistent conf
echo "{}" > '/etc/ssowat/conf.json.persistent'
chmod 644 /etc/ssowat/conf.json.persistent
chown root:root /etc/ssowat/conf.json.persistent
# Empty service conf
touch /etc/yunohost/services.yml
mkdir -p /var/cache/yunohost/repo
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
# YunoHost services
cp yunohost-api.service /etc/systemd/system/yunohost-api.service
cp yunohost-portal-api.service /etc/systemd/system/yunohost-portal-api.service
cp yunohost-firewall.service /etc/systemd/system/yunohost-firewall.service
cp yunoprompt.service /etc/systemd/system/yunoprompt.service
@ -65,6 +187,9 @@ do_init_regen() {
systemctl enable yunohost-api.service --quiet
systemctl start yunohost-api.service
systemctl enable yunohost-portal-api.service --quiet
systemctl start yunohost-portal-api.service
# Enable yunoprompt (in particular for installs from ISO where we want this to show on first boot instead of asking for a login/password)
systemctl enable yunoprompt --quiet
@ -157,6 +282,7 @@ HandleLidSwitchExternalPower=ignore
EOF
cp yunohost-api.service ${pending_dir}/etc/systemd/system/yunohost-api.service
cp yunohost-portal-api.service ${pending_dir}/etc/systemd/system/yunohost-portal-api.service
cp yunohost-firewall.service ${pending_dir}/etc/systemd/system/yunohost-firewall.service
cp yunoprompt.service ${pending_dir}/etc/systemd/system/yunoprompt.service
cp proc-hidepid.service ${pending_dir}/etc/systemd/system/proc-hidepid.service
@ -171,62 +297,45 @@ EOF
do_post_regen() {
regen_conf_files=$1
######################
# Enfore permissions #
######################
# Re-mkdir / apply permission to all basic folders etc
base_folder_and_perm_init
chmod 770 /home/yunohost.backup
chmod 770 /home/yunohost.backup/archives
chmod 700 /var/cache/yunohost
chown root:admins /home/yunohost.backup
chown root:admins /home/yunohost.backup/archives
chown root:root /var/cache/yunohost
[ ! -e /var/www/.well-known/ynh-diagnosis/ ] || chmod 775 /var/www/.well-known/ynh-diagnosis/
# NB: x permission for 'others' is important for ssl-cert (and maybe mdns), otherwise slapd will fail to start because can't access the certs
chmod 755 /etc/yunohost
# Legacy log tree structure
if [ ! -e /var/log/yunohost/operations ]
then
mkdir -p /var/log/yunohost/operations
fi
if [ -d /var/log/yunohost/categories/operation ] && [ ! -L /var/log/yunohost/categories/operation ]
then
# (we use find -type f instead of mv /folder/* to make sure to also move hidden files which are not included in globs by default)
find /var/log/yunohost/categories/operation/ -type f -print0 | xargs -0 -I {} mv {} /var/log/yunohost/operations/
# Attempt to delete the old dir (because we want it to be a symlink) or just rename it if it can't be removed (not empty) for some reason
rmdir /var/log/yunohost/categories/operation || mv /var/log/yunohost/categories/operation /var/log/yunohost/categories/operation.old
ln -s /var/log/yunohost/operations /var/log/yunohost/categories/operation
fi
# Make sure conf files why may be created by apps are owned and writable only by root
find /etc/systemd/system/*.service -type f | xargs -r chown root:root
find /etc/systemd/system/*.service -type f | xargs -r chmod 0644
if ls -l /etc/php/*/fpm/pool.d/*.conf; then
if ls -l /etc/php/*/fpm/pool.d/*.conf 2>/dev/null; then
chown root:root /etc/php/*/fpm/pool.d/*.conf
chmod 644 /etc/php/*/fpm/pool.d/*.conf
fi
# Certs
# We do this with find because there could be a lot of them...
chown -R root:ssl-cert /etc/yunohost/certs
chmod 750 /etc/yunohost/certs
find /etc/yunohost/certs/ -type f -exec chmod 640 {} \;
find /etc/yunohost/certs/ -type d -exec chmod 750 {} \;
find /etc/cron.*/yunohost-* -type f -exec chmod 755 {} \;
find /etc/cron.d/yunohost-* -type f -exec chmod 644 {} \;
find /etc/cron.*/yunohost-* -type f -exec chown root:root {} \;
setfacl -m g:all_users:--- /var/www
setfacl -m g:all_users:--- /var/log/nginx
setfacl -m g:all_users:--- /etc/yunohost
setfacl -m g:all_users:--- /etc/ssowat
for USER in $(yunohost user list --quiet --output-as json | jq -r '.users | .[] | .username'); do
[ ! -e "/home/$USER" ] || setfacl -m g:all_users:--- /home/$USER
done
# Domain settings
mkdir -p /etc/yunohost/domains
# Misc configuration / state files
chown root:root $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2> /dev/null | grep -vw mdns.yml)
chmod 600 $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2> /dev/null)
# Apps folder, custom hooks folder
[[ ! -e /etc/yunohost/hooks.d ]] || (chown root /etc/yunohost/hooks.d && chmod 700 /etc/yunohost/hooks.d)
[[ ! -e /etc/yunohost/apps ]] || (chown root /etc/yunohost/apps && chmod 700 /etc/yunohost/apps)
[[ ! -e /etc/yunohost/domains ]] || (chown root /etc/yunohost/domains && chmod 700 /etc/yunohost/domains)
# Create ssh.app and sftp.app groups if they don't exist yet
grep -q '^ssh.app:' /etc/group || groupadd ssh.app
grep -q '^sftp.app:' /etc/group || groupadd sftp.app
@ -238,6 +347,7 @@ do_post_regen() {
systemctl restart ntp
}
fi
[[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || systemctl daemon-reload
[[ ! "$regen_conf_files" =~ "login.conf.d/ynh-override.conf" ]] || {
systemctl daemon-reload
@ -245,6 +355,7 @@ do_post_regen() {
}
[[ ! "$regen_conf_files" =~ "yunohost-firewall.service" ]] || systemctl daemon-reload
[[ ! "$regen_conf_files" =~ "yunohost-api.service" ]] || systemctl daemon-reload
[[ ! "$regen_conf_files" =~ "yunohost-portal-api.service" ]] || systemctl daemon-reload
if [[ "$regen_conf_files" =~ "yunoprompt.service" ]]; then
systemctl daemon-reload
@ -257,6 +368,9 @@ do_post_regen() {
systemctl $action proc-hidepid --quiet --now
fi
systemctl enable yunohost-portal-api.service --quiet
systemctl is-active yunohost-portal-api --quiet || systemctl start yunohost-portal-api.service
# Change dpkg vendor
# see https://wiki.debian.org/Derivatives/Guidelines#Vendor
if readlink -f /etc/dpkg/origins/default | grep -q debian; then

View file

@ -9,17 +9,16 @@ do_pre_regen() {
cd /usr/share/yunohost/conf/ssh
# Support different strategy for security configurations
export compatibility="$(jq -r '.ssh_compatibility' <<< "$YNH_SETTINGS")"
export port="$(jq -r '.ssh_port' <<< "$YNH_SETTINGS")"
export password_authentication="$(jq -r '.ssh_password_authentication' <<< "$YNH_SETTINGS" | int_to_bool)"
export ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null || true)
# do not listen to IPv6 if unavailable
[[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false
ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2> /dev/null || true)
# Support different strategy for security configurations
export compatibility="$(yunohost settings get 'security.ssh.ssh_compatibility')"
export port="$(yunohost settings get 'security.ssh.ssh_port')"
export password_authentication="$(yunohost settings get 'security.ssh.ssh_password_authentication' | int_to_bool)"
export ssh_keys
export ipv6_enabled
ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config"
}

View file

@ -8,10 +8,6 @@ config="/usr/share/yunohost/conf/slapd/config.ldif"
db_init="/usr/share/yunohost/conf/slapd/db_init.ldif"
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
do_pre_regen ""

View file

@ -2,7 +2,7 @@
set -e
readonly YNH_DEFAULT_PHP_VERSION=7.4
readonly YNH_DEFAULT_PHP_VERSION=8.2
do_pre_regen() {
pending_dir=$1
@ -11,7 +11,7 @@ do_pre_regen() {
# Add sury
mkdir -p ${pending_dir}/etc/apt/sources.list.d/
echo "deb https://packages.sury.org/php/ $(lsb_release --codename --short) main" > "${pending_dir}/etc/apt/sources.list.d/extra_php_version.list"
echo "deb [signed-by=/etc/apt/trusted.gpg.d/extra_php_version.gpg] https://packages.sury.org/php/ $(lsb_release --codename --short) main" > "${pending_dir}/etc/apt/sources.list.d/extra_php_version.list"
# Ban some packages from sury
echo "
@ -23,19 +23,33 @@ Pin-Priority: 500" >> "${pending_dir}/etc/apt/preferences.d/extra_php_version"
for package in $packages_to_refuse_from_sury; do
echo "
Package: $package
Pin: origin \"packages.sury.org\"
Pin: origin \"packages.sury.org\"
Pin-Priority: -1" >> "${pending_dir}/etc/apt/preferences.d/extra_php_version"
done
# Add yarn
echo "deb [signed-by=/etc/apt/trusted.gpg.d/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > "${pending_dir}/etc/apt/sources.list.d/yarn.list"
# Ban everything from Yarn except Yarn
echo "
Package: *
Pin: origin \"dl.yarnpkg.com\"
Pin-Priority: -1
Package: yarn
Pin: origin \"dl.yarnpkg.com\"
Pin-Priority: 500" >>"${pending_dir}/etc/apt/preferences.d/yarn"
# Ban apache2, bind9
echo "
# PLEASE READ THIS WARNING AND DON'T EDIT THIS FILE
# You are probably reading this file because you tried to install apache2 or
# You are probably reading this file because you tried to install apache2 or
# bind9. These 2 packages conflict with YunoHost.
# Installing apache2 will break nginx and break the entire YunoHost ecosystem
# on your server, therefore don't remove those lines!
# on your server, therefore don't remove those lines!
# You have been warned.
@ -71,6 +85,12 @@ do_post_regen() {
wget --timeout 900 --quiet "https://packages.sury.org/php/apt.gpg" --output-document=- | gpg --dearmor > "/etc/apt/trusted.gpg.d/extra_php_version.gpg"
fi
# Similar to Sury
if [[ ! -s /etc/apt/trusted.gpg.d/yarn.gpg ]]
then
wget --timeout 900 --quiet "https://dl.yarnpkg.com/debian/pubkey.gpg" --output-document=- | gpg --dearmor >"/etc/apt/trusted.gpg.d/yarn.gpg"
fi
# Make sure php7.4 is the default version when using php in cli
if test -e /usr/bin/php$YNH_DEFAULT_PHP_VERSION; then
update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION

View file

@ -1,91 +0,0 @@
#!/bin/bash
set -e
if ! dpkg --list | grep -q 'ii *metronome '; then
echo 'metronome is not installed, skipping'
exit 0
fi
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/conf/metronome
# create directories for pending conf
metronome_dir="${pending_dir}/etc/metronome"
metronome_conf_dir="${metronome_dir}/conf.d"
mkdir -p "$metronome_conf_dir"
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# install main conf file
cat metronome.cfg.lua \
| sed "s/{{ main_domain }}/${main_domain}/g" \
> "${metronome_dir}/metronome.cfg.lua"
# Trick such that old conf files are flagged as to remove
for domain in $YNH_DOMAINS; do
touch "${metronome_conf_dir}/${domain}.cfg.lua"
done
# add domain conf files
domain_list="$(yunohost domain list --features xmpp --output-as json | jq -r ".domains[]")"
for domain in $domain_list; do
cat domain.tpl.cfg.lua \
| sed "s/{{ domain }}/${domain}/g" \
> "${metronome_conf_dir}/${domain}.cfg.lua"
done
# remove old domain conf files
conf_files=$(ls -1 /etc/metronome/conf.d \
| awk '/^[^\.]+\.[^\.]+.*\.cfg\.lua$/ { print $1 }')
for file in $conf_files; do
domain=${file%.cfg.lua}
[[ $YNH_DOMAINS =~ $domain ]] \
|| touch "${metronome_conf_dir}/${file}"
done
}
do_post_regen() {
regen_conf_files=$1
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# create metronome directories for domains
for domain in $YNH_MAIN_DOMAINS; do
mkdir -p "/var/lib/metronome/${domain//./%2e}/pep"
# http_upload directory must be writable by metronome and readable by nginx
mkdir -p "/var/xmpp-upload/${domain}/upload"
# sgid bit allows that file created in that dir will be owned by www-data
# despite the fact that metronome ain't in the www-data group
chmod g+s "/var/xmpp-upload/${domain}/upload"
done
# fix some permissions
[ ! -e '/var/xmpp-upload' ] || chown -R metronome:www-data "/var/xmpp-upload/"
[ ! -e '/var/xmpp-upload' ] || chmod 750 "/var/xmpp-upload/"
# metronome should be in ssl-cert group to let it access SSL certificates
usermod -aG ssl-cert metronome
chown -R metronome: /var/lib/metronome/
chown -R metronome: /etc/metronome/conf.d/
if [[ -z "$(ls /etc/metronome/conf.d/*.cfg.lua 2> /dev/null)" ]]; then
if systemctl is-enabled metronome &> /dev/null; then
systemctl disable metronome --now 2> /dev/null
fi
else
if ! systemctl is-enabled metronome &> /dev/null; then
systemctl enable metronome --now 2> /dev/null
sleep 3
fi
[[ -z "$regen_conf_files" ]] \
|| systemctl restart metronome
fi
}
do_$1_regen ${@:2}

View file

@ -4,25 +4,20 @@ set -e
. /usr/share/yunohost/helpers
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
do_base_regen() {
cd /usr/share/yunohost/conf/nginx
nginx_dir="/etc/nginx"
pending_dir=$1
nginx_dir="${pending_dir}/etc/nginx"
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
# install plain conf files
cp plain/* "$nginx_conf_dir"
cp acme-challenge.conf.inc "$nginx_conf_dir"
cp global.conf "$nginx_conf_dir"
cp ssowat.conf "$nginx_conf_dir"
cp yunohost_http_errors.conf.inc "$nginx_conf_dir"
cp yunohost_sso.conf.inc "$nginx_conf_dir"
# probably run with init: just disable default site, restart NGINX and exit
rm -f "${nginx_dir}/sites-enabled/default"
export compatibility="intermediate"
ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc"
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
ynh_render_template "yunohost_admin.conf.inc" "${nginx_conf_dir}/yunohost_admin.conf.inc"
@ -30,6 +25,17 @@ do_init_regen() {
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
}
do_init_regen() {
cd /usr/share/yunohost/conf/nginx
export compatibility="intermediate"
do_base_regen ""
# probably run with init: just disable default site, restart NGINX and exit
rm -f "${nginx_dir}/sites-enabled/default"
# Restart nginx if conf looks good, otherwise display error and exit unhappy
nginx -t 2> /dev/null || {
@ -53,27 +59,21 @@ do_pre_regen() {
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
# install / update plain conf files
cp plain/* "$nginx_conf_dir"
# remove the panel overlay if this is specified in settings
panel_overlay=$(yunohost settings get 'misc.portal.ssowat_panel_overlay_enabled' | int_to_bool)
if [ "$panel_overlay" == "False" ]; then
echo "#" > "${nginx_conf_dir}/yunohost_panel.conf.inc"
export webadmin_allowlist_enabled="$(jq -r '.webadmin_allowlist_enabled' <<< "$YNH_SETTINGS" | int_to_bool)"
if [ "$webadmin_allowlist_enabled" == "True" ]; then
export webadmin_allowlist="$(jq -r '.webadmin_allowlist' <<< "$YNH_SETTINGS" | sed 's/^null$//g')"
fi
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# Support different strategy for security configurations
export redirect_to_https="$(yunohost settings get 'security.nginx.nginx_redirect_to_https' | int_to_bool)"
export compatibility="$(yunohost settings get 'security.nginx.nginx_compatibility')"
export experimental="$(yunohost settings get 'security.experimental.security_experimental_enabled' | int_to_bool)"
ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc"
export redirect_to_https="$(jq -r '.nginx_redirect_to_https' <<< "$YNH_SETTINGS" | int_to_bool)"
export compatibility="$(jq -r '.nginx_compatibility' <<< "$YNH_SETTINGS" | int_to_bool)"
export experimental="$(jq -r '.security_experimental_enabled' <<< "$YNH_SETTINGS" | int_to_bool)"
do_base_regen "${pending_dir}"
cert_status=$(yunohost domain cert status --json)
# add domain conf files
xmpp_domain_list="$(yunohost domain list --features xmpp --output-as json | jq -r ".domains[]")"
mail_domain_list="$(yunohost domain list --features mail_in mail_out --output-as json | jq -r ".domains[]")"
for domain in $YNH_DOMAINS; do
domain_conf_dir="${nginx_conf_dir}/${domain}.d"
@ -86,11 +86,6 @@ do_pre_regen() {
export domain_cert_ca=$(echo $cert_status \
| jq ".certificates.\"$domain\".CA_type" \
| tr -d '"')
if echo "$xmpp_domain_list" | grep -q "^$domain$"; then
export xmpp_enabled="True"
else
export xmpp_enabled="False"
fi
if echo "$mail_domain_list" | grep -q "^$domain$"; then
export mail_enabled="True"
else
@ -106,15 +101,8 @@ do_pre_regen() {
done
export webadmin_allowlist_enabled=$(yunohost settings get security.webadmin.webadmin_allowlist_enabled | int_to_bool)
if [ "$webadmin_allowlist_enabled" == "True" ]; then
export webadmin_allowlist=$(yunohost settings get security.webadmin.webadmin_allowlist)
fi
ynh_render_template "yunohost_admin.conf.inc" "${nginx_conf_dir}/yunohost_admin.conf.inc"
ynh_render_template "yunohost_api.conf.inc" "${nginx_conf_dir}/yunohost_api.conf.inc"
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
# Legacy file to remove, but we can't really remove it because it may be included by app confs...
echo "# The old yunohost panel/tile/button doesn't exists anymore" > "$nginx_conf_dir"/yunohost_panel.conf.inc
# remove old domain conf files
conf_files=$(ls -1 /etc/nginx/conf.d \

View file

@ -22,19 +22,19 @@ do_pre_regen() {
main_domain=$(cat /etc/yunohost/current_host)
# Support different strategy for security configurations
export compatibility="$(yunohost settings get 'security.postfix.postfix_compatibility')"
export compatibility="$(jq -r '.postfix_compatibility' <<< "$YNH_SETTINGS")"
# Add possibility to specify a relay
# Could be useful with some isp with no 25 port open or more complex setup
export relay_port=""
export relay_user=""
export relay_host=""
export relay_enabled="$(yunohost settings get 'email.smtp.smtp_relay_enabled' | int_to_bool)"
export relay_enabled="$(jq -r '.smtp_relay_enabled' <<< "$YNH_SETTINGS" | int_to_bool)"
if [ "${relay_enabled}" == "True" ]; then
relay_host="$(yunohost settings get 'email.smtp.smtp_relay_host')"
relay_port="$(yunohost settings get 'email.smtp.smtp_relay_port')"
relay_user="$(yunohost settings get 'email.smtp.smtp_relay_user')"
relay_password="$(yunohost settings get 'email.smtp.smtp_relay_password')"
relay_host="$(jq -r '.smtp_relay_host' <<< "$YNH_SETTINGS")"
relay_port="$(jq -r '.smtp_relay_port' <<< "$YNH_SETTINGS")"
relay_user="$(jq -r '.smtp_relay_user' <<< "$YNH_SETTINGS")"
relay_password="$(jq -r '.smtp_relay_password' <<< "$YNH_SETTINGS")"
# Avoid to display "Relay account paswword" to other users
touch ${postfix_dir}/sasl_passwd
@ -69,7 +69,7 @@ do_pre_regen() {
> "${default_dir}/postsrsd"
# adapt it for IPv4-only hosts
ipv6="$(yunohost settings get 'email.smtp.smtp_allow_ipv6' | int_to_bool)"
ipv6="$(jq -r '.smtp_allow_ipv6' <<< "$YNH_SETTINGS" | int_to_bool)"
if [ "$ipv6" == "False" ] || [ ! -f /proc/net/if_inet6 ]; then
sed -i \
's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \

View file

@ -16,7 +16,7 @@ do_pre_regen() {
cp dovecot-ldap.conf "${dovecot_dir}/dovecot-ldap.conf"
cp dovecot.sieve "${dovecot_dir}/global_script/dovecot.sieve"
export pop3_enabled="$(yunohost settings get 'email.pop3.pop3_enabled' | int_to_bool)"
export pop3_enabled="$(jq -r '.pop3_enabled' <<< "$YNH_SETTINGS" | int_to_bool)"
export main_domain=$(cat /etc/yunohost/current_host)
export domain_list="$(yunohost domain list --features mail_in mail_out --output-as json | jq -r ".domains[]" | tr '\n' ' ')"
@ -42,7 +42,7 @@ do_post_regen() {
# create vmail user
id vmail > /dev/null 2>&1 \
|| adduser --system --ingroup mail --uid 500 vmail --home /var/vmail --no-create-home
|| { mkdir -p /var/vmail; adduser --system --ingroup mail --uid 500 vmail --home /var/vmail --no-create-home; }
# Delete legacy home for vmail that existed in the past but was empty, poluting /home/
[ ! -e /home/vmail ] || rmdir --ignore-fail-on-non-empty /home/vmail

43
hooks/conf_regen/30-opendkim Executable file
View file

@ -0,0 +1,43 @@
#!/bin/bash
set -e
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/conf/opendkim
install -D -m 644 opendkim.conf "${pending_dir}/etc/opendkim.conf"
}
do_post_regen() {
mkdir -p /etc/dkim
# Create / empty those files because we're force-regenerating them
echo "" > /etc/dkim/keytable
echo "" > /etc/dkim/signingtable
# create DKIM key for domains
domain_list="$(yunohost domain list --features mail_in mail_out --output-as json | jq -r ".domains[]" | tr '\n' ' ')"
for domain in $domain_list; do
domain_key="/etc/dkim/${domain}.mail.key"
[ ! -f "$domain_key" ] && {
# We use a 1024 bit size because nsupdate doesn't seem to be able to
# handle 2048...
opendkim-genkey --domain="$domain" \
--selector=mail --directory=/etc/dkim -b 1024
mv /etc/dkim/mail.private "$domain_key"
mv /etc/dkim/mail.txt "/etc/dkim/${domain}.mail.txt"
}
echo "mail._domainkey.${domain} ${domain}:mail:${domain_key}" >> /etc/dkim/keytable
echo "*@$domain mail._domainkey.${domain}" >> /etc/dkim/signingtable
done
chown -R opendkim /etc/dkim/
chmod 700 /etc/dkim/
systemctl restart opendkim
}
do_$1_regen ${@:2}

View file

@ -1,65 +0,0 @@
#!/bin/bash
set -e
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/conf/rspamd
install -D -m 644 metrics.local.conf \
"${pending_dir}/etc/rspamd/local.d/metrics.conf"
install -D -m 644 dkim_signing.conf \
"${pending_dir}/etc/rspamd/local.d/dkim_signing.conf"
install -D -m 644 rspamd.sieve \
"${pending_dir}/etc/dovecot/global_script/rspamd.sieve"
install -D -m 644 redis.conf \
"${pending_dir}/etc/rspamd/local.d/redis.conf"
}
do_post_regen() {
##
## DKIM key generation
##
# create DKIM directory with proper permission
mkdir -p /etc/dkim
chown _rspamd /etc/dkim
# create DKIM key for domains
domain_list="$(yunohost domain list --features mail_in mail_out --output-as json | jq -r ".domains[]" | tr '\n' ' ')"
for domain in $domain_list; do
domain_key="/etc/dkim/${domain}.mail.key"
[ ! -f "$domain_key" ] && {
# We use a 1024 bit size because nsupdate doesn't seem to be able to
# handle 2048...
opendkim-genkey --domain="$domain" \
--selector=mail --directory=/etc/dkim -b 1024
mv /etc/dkim/mail.private "$domain_key"
mv /etc/dkim/mail.txt "/etc/dkim/${domain}.mail.txt"
}
done
# fix DKIM keys permissions
chown _rspamd /etc/dkim/*.mail.key
chmod 400 /etc/dkim/*.mail.key
[ ! -e /var/log/rspamd ] || chown -R _rspamd:_rspamd /var/log/rspamd
regen_conf_files=$1
[ -z "$regen_conf_files" ] && exit 0
# compile sieve script
[[ "$regen_conf_files" =~ rspamd\.sieve ]] && {
sievec /etc/dovecot/global_script/rspamd.sieve
chown -R vmail:mail /etc/dovecot/global_script
systemctl restart dovecot
}
# Restart rspamd due to the upgrade
# https://rspamd.com/announce/2016/08/01/rspamd-1.3.1.html
systemctl -q restart rspamd.service
}
do_$1_regen ${@:2}

View file

@ -1,13 +0,0 @@
#!/bin/bash
do_pre_regen() {
:
}
do_post_regen() {
# Enforce these damn permissions because for some reason in some weird cases
# they are spontaneously replaced by root:root -_-
chown -R redis:adm /var/log/redis
}
do_$1_regen ${@:2}

View file

@ -14,10 +14,11 @@ do_pre_regen() {
mkdir -p "${fail2ban_dir}/jail.d"
cp yunohost.conf "${fail2ban_dir}/filter.d/yunohost.conf"
cp yunohost-portal.conf "${fail2ban_dir}/filter.d/yunohost-portal.conf"
cp postfix-sasl.conf "${fail2ban_dir}/filter.d/postfix-sasl.conf"
cp jail.conf "${fail2ban_dir}/jail.conf"
export ssh_port="$(yunohost settings get 'security.ssh.ssh_port')"
export ssh_port="$(jq -r '.ssh_port' <<< "$YNH_SETTINGS")"
ynh_render_template "yunohost-jails.conf" "${fail2ban_dir}/jail.d/yunohost-jails.conf"
}

View file

@ -1,4 +0,0 @@
backup_dir="$1/data/xmpp"
cp -a $backup_dir/var_lib_metronome/. /var/lib/metronome
cp -a $backup_dir/var_xmpp-upload/. /var/xmpp-upload

View file

@ -16,8 +16,6 @@
"app_arch_not_supported": "This app can only be installed on architectures {required} but your server architecture is {current}",
"app_argument_choice_invalid": "Pick a valid value for argument '{name}': '{value}' is not among the available choices ({choices})",
"app_argument_invalid": "Pick a valid value for the argument '{name}': {error}",
"app_argument_password_no_default": "Error while parsing password argument '{name}': password argument can't have a default value for security reasons",
"app_argument_required": "Argument '{name}' is required",
"app_change_url_failed": "Could not change the url for {app}: {error}",
"app_change_url_identical_domains": "The old and new domain/url_path are identical ('{domain}{path}'), nothing to do.",
"app_change_url_no_script": "The app '{app_name}' doesn't support URL modification yet. Maybe you should upgrade it.",
@ -76,7 +74,6 @@
"app_yunohost_version_not_supported": "This app requires YunoHost >= {required} but current installed version is {current}",
"apps_already_up_to_date": "All apps are already up-to-date",
"apps_catalog_failed_to_download": "Unable to download the {apps_catalog} app catalog: {error}",
"apps_catalog_init_success": "App catalog system initialized!",
"apps_catalog_obsolete_cache": "The app catalog cache is empty or obsolete.",
"apps_catalog_update_success": "The application catalog has been updated!",
"apps_catalog_updating": "Updating application catalog…",
@ -94,7 +91,7 @@
"ask_new_domain": "New domain",
"ask_new_path": "New path",
"ask_password": "Password",
"ask_user_domain": "Domain to use for the user's email address and XMPP account",
"ask_user_domain": "Domain to use for the user's email address",
"backup_abstract_method": "This backup method has yet to be implemented",
"backup_actually_backuping": "Creating a backup archive from the collected files…",
"backup_app_failed": "Could not back up {app}",
@ -162,7 +159,6 @@
"certmanager_no_cert_file": "Could not read the certificate file for the domain {domain} (file: {file})",
"certmanager_self_ca_conf_file_not_found": "Could not find configuration file for self-signing authority (file: {file})",
"certmanager_unable_to_parse_self_CA_name": "Could not parse name of self-signing authority (file: {file})",
"certmanager_warning_subdomain_dns_record": "Subdomain '{subdomain}' does not resolve to the same IP address as '{domain}'. Some features will not be available until you fix this and regenerate the certificate.",
"config_action_disabled": "Could not run action '{action}' since it is disabled, make sure to meet its constraints. help: {help}",
"config_action_failed": "Failed to run action '{action}': {error}",
"config_apply_failed": "Applying the new configuration failed: {error}",
@ -171,11 +167,6 @@
"config_forbidden_readonly_type": "The type '{type}' can't be set as readonly, use another type to render this value (relevant arg id: '{id}').",
"config_no_panel": "No config panel found.",
"config_unknown_filter_key": "The filter key '{filter_key}' is incorrect.",
"config_validate_color": "Should be a valid RGB hexadecimal color",
"config_validate_date": "Should be a valid date like in the format YYYY-MM-DD",
"config_validate_email": "Should be a valid email",
"config_validate_time": "Should be a valid time like HH:MM",
"config_validate_url": "Should be a valid web URL",
"confirm_app_install_danger": "DANGER! This app is known to be still experimental (if not explicitly not working)! You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or breaks your system… If you are willing to take that risk anyway, type '{answers}'",
"confirm_app_install_thirdparty": "DANGER! This app is not part of YunoHost's app catalog. Installing third-party apps may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or breaks your system… If you are willing to take that risk anyway, type '{answers}'",
"confirm_app_install_warning": "Warning: This app may work, but is not well-integrated into YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers}] ",
@ -334,14 +325,12 @@
"diagnosis_swap_ok": "The system has {total} of swap!",
"diagnosis_swap_tip": "Please be careful and aware that if the server is hosting swap on an SD card or SSD storage, it may drastically reduce the life expectancy of the device.",
"diagnosis_unknown_categories": "The following categories are unknown: {categories}",
"diagnosis_using_stable_codename": "<cmd>apt</cmd> (the system's package manager) is currently configured to install packages from codename 'stable', instead of the codename of the current Debian version (bullseye).",
"diagnosis_using_stable_codename_details": "This is usually caused by incorrect configuration from your hosting provider. This is dangerous, because as soon as the next Debian version becomes the new 'stable', <cmd>apt</cmd> will want to upgrade all system packages without going through a proper migration procedure. It is recommended to fix this by editing the apt source for base Debian repository, and replace the <cmd>stable</cmd> keyword by <cmd>bullseye</cmd>. The corresponding configuration file should be <cmd>/etc/apt/sources.list</cmd>, or a file in <cmd>/etc/apt/sources.list.d/</cmd>.",
"diagnosis_using_stable_codename": "<cmd>apt</cmd> (the system's package manager) is currently configured to install packages from codename 'stable', instead of the codename of the current Debian version (bookworm).",
"diagnosis_using_stable_codename_details": "This is usually caused by incorrect configuration from your hosting provider. This is dangerous, because as soon as the next Debian version becomes the new 'stable', <cmd>apt</cmd> will want to upgrade all system packages without going through a proper migration procedure. It is recommended to fix this by editing the apt source for base Debian repository, and replace the <cmd>stable</cmd> keyword by <cmd>bookworm</cmd>. The corresponding configuration file should be <cmd>/etc/apt/sources.list</cmd>, or a file in <cmd>/etc/apt/sources.list.d/</cmd>.",
"diagnosis_using_yunohost_testing": "<cmd>apt</cmd> (the system's package manager) is currently configured to install any 'testing' upgrade for YunoHost core.",
"diagnosis_using_yunohost_testing_details": "This is probably OK if you know what you are doing, but pay attention to the release notes before installing YunoHost upgrades! If you want to disable 'testing' upgrades, you should remove the <cmd>testing</cmd> keyword from <cmd>/etc/apt/sources.list.d/yunohost.list</cmd>.",
"disk_space_not_sufficient_install": "There is not enough disk space left to install this application",
"disk_space_not_sufficient_update": "There is not enough disk space left to update this application",
"domain_cannot_add_muc_upload": "You cannot add domains starting with 'muc.'. This kind of name is reserved for the XMPP multi-users chat feature integrated into YunoHost.",
"domain_cannot_add_xmpp_upload": "You cannot add domains starting with 'xmpp-upload.'. This kind of name is reserved for the XMPP upload feature integrated into YunoHost.",
"domain_cannot_remove_main": "You cannot remove '{domain}' since it's the main domain, you first need to set another domain as the main domain using 'yunohost domain main-domain -n <another-domain>'; here is the list of candidate domains: {other_domains}",
"domain_cannot_remove_main_add_new_one": "You cannot remove '{domain}' since it's the main domain and your only domain, you need to first add another domain using 'yunohost domain add <another-domain.com>', then set is as the main domain using 'yunohost domain main-domain -n <another-domain.com>' and then you can remove the domain '{domain}' using 'yunohost domain remove {domain}'.",
"domain_cert_gen_failed": "Could not generate certificate",
@ -371,8 +360,27 @@
"domain_config_default_app_help": "People will automatically be redirected to this app when opening this domain. If no app is specified, people are redirected to the user portal login form.",
"domain_config_mail_in": "Incoming emails",
"domain_config_mail_out": "Outgoing emails",
"domain_config_xmpp": "Instant messaging (XMPP)",
"domain_config_xmpp_help": "NB: some XMPP features will require that you update your DNS records and regenerate your Lets Encrypt certificate to be enabled",
"domain_config_enable_public_apps_page": "Show the list of public apps to visitors",
"domain_config_enable_public_apps_page_help": "Visitors will see a 'public apps' page when ending up on the portal instead of just the login form.",
"domain_config_custom_css": "Custom CSS stylesheet",
"domain_config_custom_css_help": "This is for advanced admins willing to customize the appearance of the portal",
"domain_config_portal_logo": "Custom logo",
"domain_config_portal_logo_help": "Accept .svg, .png and .jpeg. Prefer a monochrome .svg with <code>fill: currentColor</code> so that the logo adapts to the themes.",
"domain_config_feature_name": "Features",
"domain_config_portal_name": "Portal customization",
"domain_config_dns_name": "DNS",
"domain_config_cert_name": "Certificate",
"domain_config_portal_public_intro": "Custom public intro",
"domain_config_portal_public_intro_help": "You can use HTML, basic styles will be applied to generic elements.",
"domain_config_portal_theme": "Default theme",
"domain_config_portal_theme_help": "Users are allowed to choose another one in their settings.",
"domain_config_portal_title": "Custom title",
"domain_config_portal_user_intro": "Custom user intro",
"domain_config_portal_user_intro_help": "You can use HTML, basic styles will be applied to generic elements.",
"domain_config_search_engine": "Search engine URL",
"domain_config_search_engine_help": "This is an optional feature, allowing to display a search bar in the portal (for example if you like to use your YunoHost portal as your browser's home page). This should be an URL with an empty query string such as `https://duckduckgo.com/?q=`, with `q=` as duckduckgo's empty query parameter",
"domain_config_search_engine_name": "Search engine name",
"domain_config_show_other_domains_apps": "Show other domain's apps",
"domain_created": "Domain created",
"domain_creation_failed": "Unable to create domain {domain}: {error}",
"domain_deleted": "Domain deleted",
@ -446,9 +454,7 @@
"global_settings_setting_nginx_redirect_to_https_help": "Redirect HTTP requests to HTTPs by default (DO NOT TURN OFF unless you really know what you're doing!)",
"global_settings_setting_passwordless_sudo": "Allow admins to use 'sudo' without re-typing their passwords",
"global_settings_setting_pop3_enabled": "Enable POP3",
"global_settings_setting_pop3_enabled_help": "Enable the POP3 protocol for the mail server",
"global_settings_setting_portal_theme": "Portal theme",
"global_settings_setting_portal_theme_help": "More info regarding creating custom portal themes at https://yunohost.org/theming",
"global_settings_setting_pop3_enabled_help": "Enable the POP3 protocol for the mail server. POP3 is an older protocol to access mailboxes from email clients and is more lightweight, but has less features than IMAP (enabled by default)",
"global_settings_setting_postfix_compatibility": "Postfix Compatibility",
"global_settings_setting_postfix_compatibility_help": "Compatibility vs. security tradeoff for the Postfix server. Affects the ciphers (and other security-related aspects)",
"global_settings_setting_root_access_explain": "On Linux systems, 'root' is the absolute admin. In YunoHost context, direct 'root' SSH login is by default disable - except from the local network of the server. Members of the 'admins' group can use the sudo command to act as root from the command line. However, it can be helpful to have a (robust) root password to debug the system if for some reason regular admins can not login anymore.",
@ -474,13 +480,26 @@
"global_settings_setting_ssh_password_authentication_help": "Allow password authentication for SSH",
"global_settings_setting_ssh_port": "SSH port",
"global_settings_setting_ssh_port_help": "A port lower than 1024 is preferred to prevent usurpation attempts by non-administrator services on the remote machine. You should also avoid using a port already in use, such as 80 or 443.",
"global_settings_setting_ssowat_panel_overlay_enabled": "Enable the small 'YunoHost' portal shortcut square on apps",
"global_settings_setting_user_strength": "User password strength requirements",
"global_settings_setting_user_strength_help": "These requirements are only enforced when initializing or changing the password",
"global_settings_setting_webadmin_allowlist": "Webadmin IP allowlist",
"global_settings_setting_webadmin_allowlist_enabled": "Enable Webadmin IP allowlist",
"global_settings_setting_webadmin_allowlist_enabled_help": "Allow only some IPs to access the webadmin.",
"global_settings_setting_webadmin_allowlist_help": "IP adresses allowed to access the webadmin. CIDR notation is allowed.",
"global_settings_setting_security_name": "Security",
"global_settings_setting_password_name": "Passwords",
"global_settings_setting_ssh_name": "SSH",
"global_settings_setting_nginx_name": "NGINX (web server)",
"global_settings_setting_postfix_name": "Postfix (SMTP email server)",
"global_settings_setting_webadmin_name": "Webadmin",
"global_settings_setting_root_access_name": "Change root password",
"global_settings_setting_experimental_name": "Experimental",
"global_settings_setting_email_name": "Email",
"global_settings_setting_pop3_name": "POP3",
"global_settings_setting_smtp_name": "SMTP",
"global_settings_setting_misc_name": "Other",
"global_settings_setting_backup_name": "Backup",
"global_settings_setting_network_name": "Network",
"good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).",
"good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).",
"group_already_exist": "Group {group} already exists",
@ -513,8 +532,7 @@
"installation_complete": "Installation completed",
"invalid_credentials": "Invalid password or username",
"invalid_number": "Must be a number",
"invalid_number_max": "Must be lesser than {max}",
"invalid_number_min": "Must be greater than {min}",
"invalid_password": "Invalid password",
"invalid_regex": "Invalid regex:'{regex}'",
"invalid_shell": "Invalid shell: {shell}",
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
@ -575,6 +593,8 @@
"log_user_permission_update": "Update accesses for permission '{}'",
"log_user_update": "Update info for user '{}'",
"mail_alias_remove_failed": "Could not remove e-mail alias '{mail}'",
"mail_alias_unauthorized": "You are not authorized to add aliases related to domain '{domain}'",
"mail_already_exists": "Mail adress '{mail}' already exists",
"mail_domain_unknown": "Invalid e-mail address for domain '{domain}'. Please, use a domain administrated by this server.",
"mail_forward_remove_failed": "Could not remove e-mail forwarding '{mail}'",
"mail_unavailable": "This e-mail address is reserved for the admins group",
@ -582,28 +602,6 @@
"mailbox_used_space_dovecot_down": "The Dovecot mailbox service needs to be up if you want to fetch used mailbox space",
"main_domain_change_failed": "Unable to change the main domain",
"main_domain_changed": "The main domain has been changed",
"migration_0021_cleaning_up": "Cleaning up cache and packages not useful anymore…",
"migration_0021_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.",
"migration_0021_main_upgrade": "Starting main upgrade…",
"migration_0021_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}",
"migration_0021_not_buster2": "The current Debian distribution is not Buster! If you already ran the Buster -> Bullseye migration, then this error is symptomatic of the fact that the migration procedure was not 100% succesful (otherwise YunoHost would have flagged it as completed). It is recommended to investigate what happened with the support team, who will need the **full** log of the migration, which can be found in Tools > Logs in the webadmin.",
"migration_0021_not_enough_free_space": "Free space is pretty low in /var/! You should have at least 1GB free to run this migration.",
"migration_0021_patch_yunohost_conflicts": "Applying patch to workaround conflict issue…",
"migration_0021_patching_sources_list": "Patching the sources.lists…",
"migration_0021_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from the YunoHost app catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}",
"migration_0021_start": "Starting migration to Bullseye",
"migration_0021_still_on_buster_after_main_upgrade": "Something went wrong during the main upgrade, the system appears to still be on Debian Buster",
"migration_0021_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Bullseye.",
"migration_0021_yunohost_upgrade": "Starting YunoHost core upgrade…",
"migration_0023_not_enough_space": "Make sufficient space available in {path} to run the migration.",
"migration_0023_postgresql_11_not_installed": "PostgreSQL was not installed on your system. Nothing to do.",
"migration_0023_postgresql_13_not_installed": "PostgreSQL 11 is installed, but not PostgreSQL 13!? Something weird might have happened on your system :(…",
"migration_0024_rebuild_python_venv_broken_app": "Skipping {app} because virtualenv can't easily be rebuilt for this app. Instead, you should fix the situation by forcing the upgrade of this app using `yunohost app upgrade --force {app}`.",
"migration_0024_rebuild_python_venv_disclaimer_base": "Following the upgrade to Debian Bullseye, some Python applications needs to be partially rebuilt to get converted to the new Python version shipped in Debian (in technical terms: what's called the 'virtualenv' needs to be recreated). In the meantime, those Python applications may not work. YunoHost can attempt to rebuild the virtualenv for some of those, as detailed below. For other apps, or if the rebuild attempt fails, you will need to manually force an upgrade for those apps.",
"migration_0024_rebuild_python_venv_disclaimer_ignored": "Virtualenvs can't be rebuilt automatically for those apps. You need to force an upgrade for those, which can be done from the command line with: `yunohost app upgrade --force APP`: {ignored_apps}",
"migration_0024_rebuild_python_venv_disclaimer_rebuild": "Rebuilding the virtualenv will be attempted for the following apps (NB: the operation may take some time!): {rebuild_apps}",
"migration_0024_rebuild_python_venv_failed": "Failed to rebuild the Python virtualenv for {app}. The app may not work as long as this is not resolved. You should fix the situation by forcing the upgrade of this app using `yunohost app upgrade --force {app}`.",
"migration_0024_rebuild_python_venv_in_progress": "Now attempting to rebuild the Python virtualenv for `{app}`",
"migration_0027_cleaning_up": "Cleaning up cache and packages not useful anymore…",
"migration_0027_delayed_api_restart": "The YunoHost API will automatically be restarted in 15 seconds. It may be unavailable for a few seconds, and then you will have to login again.",
"migration_0027_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade properly.",
@ -618,13 +616,19 @@
"migration_0027_still_on_bullseye_after_main_upgrade": "Something went wrong during the main upgrade, the system appears to still be on Debian Bullseye.",
"migration_0027_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Bookworm.",
"migration_0027_yunohost_upgrade": "Starting YunoHost core upgrade…",
"migration_description_0021_migrate_to_bullseye": "Upgrade the system to Debian Bullseye and YunoHost 11.x",
"migration_description_0022_php73_to_php74_pools": "Migrate php7.3-fpm 'pool' conf files to php7.4",
"migration_description_0023_postgresql_11_to_13": "Migrate databases from PostgreSQL 11 to 13",
"migration_description_0024_rebuild_python_venv": "Repair Python app after bullseye migration",
"migration_description_0025_global_settings_to_configpanel": "Migrate legacy global settings nomenclature to the new, modern nomenclature",
"migration_description_0026_new_admins_group": "Migrate to the new 'multiple admins' system",
"migration_0029_not_enough_space": "Make sufficient space available in {path} to run the migration.",
"migration_0029_postgresql_13_not_installed": "PostgreSQL was not installed on your system. Nothing to do.",
"migration_0029_postgresql_15_not_installed": "PostgreSQL 13 is installed, but not PostgreSQL 15!? Something weird might have happened on your system :(…",
"migration_0030_rebuild_python_venv_in_bookworm_broken_app": "Skipping {app} because virtualenv can't easily be rebuilt for this app. Instead, you should fix the situation by forcing the upgrade of this app using `yunohost app upgrade --force {app}`.",
"migration_0030_rebuild_python_venv_in_bookworm_disclaimer_base": "Following the upgrade to Debian Bookworm, some Python applications needs to be partially rebuilt to get converted to the new Python version shipped in Debian (in technical terms: what's called the 'virtualenv' needs to be recreated). In the meantime, those Python applications may not work. YunoHost can attempt to rebuild the virtualenv for some of those, as detailed below. For other apps, or if the rebuild attempt fails, you will need to manually force an upgrade for those apps.",
"migration_0030_rebuild_python_venv_in_bookworm_disclaimer_ignored": "Virtualenvs can't be rebuilt automatically for those apps. You need to force an upgrade for those, which can be done from the command line with: `yunohost app upgrade --force APP`: {ignored_apps}",
"migration_0030_rebuild_python_venv_in_bookworm_disclaimer_rebuild": "Rebuilding the virtualenv will be attempted for the following apps (NB: the operation may take some time!): {rebuild_apps}",
"migration_0030_rebuild_python_venv_in_bookworm_failed": "Failed to rebuild the Python virtualenv for {app}. The app may not work as long as this is not resolved. You should fix the situation by forcing the upgrade of this app using `yunohost app upgrade --force {app}`.",
"migration_0030_rebuild_python_venv_in_bookworm_in_progress": "Now attempting to rebuild the Python virtualenv for `{app}`",
"migration_description_0027_migrate_to_bookworm": "Upgrade the system to Debian Bookworm and YunoHost 12",
"migration_description_0028_delete_legacy_xmpp_permission": "Delete the old XMPP permissions, Metronome is now an app",
"migration_description_0029_postgresql_13_to_15": "Migrate databases from PostgreSQL 13 to 15",
"migration_description_0030_rebuild_python_venv_in_bookworm": "Repair Python app after bookworm migration",
"migration_ldap_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.",
"migration_ldap_can_not_backup_before_migration": "The backup of the system could not be completed before the migration failed. Error: {error}",
"migration_ldap_migration_failed_trying_to_rollback": "Could not migrate… trying to roll back the system.",
@ -660,9 +664,7 @@
"pattern_domain": "Must be a valid domain name (e.g. my-domain.org)",
"pattern_email": "Must be a valid e-mail address, without '+' symbol (e.g. someone@example.com)",
"pattern_email_forward": "Must be a valid e-mail address, '+' symbol accepted (e.g. someone+tag@example.com)",
"pattern_firstname": "Must be a valid first name (at least 3 chars)",
"pattern_fullname": "Must be a valid full name (at least 3 chars)",
"pattern_lastname": "Must be a valid last name (at least 3 chars)",
"pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to not have a quota",
"pattern_password": "Must be at least 3 characters long",
"pattern_password_app": "Sorry, passwords can not contain the following characters: {forbidden_chars}",
@ -687,7 +689,22 @@
"port_already_closed": "Port {port} is already closed for {ip_version} connections",
"port_already_opened": "Port {port} is already opened for {ip_version} connections",
"postinstall_low_rootfsspace": "The root filesystem has a total space less than 10 GB, which is quite worrisome! You will likely run out of disk space very quickly! It's recommended to have at least 16GB for the root filesystem. If you want to install YunoHost despite this warning, re-run the postinstall with --force-diskspace",
"regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'…",
"pydantic_type_error": "Invalid type.",
"pydantic_type_error_none_not_allowed": "Value is required.",
"pydantic_type_error_str": "Invalid type, string expected.",
"pydantic_value_error_color": "Not a valid color, value must be a named or hex color.",
"pydantic_value_error_const": "Unexpected value; choose between {permitted}",
"pydantic_value_error_date": "Invalid date format",
"pydantic_value_error_email": "Value is not a valid email address",
"pydantic_value_error_number_not_ge": "Value must be greater than or equal to {limit_value}.",
"pydantic_value_error_number_not_le": "Value must be less than or equal to {limit_value}.",
"pydantic_value_error_str_regex": "Invalid string; value doesn't respects the pattern '{pattern}'",
"pydantic_value_error_time": "Invalid time format",
"pydantic_value_error_url_extra": "URL invalid, extra characters found after valid URL: '{extra}'",
"pydantic_value_error_url_host": "URL host invalid",
"pydantic_value_error_url_port": "URL port invalid, port cannot exceed 65535",
"pydantic_value_error_url_scheme": "Invalid or missing URL scheme",
"regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'...",
"regenconf_failed": "Could not regenerate the configuration for category(s): {categories}",
"regenconf_file_backed_up": "Configuration file '{conf}' backed up to '{backup}'",
"regenconf_file_copy_failed": "Could not copy the new configuration file '{new}' to '{conf}'",
@ -736,17 +753,17 @@
"service_description_dnsmasq": "Handles domain name resolution (DNS)",
"service_description_dovecot": "Allows e-mail clients to access/fetch email (via IMAP and POP3)",
"service_description_fail2ban": "Protects against brute-force and other kinds of attacks from the Internet",
"service_description_metronome": "Manage XMPP instant messaging accounts",
"service_description_mysql": "Stores app data (SQL database)",
"service_description_nginx": "Serves or provides access to all the websites hosted on your server",
"service_description_opendkim": "Signs outgoing emails using DKIM such that they are less likely to be flagged as spam",
"service_description_postfix": "Used to send and receive e-mails",
"service_description_postgresql": "Stores app data (SQL database)",
"service_description_redis-server": "A specialized database used for rapid data access, task queue, and communication between programs",
"service_description_rspamd": "Filters spam, and other e-mail related features",
"service_description_slapd": "Stores users, domains and related info",
"service_description_ssh": "Allows you to connect remotely to your server via a terminal (SSH protocol)",
"service_description_yunohost-api": "Manages interactions between the YunoHost web interface and the system",
"service_description_yunohost-firewall": "Manages open and close connection ports to services",
"service_description_yunohost-portal-api": "Manages interactions between the different user portal web interfaces and the system",
"service_description_yunomdns": "Allows you to reach your server using 'yunohost.local' in your local network",
"service_disable_failed": "Could not make the service '{service}' not start at boot.\n\nRecent service logs:{logs}",
"service_disabled": "The service '{service}' will not be started anymore when system boots.",
@ -768,7 +785,7 @@
"service_unknown": "Unknown service '{service}'",
"show_tile_cant_be_enabled_for_regex": "You cannot enable 'show_tile' right now, because the URL for the permission '{permission}' is a regex",
"show_tile_cant_be_enabled_for_url_not_defined": "You cannot enable 'show_tile' right now, because you must first define an URL for the permission '{permission}'",
"ssowat_conf_generated": "SSOwat configuration regenerated",
"ssowat_conf_generated": "SSO and portal configurations regenerated",
"system_upgraded": "System upgraded",
"system_username_exists": "Username already exists in the list of system users",
"this_action_broke_dpkg": "This action broke dpkg/APT (the system package managers)… You can try to solve this issue by connecting through SSH and running `sudo apt install --fix-broken` and/or `sudo dpkg --configure -a`.",

View file

@ -5,7 +5,7 @@ REPO_URL=$(git remote get-url origin)
ME=$(git config --get user.name)
EMAIL=$(git config --get user.email)
LAST_RELEASE=$(git tag --list 'debian/11.*' --sort="v:refname" | tail -n 1)
LAST_RELEASE=$(git tag --list 'debian/12.*' --sort="v:refname" | tail -n 1)
echo "$REPO ($VERSION) $RELEASE; urgency=low"
echo ""

View file

@ -76,9 +76,6 @@ def find_expected_string_keys():
continue
yield "migration_description_" + os.path.basename(path)[:-3]
# FIXME: to be removed in bookworm branch
yield "migration_description_0027_migrate_to_bookworm"
# For each default service, expect to find "service_description_<name>"
for service, info in yaml.safe_load(
open(ROOT + "conf/yunohost/services.yml")
@ -139,16 +136,32 @@ def find_expected_string_keys():
# Domain config panel
domain_config = toml.load(open(ROOT + "share/config_domain.toml"))
for panel in domain_config.values():
domain_settings_with_help_key = [
"portal_logo",
"portal_public_intro",
"portal_theme",
"portal_user_intro",
"search_engine",
"custom_css",
"dns",
"enable_public_apps_page",
]
domain_section_with_no_name = ["app", "cert_", "mail", "registrar"]
for panel_key, panel in domain_config.items():
if not isinstance(panel, dict):
continue
for section in panel.values():
yield f"domain_config_{panel_key}_name"
for section_key, section in panel.items():
if not isinstance(section, dict):
continue
if section_key not in domain_section_with_no_name:
yield f"domain_config_{section_key}_name"
for key, values in section.items():
if not isinstance(values, dict):
continue
yield f"domain_config_{key}"
if key in domain_settings_with_help_key:
yield f"domain_config_{key}_help"
# Global settings
global_config = toml.load(open(ROOT + "share/config_global.toml"))
@ -165,12 +178,14 @@ def find_expected_string_keys():
"root_password_confirm",
]
for panel in global_config.values():
for panel_key, panel in global_config.items():
if not isinstance(panel, dict):
continue
for section in panel.values():
yield f"global_settings_setting_{panel_key}_name"
for section_key, section in panel.items():
if not isinstance(section, dict):
continue
yield f"global_settings_setting_{section_key}_name"
for key, values in section.items():
if not isinstance(values, dict):
continue

View file

@ -0,0 +1,92 @@
_global:
namespace: yunohost
authentication:
api: ldap_ynhuser
cli: null
lock: false
cache: false
portal:
category_help: Portal routes
actions:
### portal_me()
me:
action_help: Allow user to fetch their own infos
api: GET /me
### portal_apps()
apps:
action_help: Allow users to fetch lit of apps they have access to
api: GET /me/apps
### portal_update()
update:
action_help: Allow user to update their infos (display name, mail aliases/forward, password, ...)
api: PUT /update
arguments:
--fullname:
help: The full name of the user. For example 'Camille Dupont'
extra:
pattern: &pattern_fullname
- !!str ^([^\W_]{1,30}[ ,.'-]{0,3})+$
- "pattern_fullname"
--mailforward:
help: Mailforward addresses to add
nargs: "*"
metavar: MAIL
extra:
pattern: &pattern_email_forward
- !!str ^[\w\+.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$
- "pattern_email_forward"
--mailalias:
help: Mail aliases to add
nargs: "*"
metavar: MAIL
extra:
pattern: &pattern_email
- !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$
- "pattern_email"
--currentpassword:
help: Current password
nargs: "?"
--newpassword:
help: New password to set
nargs: "?"
### portal_update_password()
# update_password:
# action_help: Allow user to change their password
# api: PUT /me/update_password
# arguments:
# -c:
# full: --current
# help: Current password
# -p:
# full: --password
# help: New password to set
### portal_reset_password()
reset_password:
action_help: Allow user to update their infos (display name, mail aliases/forward, ...)
api: PUT /me/reset_password
authentication:
# FIXME: to be implemented ?
api: reset_password_token
# FIXME: add args etc
### portal_register()
register:
action_help: Allow user to register using an invite token or ???
api: POST /me
authentication:
# FIXME: to be implemented ?
api: register_invite_token
# FIXME: add args etc
### portal_public()
public:
action_help: Allow anybody to list public apps and other infos regarding the public portal
api: GET /public
authentication:
api: null

View file

@ -70,26 +70,10 @@ user:
help: The full name of the user. For example 'Camille Dupont'
extra:
ask: ask_fullname
required: False
required: True
pattern: &pattern_fullname
- !!str ^([^\W_]{1,30}[ ,.'-]{0,3})+$
- "pattern_fullname"
-f:
full: --firstname
help: Deprecated. Use --fullname instead.
extra:
required: False
pattern: &pattern_firstname
- !!str ^([^\W\d_]{1,30}[ ,.'-]{0,3})+$
- "pattern_firstname"
-l:
full: --lastname
help: Deprecated. Use --fullname instead.
extra:
required: False
pattern: &pattern_lastname
- !!str ^([^\W\d_]{1,30}[ ,.'-]{0,3})+$
- "pattern_lastname"
-p:
full: --password
help: User password
@ -102,7 +86,7 @@ user:
comment: good_practices_about_user_password
-d:
full: --domain
help: Domain for the email address and xmpp account
help: Domain for the email address
extra:
pattern: &pattern_domain
- !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$
@ -147,16 +131,6 @@ user:
help: The full name of the user. For example 'Camille Dupont'
extra:
pattern: *pattern_fullname
-f:
full: --firstname
help: Deprecated. Use --fullname instead.
extra:
pattern: *pattern_firstname
-l:
full: --lastname
help: Deprecated. Use --fullname instead.
extra:
pattern: *pattern_lastname
-m:
full: --mail
extra:
@ -244,10 +218,6 @@ user:
action_help: List existing groups
api: GET /users/groups
arguments:
-s:
full: --short
help: List only the names of groups
action: store_true
-f:
full: --full
help: Display all informations known about each groups
@ -493,7 +463,7 @@ domain:
help: Display domains as a tree
action: store_true
--features:
help: List only domains with features enabled (xmpp, mail_in, mail_out)
help: List only domains with features enabled (mail_in, mail_out)
nargs: "*"
### domain_info()
@ -553,17 +523,6 @@ domain:
help: If removing a DynDNS domain, unsubscribe from the DynDNS service with a password
extra:
pattern: *pattern_password
### domain_dns_conf()
dns-conf:
deprecated: true
action_help: Generate sample DNS configuration for a domain
arguments:
domain:
help: Target domain
extra:
pattern: *pattern_domain
### domain_maindomain()
main-domain:
@ -578,54 +537,6 @@ domain:
extra:
pattern: *pattern_domain
### certificate_status()
cert-status:
deprecated: true
action_help: List status of current certificates (all by default).
arguments:
domain_list:
help: Domains to check
nargs: "*"
--full:
help: Show more details
action: store_true
### certificate_install()
cert-install:
deprecated: true
action_help: Install Let's Encrypt certificates for given domains (all by default).
arguments:
domain_list:
help: Domains for which to install the certificates
nargs: "*"
--force:
help: Install even if current certificate is not self-signed
action: store_true
--no-checks:
help: Does not perform any check that your domain seems correctly configured (DNS, reachability) before attempting to install. (Not recommended)
action: store_true
--self-signed:
help: Install self-signed certificate instead of Let's Encrypt
action: store_true
### certificate_renew()
cert-renew:
deprecated: true
action_help: Renew the Let's Encrypt certificates for given domains (all by default).
arguments:
domain_list:
help: Domains for which to renew the certificates
nargs: "*"
--force:
help: Ignore the validity threshold (15 days)
action: store_true
--email:
help: Send an email to root with logs if some renewing fails
action: store_true
--no-checks:
help: Does not perform any check that your domain seems correctly configured (DNS, reachability) before attempting to renew. (Not recommended)
action: store_true
### domain_url_available()
url-available:
hide_in_help: True
@ -825,7 +736,7 @@ domain:
help: Domains for which to renew the certificates
nargs: "*"
--force:
help: Ignore the validity threshold (30 days)
help: Ignore the validity threshold (15 days)
action: store_true
--email:
help: Send an email to root with logs if some renewing fails
@ -2067,7 +1978,7 @@ diagnosis:
api: PUT /diagnosis/ignore
arguments:
--filter:
help: "Add a filter. The first element should be a diagnosis category, and other criterias can be provided using the infos from the 'meta' sections in 'yunohost diagnosis show'. For example: 'dnsrecords domain=yolo.test category=xmpp'"
help: "Add a filter. The first element should be a diagnosis category, and other criterias can be provided using the infos from the 'meta' sections in 'yunohost diagnosis show'. For example: 'dnsrecords domain=yolo.test category=mail'"
nargs: "*"
metavar: CRITERIA
--list:

View file

@ -2,16 +2,8 @@ version = "1.0"
i18n = "domain_config"
[feature]
name = "Features"
[feature.app]
[feature.app.default_app]
type = "app"
filter = "is_webapp"
default = "_none"
[feature.mail]
[feature.mail.mail_out]
type = "boolean"
default = 1
@ -20,61 +12,106 @@ name = "Features"
type = "boolean"
default = 1
[feature.xmpp]
[feature.app]
[feature.app.default_app]
type = "app"
filter = "is_webapp"
default = "_none"
[feature.xmpp.xmpp]
[feature.portal]
# Only available for "topest" domains
[feature.portal.enable_public_apps_page]
type = "boolean"
default = 0
default = false
[feature.portal.show_other_domains_apps]
type = "boolean"
default = false
[feature.portal.portal_title]
type = "string"
default = "YunoHost"
[feature.portal.portal_logo]
type = "file"
accept = ["image/png", "image/jpeg", "image/svg+xml"]
mode = "python"
bind = "/usr/share/yunohost/portal/customassets/{filename}{ext}"
[feature.portal.portal_theme]
type = "select"
choices = ["system", "light", "dark", "omg", "legacy", "black", "synthwave", "halloween", "coffee", "cupcake", "cyberpunk", "valentine", "nord"]
default = "system"
[feature.portal.search_engine]
type = "url"
default = ""
[feature.portal.search_engine_name]
type = "string"
visible = "search_engine"
[feature.portal.portal_user_intro]
type = "text"
[feature.portal.portal_public_intro]
type = "text"
# FIXME link to GCU
[feature.portal.custom_css]
# NB: this is wrote into "/usr/share/yunohost/portal/customassets/{domain}.custom.css"
type = "text"
[dns]
name = "DNS"
[dns.registrar]
# This part is automatically generated in DomainConfigPanel
[cert]
name = "Certificate"
[cert.cert]
[cert.cert_]
# The section has a different id than 'cert' otherwise it ends up with an unecessary "name" because it's defined for the panel (in i18n.json)
[cert.cert.cert_summary]
[cert.cert_.cert_summary]
type = "alert"
# Automatically filled by DomainConfigPanel
[cert.cert.cert_validity]
[cert.cert_.cert_validity]
type = "number"
readonly = true
visible = "false"
# Automatically filled by DomainConfigPanel
[cert.cert.cert_issuer]
[cert.cert_.cert_issuer]
type = "string"
visible = false
# Automatically filled by DomainConfigPanel
[cert.cert.acme_eligible]
[cert.cert_.acme_eligible]
type = "boolean"
visible = false
# Automatically filled by DomainConfigPanel
[cert.cert.acme_eligible_explain]
[cert.cert_.acme_eligible_explain]
type = "alert"
style = "warning"
visible = "acme_eligible == false || acme_eligible == null"
[cert.cert.cert_no_checks]
[cert.cert_.cert_no_checks]
type = "boolean"
default = false
visible = "acme_eligible == false || acme_eligible == null"
[cert.cert.cert_install]
[cert.cert_.cert_install]
type = "button"
icon = "star"
style = "success"
visible = "cert_issuer != 'letsencrypt'"
enabled = "acme_eligible || cert_no_checks"
[cert.cert.cert_renew]
[cert.cert_.cert_renew]
type = "button"
icon = "refresh"
style = "warning"

View file

@ -2,9 +2,7 @@ version = "1.0"
i18n = "global_settings_setting"
[security]
name = "Security"
[security.password]
name = "Passwords"
[security.password.admin_strength]
type = "select"
@ -28,7 +26,7 @@ name = "Security"
default = false
[security.ssh]
name = "SSH"
[security.ssh.ssh_compatibility]
type = "select"
choices.intermediate = "Intermediate (compatible with older softwares)"
@ -44,7 +42,6 @@ name = "Security"
default = true
[security.nginx]
name = "NGINX (web server)"
[security.nginx.nginx_redirect_to_https]
type = "boolean"
default = true
@ -56,7 +53,7 @@ name = "Security"
default = "intermediate"
[security.postfix]
name = "Postfix (SMTP email server)"
[security.postfix.postfix_compatibility]
type = "select"
choices.intermediate = "Intermediate (allows TLS 1.2)"
@ -64,7 +61,6 @@ name = "Security"
default = "intermediate"
[security.webadmin]
name = "Webadmin"
[security.webadmin.webadmin_allowlist_enabled]
type = "boolean"
default = false
@ -76,8 +72,6 @@ name = "Security"
default = ""
[security.root_access]
name = "Change root password"
[security.root_access.root_access_explain]
type = "alert"
style = "info"
@ -94,22 +88,17 @@ name = "Security"
default = ""
[security.experimental]
name = "Experimental"
[security.experimental.security_experimental_enabled]
type = "boolean"
default = false
[email]
name = "Email"
[email.pop3]
name = "POP3"
[email.pop3.pop3_enabled]
type = "boolean"
default = false
[email.smtp]
name = "SMTP"
[email.smtp.smtp_allow_ipv6]
type = "boolean"
default = true
@ -154,26 +143,13 @@ name = "Email"
visible = "smtp_backup_mx_domains"
[misc]
name = "Other"
[misc.portal]
name = "User portal"
[misc.portal.ssowat_panel_overlay_enabled]
type = "boolean"
default = true
[misc.portal.portal_theme]
type = "select"
# Choices are loaded dynamically in the python code
default = "default"
[misc.backup]
name = "Backup"
[misc.backup.backup_compress_tar_archives]
type = "boolean"
default = false
[misc.network]
name = "Network"
[misc.network.dns_exposure]
type = "select"
choices.both = "Both"

View file

@ -50,6 +50,13 @@ def cli(debug, quiet, output_as, timeout, args, parser):
def api(debug, host, port):
allowed_cors_origins = []
allowed_cors_origins_file = "/etc/yunohost/.admin-api-allowed-cors-origins"
if os.path.exists(allowed_cors_origins_file):
allowed_cors_origins = open(allowed_cors_origins_file).read().strip().split(",")
init_logging(interface="api", debug=debug)
def is_installed_api():
@ -64,6 +71,28 @@ def api(debug, host, port):
actionsmap="/usr/share/yunohost/actionsmap.yml",
locales_dir="/usr/share/yunohost/locales/",
routes={("GET", "/installed"): is_installed_api},
allowed_cors_origins=allowed_cors_origins,
)
sys.exit(ret)
def portalapi(debug, host, port):
allowed_cors_origins = []
allowed_cors_origins_file = "/etc/yunohost/.portal-api-allowed-cors-origins"
if os.path.exists(allowed_cors_origins_file):
allowed_cors_origins = open(allowed_cors_origins_file).read().strip().split(",")
# FIXME : is this the logdir we want ? (yolo to work around permission issue)
init_logging(interface="portalapi", debug=debug, logdir="/var/log")
ret = moulinette.api(
host=host,
port=port,
actionsmap="/usr/share/yunohost/actionsmap-portal.yml",
locales_dir="/usr/share/yunohost/locales/",
allowed_cors_origins=allowed_cors_origins,
)
sys.exit(ret)
@ -115,17 +144,11 @@ def init_logging(interface="cli", debug=False, quiet=False, logdir="/var/log/yun
"version": 1,
"disable_existing_loggers": True,
"formatters": {
"console": {
"format": "%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s"
"tty-debug": {
"format": "%(relativeCreated)-4d %(level_with_color)s %(message)s"
},
"tty-debug": {"format": "%(relativeCreated)-4d %(fmessage)s"},
"precise": {
"format": "%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s"
},
},
"filters": {
"action": {
"()": "moulinette.utils.log.ActionFilter",
"format": "%(asctime)-15s %(levelname)-8s %(name)s.%(funcName)s - %(message)s"
},
},
"handlers": {
@ -138,11 +161,14 @@ def init_logging(interface="cli", debug=False, quiet=False, logdir="/var/log/yun
"level": "DEBUG" if debug else "INFO",
"class": "moulinette.interfaces.api.APIQueueHandler",
},
"portalapi": {
"level": "DEBUG" if debug else "INFO",
"class": "moulinette.interfaces.api.APIQueueHandler",
},
"file": {
"class": "logging.FileHandler",
"formatter": "precise",
"filename": logfile,
"filters": ["action"],
},
},
"loggers": {
@ -164,7 +190,7 @@ def init_logging(interface="cli", debug=False, quiet=False, logdir="/var/log/yun
}
# Logging configuration for CLI (or any other interface than api...) #
if interface != "api":
if interface not in ["api", "portalapi"]:
configure_logging(logging_configuration)
# Logging configuration for API #

File diff suppressed because it is too large Load diff

View file

@ -19,28 +19,28 @@
import os
import re
import hashlib
from logging import getLogger
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.network import download_json
from moulinette.utils.filesystem import (
read_json,
read_yaml,
write_to_json,
write_to_yaml,
mkdir,
)
from yunohost.utils.i18n import _value_for_locale
from yunohost.utils.error import YunohostError
logger = getActionLogger("yunohost.app_catalog")
logger = getLogger("yunohost.app_catalog")
APPS_CATALOG_CACHE = "/var/cache/yunohost/repo"
APPS_CATALOG_LOGOS = "/usr/share/yunohost/applogos"
APPS_CATALOG_CONF = "/etc/yunohost/apps_catalog.yml"
APPS_CATALOG_API_VERSION = 3
APPS_CATALOG_DEFAULT_URL = "https://app.yunohost.org/default"
DEFAULT_APPS_CATALOG_LIST = [{"id": "default", "url": APPS_CATALOG_DEFAULT_URL}]
def app_catalog(full=False, with_categories=False, with_antifeatures=False):
@ -120,33 +120,21 @@ def app_search(string):
return matching_apps
def _initialize_apps_catalog_system():
"""
This function is meant to intialize the apps_catalog system with YunoHost's default app catalog.
"""
default_apps_catalog_list = [{"id": "default", "url": APPS_CATALOG_DEFAULT_URL}]
try:
logger.debug(
"Initializing apps catalog system with YunoHost's default app list"
)
write_to_yaml(APPS_CATALOG_CONF, default_apps_catalog_list)
except Exception as e:
raise YunohostError(
f"Could not initialize the apps catalog system... : {e}", raw_msg=True
)
logger.success(m18n.n("apps_catalog_init_success"))
def _read_apps_catalog_list():
"""
Read the json corresponding to the list of apps catalogs
"""
if not os.path.exists(APPS_CATALOG_CONF):
return DEFAULT_APPS_CATALOG_LIST
try:
list_ = read_yaml(APPS_CATALOG_CONF)
if list_ == DEFAULT_APPS_CATALOG_LIST:
try:
os.remove(APPS_CATALOG_CONF)
except Exception:
pass
# Support the case where file exists but is empty
# by returning [] if list_ is None
return list_ if list_ else []

View file

@ -16,11 +16,14 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import jwt
import os
import logging
import ldap
import ldap.sasl
import time
import hashlib
from pathlib import Path
from moulinette import m18n
from moulinette.authentication import BaseAuthenticator
@ -29,14 +32,32 @@ from moulinette.utils.text import random_ascii
from yunohost.utils.error import YunohostError, YunohostAuthenticationError
from yunohost.utils.ldap import _get_ldap_interface
session_secret = random_ascii()
logger = logging.getLogger("yunohost.authenticators.ldap_admin")
def SESSION_SECRET():
# Only load this once actually requested to avoid boring issues like
# "secret doesnt exists yet" (before postinstall) and therefore service
# miserably fail to start
if not SESSION_SECRET.value:
SESSION_SECRET.value = open("/etc/yunohost/.admin_cookie_secret").read().strip()
assert SESSION_SECRET.value
return SESSION_SECRET.value
SESSION_SECRET.value = None # type: ignore
SESSION_FOLDER = "/var/cache/yunohost/sessions"
SESSION_VALIDITY = 3 * 24 * 3600 # 3 days
LDAP_URI = "ldap://localhost:389"
ADMIN_GROUP = "cn=admins,ou=groups"
AUTH_DN = "uid={uid},ou=users,dc=yunohost,dc=org"
def short_hash(data):
return hashlib.shake_256(data.encode()).hexdigest(20)
class Authenticator(BaseAuthenticator):
name = "ldap_admin"
@ -122,55 +143,87 @@ class Authenticator(BaseAuthenticator):
if con:
con.unbind_s()
return {"user": uid}
def set_session_cookie(self, infos):
from bottle import response
assert isinstance(infos, dict)
assert "user" in infos
# This allows to generate a new session id or keep the existing one
current_infos = self.get_session_cookie(raise_if_no_session_exists=False)
new_infos = {"id": current_infos["id"]}
new_infos.update(infos)
# Create a session id, built as <user_hash> + some random ascii
# Prefixing with the user hash is meant to provide the ability to invalidate all this user's session
# (eg because the user gets deleted, or password gets changed)
# User hashing not really meant for security, just to sort of anonymize/pseudonymize the session file name
infos["id"] = short_hash(infos['user']) + random_ascii(20)
response.set_cookie(
"yunohost.admin",
new_infos,
jwt.encode(infos, SESSION_SECRET(), algorithm="HS256"),
secure=True,
secret=session_secret,
httponly=True,
# samesite="strict", # Bottle 0.12 doesn't support samesite, to be added in next versions
path="/yunohost/api",
samesite="strict",
)
# Create the session file (expiration mechanism)
session_file = f'{SESSION_FOLDER}/{infos["id"]}'
os.system(f'touch "{session_file}"')
def get_session_cookie(self, raise_if_no_session_exists=True):
from bottle import request
from bottle import request, response
try:
# N.B. : here we implicitly reauthenticate the cookie
# because it's signed via the session_secret
# If no session exists (or if session is invalid?)
# it's gonna return the default empty dict,
# which we interpret as an authentication failure
infos = request.get_cookie(
"yunohost.admin", secret=session_secret, default={}
token = request.get_cookie("yunohost.admin", default="").encode()
infos = jwt.decode(
token,
SESSION_SECRET(),
algorithms="HS256",
options={"require": ["id", "user"]},
)
except Exception:
if not raise_if_no_session_exists:
return {"id": random_ascii()}
raise YunohostAuthenticationError("unable_authenticate")
if not infos and raise_if_no_session_exists:
if not infos:
raise YunohostAuthenticationError("unable_authenticate")
if "id" not in infos:
infos["id"] = random_ascii()
self.purge_expired_session_files()
session_file = f'{SESSION_FOLDER}/{infos["id"]}'
if not os.path.exists(session_file):
response.delete_cookie("yunohost.admin", path="/yunohost/api")
raise YunohostAuthenticationError("session_expired")
# FIXME: Here, maybe we want to re-authenticate the session via the authenticator
# For example to check that the username authenticated is still in the admin group...
# Otherwise, we 'touch' the file to extend the validity
os.system(f'touch "{session_file}"')
return infos
def delete_session_cookie(self):
from bottle import response
response.set_cookie("yunohost.admin", "", max_age=-1)
response.delete_cookie("yunohost.admin")
try:
infos = self.get_session_cookie()
session_file = f'{SESSION_FOLDER}/{infos["id"]}'
os.remove(session_file)
except Exception as e:
logger.debug(f"User logged out, but failed to properly invalidate the session : {e}")
response.delete_cookie("yunohost.admin", path="/yunohost/api")
def purge_expired_session_files(self):
for session_file in Path(SESSION_FOLDER).iterdir():
if abs(session_file.stat().st_mtime - time.time()) > SESSION_VALIDITY:
try:
session_file.unlink()
except Exception as e:
logger.debug(f"Failed to delete session file {session_file} ? {e}")
@staticmethod
def invalidate_all_sessions_for_user(user):
for file in Path(SESSION_FOLDER).glob(f"{short_hash(user)}*"):
try:
file.unlink()
except Exception as e:
logger.debug(f"Failed to delete session file {file} ? {e}")

View file

@ -0,0 +1,308 @@
# -*- coding: utf-8 -*-
import time
import jwt
import logging
import ldap
import ldap.sasl
import base64
import os
import hashlib
from pathlib import Path
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
from moulinette import m18n
from moulinette.authentication import BaseAuthenticator
from moulinette.utils.text import random_ascii
from moulinette.utils.filesystem import read_json
from yunohost.utils.error import YunohostError, YunohostAuthenticationError
from yunohost.utils.ldap import _get_ldap_interface
logger = logging.getLogger("yunohostportal.authenticators.ldap_ynhuser")
def SESSION_SECRET():
# Only load this once actually requested to avoid boring issues like
# "secret doesnt exists yet" (before postinstall) and therefore service
# miserably fail to start
if not SESSION_SECRET.value:
SESSION_SECRET.value = open("/etc/yunohost/.ssowat_cookie_secret").read().strip()
assert SESSION_SECRET.value
return SESSION_SECRET.value
SESSION_SECRET.value = None # type: ignore
SESSION_FOLDER = "/var/cache/yunohost-portal/sessions"
SESSION_VALIDITY = 3 * 24 * 3600 # 3 days
URI = "ldap://localhost:389"
USERDN = "uid={username},ou=users,dc=yunohost,dc=org"
# Cache on-disk settings to RAM for faster access
DOMAIN_USER_ACL_DICT: dict[str, dict] = {}
PORTAL_SETTINGS_DIR = "/etc/yunohost/portal"
# Should a user have *minimal* access to a domain?
# - if the user has permission for an application with a URI on the domain, yes
# - if the user is an admin, yes
# - if the user has an email on the domain, yes
# - otherwise, no
def user_is_allowed_on_domain(user: str, domain: str) -> bool:
assert "/" not in domain
portal_settings_path = Path(PORTAL_SETTINGS_DIR) / f"{domain}.json"
if not portal_settings_path.exists():
if "." not in domain:
return False
parent_domain = domain.split(".", 1)[-1]
return user_is_allowed_on_domain(user, parent_domain)
# Check that the domain permissions haven't changed on-disk since we read them
# by comparing file mtime. If we haven't read the file yet, read it for the first time.
# We compare mtime by equality not superiority because maybe the system clock has changed.
mtime = portal_settings_path.stat().st_mtime
if domain not in DOMAIN_USER_ACL_DICT or DOMAIN_USER_ACL_DICT[domain]["mtime"] != mtime:
users: set[str] = set()
for infos in read_json(str(portal_settings_path))["apps"].values():
users = users.union(infos["users"])
DOMAIN_USER_ACL_DICT[domain] = {}
DOMAIN_USER_ACL_DICT[domain]["mtime"] = mtime
DOMAIN_USER_ACL_DICT[domain]["users"] = users
if user in DOMAIN_USER_ACL_DICT[domain]["users"]:
# A user with explicit permission to an application is certainly welcome
return True
ADMIN_GROUP = "cn=admins,ou=groups"
try:
admins = (
_get_ldap_interface()
.search(ADMIN_GROUP, attrs=["memberUid"])[0]
.get("memberUid", [])
)
except Exception as e:
logger.error(f"Failed to list admin users: {e}")
return False
if user in admins:
# Admins can access everything
return True
try:
user_result = _get_ldap_interface().search("ou=users", f"uid={user}", [ "mail" ])
if len(user_result) != 1:
logger.error(f"User not found or many users found for {user}. How is this possible after so much validation?")
return False
user_mail = user_result[0]["mail"]
if len(user_mail) != 1:
logger.error(f"User {user} found, but has the wrong number of email addresses: {user_mail}")
return False
user_mail = user_mail[0]
if not "@" in user_mail:
logger.error(f"Invalid email address for {user}: {user_mail}")
return False
if user_mail.split("@")[1] == domain:
# A user from that domain is welcome
return True
# Users from other domains don't belong here
return False
except Exception as e:
logger.error(f"Failed to get email info for {user}: {e}")
return False
# We want to save the password in the cookie, but we should do so in an encrypted fashion
# This is needed because the SSO later needs to possibly inject the Basic Auth header
# which includes the user's password
# It's also needed because we need to be able to open LDAP sessions, authenticated as the user,
# which requires the user's password
#
# To do so, we use AES-256-CBC. As it's a block encryption algorithm, it requires an IV,
# which we need to keep around for decryption on SSOwat'side.
#
# SESSION_SECRET is used as the encryption key, which implies it must be exactly 32-char long (256/8)
#
# The result is a string formatted as <password_enc_b64>|<iv_b64>
# For example: ctl8kk5GevYdaA5VZ2S88Q==|yTAzCx0Gd1+MCit4EQl9lA==
def encrypt(data):
alg = algorithms.AES(SESSION_SECRET().encode())
iv = os.urandom(int(alg.block_size / 8))
E = Cipher(alg, modes.CBC(iv), default_backend()).encryptor()
p = padding.PKCS7(alg.block_size).padder()
data_padded = p.update(data.encode()) + p.finalize()
data_enc = E.update(data_padded) + E.finalize()
data_enc_b64 = base64.b64encode(data_enc).decode()
iv_b64 = base64.b64encode(iv).decode()
return data_enc_b64 + "|" + iv_b64
def decrypt(data_enc_and_iv_b64):
data_enc_b64, iv_b64 = data_enc_and_iv_b64.split("|")
data_enc = base64.b64decode(data_enc_b64)
iv = base64.b64decode(iv_b64)
alg = algorithms.AES(SESSION_SECRET().encode())
D = Cipher(alg, modes.CBC(iv), default_backend()).decryptor()
p = padding.PKCS7(alg.block_size).unpadder()
data_padded = D.update(data_enc)
data = p.update(data_padded) + p.finalize()
return data.decode()
def short_hash(data):
return hashlib.shake_256(data.encode()).hexdigest(20)
class Authenticator(BaseAuthenticator):
name = "ldap_ynhuser"
def _authenticate_credentials(self, credentials=None):
from bottle import request
try:
username, password = credentials.split(":", 1)
except ValueError:
raise YunohostError("invalid_credentials")
def _reconnect():
con = ldap.ldapobject.ReconnectLDAPObject(URI, retry_max=2, retry_delay=0.5)
con.simple_bind_s(USERDN.format(username=username), password)
return con
try:
con = _reconnect()
except ldap.INVALID_CREDENTIALS:
# FIXME FIXME FIXME : this should be properly logged and caught by Fail2ban ! ! ! ! ! ! !
raise YunohostError("invalid_password")
except ldap.SERVER_DOWN:
logger.warning(m18n.n("ldap_server_down"))
# Check that we are indeed logged in with the expected identity
try:
# whoami_s return dn:..., then delete these 3 characters
who = con.whoami_s()[3:]
except Exception as e:
logger.warning("Error during ldap authentication process: %s", e)
raise
else:
if who != USERDN.format(username=username):
raise YunohostError(
"Not logged with the appropriate identity ?!",
raw_msg=True,
)
finally:
# Free the connection, we don't really need it to keep it open as the point is only to check authentication...
if con:
con.unbind_s()
if not user_is_allowed_on_domain(username, request.get_header("host")):
raise YunohostAuthenticationError("unable_authenticate")
return {"user": username, "pwd": encrypt(password)}
def set_session_cookie(self, infos):
from bottle import response, request
assert isinstance(infos, dict)
assert "user" in infos
assert "pwd" in infos
# Create a session id, built as <user_hash> + some random ascii
# Prefixing with the user hash is meant to provide the ability to invalidate all this user's session
# (eg because the user gets deleted, or password gets changed)
# User hashing not really meant for security, just to sort of anonymize/pseudonymize the session file name
infos["id"] = short_hash(infos['user']) + random_ascii(20)
infos["host"] = request.get_header("host")
is_dev = Path("/etc/yunohost/.portal-api-allowed-cors-origins").exists()
response.set_cookie(
"yunohost.portal",
jwt.encode(infos, SESSION_SECRET(), algorithm="HS256"),
secure=True,
httponly=True,
path="/",
# Doesn't this cause issues ? May cause issue if the portal is on different subdomain than the portal API ? Will surely cause issue for development similar to CORS ?
samesite="strict" if not is_dev else None,
domain=f".{request.get_header('host')}",
)
# Create the session file (expiration mechanism)
session_file = f'{SESSION_FOLDER}/{infos["id"]}'
os.system(f'touch "{session_file}"')
def get_session_cookie(self, decrypt_pwd=False):
from bottle import request, response
try:
token = request.get_cookie("yunohost.portal", default="").encode()
infos = jwt.decode(
token,
SESSION_SECRET(),
algorithms="HS256",
options={"require": ["id", "host", "user", "pwd"]},
)
except Exception:
raise YunohostAuthenticationError("unable_authenticate")
if not infos:
raise YunohostAuthenticationError("unable_authenticate")
if infos["host"] != request.get_header("host"):
raise YunohostAuthenticationError("unable_authenticate")
if not user_is_allowed_on_domain(infos["user"], infos["host"]):
raise YunohostAuthenticationError("unable_authenticate")
self.purge_expired_session_files()
session_file = Path(SESSION_FOLDER) / infos["id"]
if not session_file.exists():
response.delete_cookie("yunohost.portal", path="/")
raise YunohostAuthenticationError("session_expired")
# Otherwise, we 'touch' the file to extend the validity
session_file.touch()
if decrypt_pwd:
infos["pwd"] = decrypt(infos["pwd"])
return infos
def delete_session_cookie(self):
from bottle import response
try:
infos = self.get_session_cookie()
session_file = Path(SESSION_FOLDER) / infos["id"]
session_file.unlink()
except Exception as e:
logger.debug(f"User logged out, but failed to properly invalidate the session : {e}")
response.delete_cookie("yunohost.portal", path="/")
def purge_expired_session_files(self):
for session_file in Path(SESSION_FOLDER).iterdir():
print(session_file.stat().st_mtime - time.time())
if abs(session_file.stat().st_mtime - time.time()) > SESSION_VALIDITY:
try:
session_file.unlink()
except Exception as e:
logger.debug(f"Failed to delete session file {session_file} ? {e}")
@staticmethod
def invalidate_all_sessions_for_user(user):
for file in Path(SESSION_FOLDER).glob(f"{short_hash(user)}*"):
try:
file.unlink()
except Exception as e:
logger.debug(f"Failed to delete session file {file} ? {e}")

View file

@ -30,10 +30,10 @@ from glob import glob
from collections import OrderedDict
from functools import reduce
from packaging import version
from logging import getLogger
from moulinette import Moulinette, m18n
from moulinette.utils.text import random_ascii
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import (
read_file,
mkdir,
@ -76,7 +76,6 @@ from yunohost.utils.system import (
binary_to_human,
space_used_by_directory,
)
from yunohost.settings import settings_get
BACKUP_PATH = "/home/yunohost.backup"
ARCHIVES_PATH = f"{BACKUP_PATH}/archives"
@ -84,7 +83,7 @@ APP_MARGIN_SPACE_SIZE = 100 # In MB
CONF_MARGIN_SPACE_SIZE = 10 # IN MB
POSTINSTALL_ESTIMATE_SPACE_SIZE = 5 # In MB
MB_ALLOWED_TO_ORGANIZE = 10
logger = getActionLogger("yunohost.backup")
logger = getLogger("yunohost.backup")
class BackupRestoreTargetsManager:
@ -1186,9 +1185,6 @@ class RestoreManager:
try:
self._postinstall_if_needed()
# Apply dirty patch to redirect php5 file on php7
self._patch_legacy_php_versions_in_csv_file()
self._restore_system()
self._restore_apps()
except Exception as e:
@ -1199,39 +1195,6 @@ class RestoreManager:
finally:
self.clean()
def _patch_legacy_php_versions_in_csv_file(self):
"""
Apply dirty patch to redirect php5 and php7.0 files to php7.4
"""
from yunohost.utils.legacy import LEGACY_PHP_VERSION_REPLACEMENTS
backup_csv = os.path.join(self.work_dir, "backup.csv")
if not os.path.isfile(backup_csv):
return
replaced_something = False
with open(backup_csv) as csvfile:
reader = csv.DictReader(csvfile, fieldnames=["source", "dest"])
newlines = []
for row in reader:
for pattern, replace in LEGACY_PHP_VERSION_REPLACEMENTS:
if pattern in row["source"]:
replaced_something = True
row["source"] = row["source"].replace(pattern, replace)
newlines.append(row)
if not replaced_something:
return
with open(backup_csv, "w") as csvfile:
writer = csv.DictWriter(
csvfile, fieldnames=["source", "dest"], quoting=csv.QUOTE_ALL
)
for row in newlines:
writer.writerow(row)
def _restore_system(self):
"""Restore user and system parts"""
@ -1368,8 +1331,6 @@ class RestoreManager:
name should be already install)
"""
from yunohost.utils.legacy import (
_patch_legacy_php_versions,
_patch_legacy_php_versions_in_settings,
_patch_legacy_helpers,
)
from yunohost.user import user_group_list
@ -1408,10 +1369,6 @@ class RestoreManager:
# Attempt to patch legacy helpers...
_patch_legacy_helpers(app_settings_in_archive)
# Apply dirty patch to make php5 apps compatible with php7
_patch_legacy_php_versions(app_settings_in_archive)
_patch_legacy_php_versions_in_settings(app_settings_in_archive)
# Delete _common.sh file in backup
common_file = os.path.join(app_backup_in_archive, "_common.sh")
rm(common_file, force=True)
@ -1923,6 +1880,8 @@ class TarBackupMethod(BackupMethod):
@property
def _archive_file(self):
from yunohost.settings import settings_get
if isinstance(self.manager, RestoreManager):
return self.manager.archive_path
@ -2554,9 +2513,6 @@ def backup_info(name, with_details=False, human_readable=False):
for category in ["apps", "system"]:
for name, key_info in info[category].items():
if category == "system":
# Stupid legacy fix for weird format between 3.5 and 3.6
if isinstance(key_info, dict):
key_info = key_info.keys()
info[category][name] = key_info = {"paths": key_info}
else:
info[category][name] = key_info

View file

@ -21,11 +21,10 @@ import sys
import shutil
import subprocess
from glob import glob
from logging import getLogger
from datetime import datetime
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, chown, chmod
from moulinette.utils.process import check_output
@ -38,7 +37,7 @@ from yunohost.service import _run_service_command
from yunohost.regenconf import regen_conf
from yunohost.log import OperationLogger
logger = getActionLogger("yunohost.certmanager")
logger = getLogger("yunohost.certmanager")
CERT_FOLDER = "/etc/yunohost/certs/"
TMP_FOLDER = "/var/www/.well-known/acme-challenge-private/"
@ -557,6 +556,7 @@ def _fetch_and_enable_new_certificate(domain, no_checks=False):
def _prepare_certificate_signing_request(domain, key_file, output_folder):
from OpenSSL import crypto # lazy loading this module for performance reasons
from yunohost.hook import hook_callback
# Init a request
csr = crypto.X509Req()
@ -564,53 +564,34 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder):
# Set the domain
csr.get_subject().CN = domain
from yunohost.domain import domain_config_get
sanlist = []
hook_results = hook_callback("cert_alternate_names", env={"domain": domain})
for hook_name, results in hook_results.items():
#
# There can be multiple results per hook name, so results look like
# {'/some/path/to/hook1':
# { 'state': 'succeed',
# 'stdreturn': ["foo", "bar"]
# },
# '/some/path/to/hook2':
# { ... },
# [...]
#
# Loop over the sub-results
for result in results.values():
if result.get("stdreturn"):
sanlist += result["stdreturn"]
# If XMPP is enabled for this domain, add xmpp-upload and muc subdomains
# in subject alternate names
if domain_config_get(domain, key="feature.xmpp.xmpp") == 1:
subdomain = "xmpp-upload." + domain
xmpp_records = (
Diagnoser.get_cached_report(
"dnsrecords", item={"domain": domain, "category": "xmpp"}
).get("data")
or {}
)
sanlist = []
# Handle the boring case where the domain is not the root of the dns zone etc...
from yunohost.dns import (
_get_relative_name_for_dns_zone,
_get_dns_zone_for_domain,
)
base_dns_zone = _get_dns_zone_for_domain(domain)
basename = _get_relative_name_for_dns_zone(domain, base_dns_zone)
suffix = f".{basename}" if basename != "@" else ""
for sub in ("xmpp-upload", "muc"):
subdomain = sub + "." + domain
if xmpp_records.get("CNAME:" + sub + suffix) == "OK":
sanlist.append(("DNS:" + subdomain))
else:
logger.warning(
m18n.n(
"certmanager_warning_subdomain_dns_record",
subdomain=subdomain,
domain=domain,
)
if sanlist:
csr.add_extensions(
[
crypto.X509Extension(
b"subjectAltName",
False,
(", ".join([f"DNS:{sub}.{domain}" for sub in sanlist])).encode("utf-8"),
)
if sanlist:
csr.add_extensions(
[
crypto.X509Extension(
b"subjectAltName",
False,
(", ".join(sanlist)).encode("utf-8"),
)
]
)
]
)
# Set the key
with open(key_file, "rt") as f:
@ -744,15 +725,6 @@ def _enable_certificate(domain, new_cert_folder):
logger.debug("Restarting services...")
for service in ("dovecot", "metronome"):
# Ugly trick to not restart metronome if it's not installed or no domain configured for XMPP
if service == "metronome" and (
os.system("dpkg --list | grep -q 'ii *metronome'") != 0
or not glob("/etc/metronome/conf.d/*.cfg.lua")
):
continue
_run_service_command("restart", service)
if os.path.isfile("/etc/yunohost/installed"):
# regen nginx conf to be sure it integrates OCSP Stapling
# (We don't do this yet if postinstall is not finished yet)
@ -760,6 +732,7 @@ def _enable_certificate(domain, new_cert_folder):
regen_conf(names=["nginx", "postfix"])
_run_service_command("reload", "nginx")
_run_service_command("restart", "dovecot")
from yunohost.hook import hook_callback

View file

@ -19,9 +19,9 @@
import os
import json
import subprocess
import logging
from typing import List
from moulinette.utils import log
from moulinette.utils.process import check_output
from moulinette.utils.filesystem import read_file, read_json, write_to_json
from yunohost.diagnosis import Diagnoser
@ -31,7 +31,7 @@ from yunohost.utils.system import (
system_arch,
)
logger = log.getActionLogger("yunohost.diagnosis")
logger = logging.getLogger("yunohost.diagnosis")
class MyDiagnoser(Diagnoser):

View file

@ -19,9 +19,9 @@
import re
import os
import random
import logging
from typing import List
from moulinette.utils import log
from moulinette.utils.network import download_text
from moulinette.utils.process import check_output
from moulinette.utils.filesystem import read_file
@ -30,7 +30,7 @@ from yunohost.diagnosis import Diagnoser
from yunohost.utils.network import get_network_interfaces
from yunohost.settings import settings_get
logger = log.getActionLogger("yunohost.diagnosis")
logger = logging.getLogger("yunohost.diagnosis")
class MyDiagnoser(Diagnoser):

View file

@ -18,11 +18,11 @@
#
import os
import re
import logging
from typing import List
from datetime import datetime, timedelta
from publicsuffix2 import PublicSuffixList
from moulinette.utils import log
from moulinette.utils.process import check_output
from yunohost.utils.dns import (
@ -39,7 +39,7 @@ from yunohost.dns import (
_get_relative_name_for_dns_zone,
)
logger = log.getActionLogger("yunohost.diagnosis")
logger = logging.getLogger("yunohost.diagnosis")
class MyDiagnoser(Diagnoser):
@ -91,7 +91,7 @@ class MyDiagnoser(Diagnoser):
domain, include_empty_AAAA_if_no_ipv6=True
)
categories = ["basic", "mail", "xmpp", "extra"]
categories = ["basic", "mail", "extra"]
for category in categories:
records = expected_configuration[category]

View file

@ -19,11 +19,11 @@
import os
import dns.resolver
import re
import logging
from typing import List
from subprocess import CalledProcessError
from moulinette.utils import log
from moulinette.utils.process import check_output
from moulinette.utils.filesystem import read_yaml
@ -34,7 +34,7 @@ from yunohost.utils.dns import dig
DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/dnsbl_list.yml"
logger = log.getActionLogger("yunohost.diagnosis")
logger = logging.getLogger("yunohost.diagnosis")
class MyDiagnoser(Diagnoser):

View file

@ -21,9 +21,9 @@ import os
import time
import glob
from importlib import import_module
from logging import getLogger
from moulinette import m18n, Moulinette
from moulinette.utils import log
from moulinette.utils.filesystem import (
read_json,
write_to_json,
@ -33,7 +33,7 @@ from moulinette.utils.filesystem import (
from yunohost.utils.error import YunohostError, YunohostValidationError
logger = log.getActionLogger("yunohost.diagnosis")
logger = getLogger("yunohost.diagnosis")
DIAGNOSIS_CACHE = "/var/cache/yunohost/diagnosis/"
DIAGNOSIS_CONFIG_FILE = "/etc/yunohost/diagnosis.yml"

View file

@ -19,12 +19,11 @@
import os
import re
import time
from logging import getLogger
from difflib import SequenceMatcher
from collections import OrderedDict
from moulinette import m18n, Moulinette
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, write_to_file, read_toml, mkdir
from yunohost.domain import (
@ -38,11 +37,10 @@ from yunohost.domain import (
from yunohost.utils.dns import dig, is_yunohost_dyndns_domain, is_special_use_tld
from yunohost.utils.error import YunohostValidationError, YunohostError
from yunohost.utils.network import get_public_ip
from yunohost.settings import settings_get
from yunohost.log import is_unit_operation
from yunohost.hook import hook_callback
logger = getActionLogger("yunohost.domain")
logger = getLogger("yunohost.domain")
DOMAIN_REGISTRAR_LIST_PATH = "/usr/share/yunohost/registrar_list.toml"
@ -77,12 +75,6 @@ def domain_dns_suggest(domain):
result += "\n{name} {ttl} IN {type} {value}".format(**record)
result += "\n\n"
if dns_conf["xmpp"]:
result += "\n\n"
result += "; XMPP"
for record in dns_conf["xmpp"]:
result += "\n{name} {ttl} IN {type} {value}".format(**record)
if dns_conf["extra"]:
result += "\n\n"
result += "; Extra"
@ -90,7 +82,7 @@ def domain_dns_suggest(domain):
result += "\n{name} {ttl} IN {type} {value}".format(**record)
for name, record_list in dns_conf.items():
if name not in ("basic", "xmpp", "mail", "extra") and record_list:
if name not in ("basic", "mail", "extra") and record_list:
result += "\n\n"
result += "; " + name
for record in record_list:
@ -119,14 +111,6 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
# if ipv6 available
{"type": "AAAA", "name": "@", "value": "valid-ipv6", "ttl": 3600},
],
"xmpp": [
{"type": "SRV", "name": "_xmpp-client._tcp", "value": "0 5 5222 domain.tld.", "ttl": 3600},
{"type": "SRV", "name": "_xmpp-server._tcp", "value": "0 5 5269 domain.tld.", "ttl": 3600},
{"type": "CNAME", "name": "muc", "value": "@", "ttl": 3600},
{"type": "CNAME", "name": "pubsub", "value": "@", "ttl": 3600},
{"type": "CNAME", "name": "vjud", "value": "@", "ttl": 3600}
{"type": "CNAME", "name": "xmpp-upload", "value": "@", "ttl": 3600}
],
"mail": [
{"type": "MX", "name": "@", "value": "10 domain.tld.", "ttl": 3600},
{"type": "TXT", "name": "@", "value": "\"v=spf1 a mx ip4:123.123.123.123 ipv6:valid-ipv6 -all\"", "ttl": 3600 },
@ -146,9 +130,10 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
}
"""
from yunohost.settings import settings_get
basic = []
mail = []
xmpp = []
extra = []
ipv4 = get_public_ip()
ipv6 = get_public_ip(6)
@ -211,29 +196,6 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
[f"_dmarc{suffix}", ttl, "TXT", '"v=DMARC1; p=none"'],
]
########
# XMPP #
########
if settings["xmpp"]:
xmpp += [
[
f"_xmpp-client._tcp{suffix}",
ttl,
"SRV",
f"0 5 5222 {domain}.",
],
[
f"_xmpp-server._tcp{suffix}",
ttl,
"SRV",
f"0 5 5269 {domain}.",
],
[f"muc{suffix}", ttl, "CNAME", f"{domain}."],
[f"pubsub{suffix}", ttl, "CNAME", f"{domain}."],
[f"vjud{suffix}", ttl, "CNAME", f"{domain}."],
[f"xmpp-upload{suffix}", ttl, "CNAME", f"{domain}."],
]
#########
# Extra #
#########
@ -259,10 +221,6 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
{"name": name, "ttl": ttl_, "type": type_, "value": value}
for name, ttl_, type_, value in basic
],
"xmpp": [
{"name": name, "ttl": ttl_, "type": type_, "value": value}
for name, ttl_, type_, value in xmpp
],
"mail": [
{"name": name, "ttl": ttl_, "type": type_, "value": value}
for name, ttl_, type_, value in mail
@ -277,15 +235,8 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
# Custom records #
##################
# Defined by custom hooks ships in apps for example ...
# FIXME : this ain't practical for apps that may want to add
# custom dns records for a subdomain ... there's no easy way for
# an app to compare the base domain is the parent of the subdomain ?
# (On the other hand, in sep 2021, it looks like no app is using
# this mechanism...)
hook_results = hook_callback("custom_dns_rules", args=[base_domain])
# Defined by custom hooks shipped in apps for example ...
hook_results = hook_callback("custom_dns_rules", env={"base_domain": base_domain, "suffix": suffix})
for hook_name, results in hook_results.items():
#
# There can be multiple results per hook name, so results look like
@ -512,11 +463,26 @@ def _get_relative_name_for_dns_zone(domain, base_dns_zone):
def _get_registrar_config_section(domain):
from lexicon.providers.auto import _relevant_provider_for_domain
registrar_infos = {
"name": m18n.n(
"registrar_infos"
), # This is meant to name the config panel section, for proper display in the webadmin
}
registrar_infos = OrderedDict(
{
"name": m18n.n(
"registrar_infos"
), # This is meant to name the config panel section, for proper display in the webadmin
"registrar": OrderedDict(
{
"readonly": True,
"visible": False,
"default": None,
}
),
"infos": OrderedDict(
{
"type": "alert",
"style": "info",
}
),
}
)
dns_zone = _get_dns_zone_for_domain(domain)
@ -529,31 +495,20 @@ def _get_registrar_config_section(domain):
else:
parent_domain_link = parent_domain
registrar_infos["registrar"] = OrderedDict(
{
"type": "alert",
"style": "info",
"ask": m18n.n(
"domain_dns_registrar_managed_in_parent_domain",
parent_domain=parent_domain,
parent_domain_link=parent_domain_link,
),
"value": "parent_domain",
}
registrar_infos["registrar"]["default"] = "parent_domain"
registrar_infos["infos"]["ask"] = m18n.n(
"domain_dns_registrar_managed_in_parent_domain",
parent_domain=parent_domain,
parent_domain_link=parent_domain_link,
)
return OrderedDict(registrar_infos)
return registrar_infos
# TODO big project, integrate yunohost's dynette as a registrar-like provider
# TODO big project, integrate other dyndns providers such as netlib.re, or cf the list of dyndns providers supported by cloudron...
if is_yunohost_dyndns_domain(dns_zone):
registrar_infos["registrar"] = OrderedDict(
{
"type": "alert",
"style": "success",
"ask": m18n.n("domain_dns_registrar_yunohost"),
"value": "yunohost",
}
)
registrar_infos["registrar"]["default"] = "yunohost"
registrar_infos["infos"]["style"] = "success"
registrar_infos["infos"]["ask"] = m18n.n("domain_dns_registrar_yunohost")
registrar_infos["recovery_password"] = OrderedDict(
{
"type": "password",
@ -561,36 +516,22 @@ def _get_registrar_config_section(domain):
"default": "",
}
)
return OrderedDict(registrar_infos)
return registrar_infos
elif is_special_use_tld(dns_zone):
registrar_infos["registrar"] = OrderedDict(
{
"type": "alert",
"style": "info",
"ask": m18n.n("domain_dns_conf_special_use_tld"),
"value": None,
}
)
registrar_infos["infos"]["ask"] = m18n.n("domain_dns_conf_special_use_tld")
try:
registrar = _relevant_provider_for_domain(dns_zone)[0]
except ValueError:
registrar_infos["registrar"] = OrderedDict(
{
"type": "alert",
"style": "warning",
"ask": m18n.n("domain_dns_registrar_not_supported"),
"value": None,
}
)
registrar_infos["registrar"]["default"] = None
registrar_infos["infos"]["ask"] = m18n.n("domain_dns_registrar_not_supported")
registrar_infos["infos"]["style"] = "warning"
else:
registrar_infos["registrar"] = OrderedDict(
{
"type": "alert",
"style": "info",
"ask": m18n.n("domain_dns_registrar_supported", registrar=registrar),
"value": registrar,
}
registrar_infos["registrar"]["default"] = registrar
registrar_infos["infos"]["ask"] = m18n.n(
"domain_dns_registrar_supported", registrar=registrar
)
TESTED_REGISTRARS = ["ovh", "gandi"]
@ -618,7 +559,7 @@ def _get_registrar_config_section(domain):
infos["optional"] = infos.get("optional", "False")
registrar_infos.update(registrar_credentials)
return OrderedDict(registrar_infos)
return registrar_infos
def _get_registar_settings(domain):

View file

@ -18,28 +18,35 @@
#
import os
import time
from typing import List, Optional
from pathlib import Path
from typing import TYPE_CHECKING, Any, List, Optional, Union
from collections import OrderedDict
from logging import getLogger
from moulinette import m18n, Moulinette
from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file, read_yaml, write_to_yaml, rm
from yunohost.app import (
app_ssowatconf,
_installed_apps,
_get_app_settings,
_get_conflicting_apps,
from moulinette.utils.filesystem import (
read_json,
read_yaml,
rm,
read_file,
write_to_file,
write_to_json,
write_to_yaml,
)
from yunohost.regenconf import regen_conf, _force_clear_hashes, _process_regen_conf
from yunohost.utils.configpanel import ConfigPanel
from yunohost.utils.form import BaseOption
from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.dns import is_yunohost_dyndns_domain
from yunohost.log import is_unit_operation
logger = getActionLogger("yunohost.domain")
if TYPE_CHECKING:
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
from yunohost.utils.configpanel import RawConfig
from yunohost.utils.form import FormModel
from yunohost.utils.configpanel import RawSettings
logger = getLogger("yunohost.domain")
DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains"
@ -100,6 +107,30 @@ def _get_domains(exclude_subdomains=False):
return domain_list_cache
def _get_domain_portal_dict():
domains = _get_domains()
out = OrderedDict()
for domain in domains:
parent = None
# Use the topest parent domain if any
for d in out.keys():
if domain.endswith(f".{d}"):
parent = d
break
out[domain] = f'{parent or domain}/yunohost/sso'
# By default, redirect to $host/yunohost/admin for domains not listed in the dict
# maybe in the future, we can allow to tweak this
out["default"] = "/yunohost/admin"
return dict(out)
def domain_list(exclude_subdomains=False, tree=False, features=[]):
"""
List domains
@ -153,13 +184,14 @@ def domain_info(domain):
domain -- Domain to be checked
"""
from yunohost.app import app_info
from yunohost.app import app_info, _installed_apps, _get_app_settings
from yunohost.dns import _get_registar_settings
from yunohost.certificate import certificate_status
_assert_domain_exists(domain)
registrar, _ = _get_registar_settings(domain)
certificate = domain_cert_status([domain], full=True)["certificates"][domain]
certificate = certificate_status([domain], full=True)["certificates"][domain]
apps = []
for app in _installed_apps():
@ -229,16 +261,11 @@ def domain_add(
from yunohost.utils.ldap import _get_ldap_interface
from yunohost.utils.password import assert_password_is_strong_enough
from yunohost.certificate import _certificate_install_selfsigned
from yunohost.utils.dns import is_yunohost_dyndns_domain
if dyndns_recovery_password:
operation_logger.data_to_redact.append(dyndns_recovery_password)
if domain.startswith("xmpp-upload."):
raise YunohostValidationError("domain_cannot_add_xmpp_upload")
if domain.startswith("muc."):
raise YunohostError("domain_cannot_add_muc_upload")
ldap = _get_ldap_interface()
try:
@ -307,10 +334,8 @@ def domain_add(
regen_conf(
names=[
"nginx",
"metronome",
"dnsmasq",
"postfix",
"rspamd",
"mdns",
"dovecot",
]
@ -352,8 +377,15 @@ def domain_remove(
"""
import glob
from yunohost.hook import hook_callback
from yunohost.app import app_ssowatconf, app_info, app_remove
from yunohost.app import (
app_ssowatconf,
app_info,
app_remove,
_get_app_settings,
_installed_apps,
)
from yunohost.utils.ldap import _get_ldap_interface
from yunohost.utils.dns import is_yunohost_dyndns_domain
if dyndns_recovery_password:
operation_logger.data_to_redact.append(dyndns_recovery_password)
@ -474,7 +506,7 @@ def domain_remove(
f"/etc/nginx/conf.d/{domain}.conf", new_conf=None, save=True
)
regen_conf(names=["nginx", "metronome", "dnsmasq", "postfix", "rspamd", "mdns"])
regen_conf(names=["nginx", "dnsmasq", "postfix", "mdns"])
app_ssowatconf()
hook_callback("post_domain_remove", args=[domain])
@ -560,9 +592,6 @@ def domain_main_domain(operation_logger, new_main_domain=None):
logger.warning(str(e), exc_info=1)
raise YunohostError("main_domain_change_failed")
# Generate SSOwat configuration file
app_ssowatconf()
# Regen configurations
if os.path.exists("/etc/yunohost/installed"):
regen_conf()
@ -585,9 +614,25 @@ def domain_url_available(domain, path):
path -- The path to check (e.g. /coffee)
"""
from yunohost.app import _get_conflicting_apps
return len(_get_conflicting_apps(domain, path)) == 0
def _get_raw_domain_settings(domain):
"""Get domain settings directly from file.
Be carefull, domain settings are saved in `"diff"` mode (i.e. default settings are not saved)
so the file may be completely empty
"""
_assert_domain_exists(domain)
# NB: this corresponds to save_path_tpl in DomainConfigPanel
path = f"{DOMAIN_SETTINGS_DIR}/{domain}.yml"
if os.path.exists(path):
return read_yaml(path)
return {}
def domain_config_get(domain, key="", full=False, export=False):
"""
Display a domain configuration
@ -605,6 +650,7 @@ def domain_config_get(domain, key="", full=False, export=False):
else:
mode = "classic"
DomainConfigPanel = _get_DomainConfigPanel()
config = DomainConfigPanel(domain)
return config.get(key, mode)
@ -616,161 +662,198 @@ def domain_config_set(
"""
Apply a new domain configuration
"""
from yunohost.utils.form import BaseOption
DomainConfigPanel = _get_DomainConfigPanel()
BaseOption.operation_logger = operation_logger
config = DomainConfigPanel(domain)
return config.set(key, value, args, args_file, operation_logger=operation_logger)
class DomainConfigPanel(ConfigPanel):
entity_type = "domain"
save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml"
save_mode = "diff"
def _get_DomainConfigPanel():
from yunohost.utils.configpanel import ConfigPanel
def get(self, key="", mode="classic"):
result = super().get(key=key, mode=mode)
class DomainConfigPanel(ConfigPanel):
entity_type = "domain"
save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml"
save_mode = "diff"
if mode == "full":
for panel, section, option in self._iterate():
# This injects:
# i18n: domain_config_cert_renew_help
# i18n: domain_config_default_app_help
def _get_raw_config(self) -> "RawConfig":
# TODO add mechanism to share some settings with other domains on the same zone
raw_config = super()._get_raw_config()
any_filter = all(self.filter_key)
panel_id, section_id, option_id = self.filter_key
# Portal settings are only available on "topest" domains
if _get_parent_domain_of(self.entity, topest=True) is not None:
del raw_config["feature"]["portal"]
# Optimize wether or not to load the DNS section,
# e.g. we don't want to trigger the whole _get_registary_config_section
# when just getting the current value from the feature section
if not any_filter or panel_id == "dns":
from yunohost.dns import _get_registrar_config_section
raw_config["dns"]["registrar"] = _get_registrar_config_section(
self.entity
)
# Cert stuff
if not any_filter or panel_id == "cert":
from yunohost.certificate import certificate_status
status = certificate_status([self.entity], full=True)["certificates"][
self.entity
]
raw_config["cert"]["cert_"]["cert_summary"]["style"] = status["style"]
# i18n: domain_config_cert_summary_expired
# i18n: domain_config_cert_summary_selfsigned
# i18n: domain_config_cert_summary_abouttoexpire
# i18n: domain_config_cert_summary_ok
# i18n: domain_config_cert_summary_letsencrypt
raw_config["cert"]["cert_"]["cert_summary"]["ask"] = m18n.n(
f"domain_config_cert_summary_{status['summary']}"
)
for option_id, status_key in [
("cert_validity", "validity"),
("cert_issuer", "CA_type"),
("acme_eligible", "ACME_eligible"),
# FIXME not sure why "summary" was injected in settings values
# ("summary", "summary")
]:
raw_config["cert"]["cert_"][option_id]["default"] = status[
status_key
]
# Other specific strings used in config panels
# i18n: domain_config_cert_renew_help
# i18n: domain_config_default_app_help
# i18n: domain_config_xmpp_help
if m18n.key_exists(self.config["i18n"] + "_" + option["id"] + "_help"):
option["help"] = m18n.n(
self.config["i18n"] + "_" + option["id"] + "_help"
return raw_config
def _get_raw_settings(self) -> "RawSettings":
raw_settings = super()._get_raw_settings()
custom_css = Path(f"/usr/share/yunohost/portal/customassets/{self.entity}.custom.css")
if custom_css.exists():
raw_settings["custom_css"] = read_file(str(custom_css))
return raw_settings
def _apply(
self,
form: "FormModel",
previous_settings: dict[str, Any],
exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None,
) -> None:
next_settings = {
k: v for k, v in form.dict().items() if previous_settings.get(k) != v
}
if "default_app" in next_settings:
from yunohost.app import app_map
if "/" in app_map(raw=True).get(self.entity, {}):
raise YunohostValidationError(
"app_make_default_location_already_used",
app=next_settings["default_app"],
domain=self.entity,
other_app=app_map(raw=True)[self.entity]["/"]["id"],
)
return self.config
return result
if next_settings.get("recovery_password", None):
domain_dyndns_set_recovery_password(
self.entity, next_settings["recovery_password"]
)
def _get_raw_config(self):
toml = super()._get_raw_config()
custom_css = next_settings.pop("custom_css", "").strip()
if custom_css:
write_to_file(f"/usr/share/yunohost/portal/customassets/{self.entity}.custom.css", custom_css)
# Make sure the value doesnt get written in the yml
form.custom_css = ""
toml["feature"]["xmpp"]["xmpp"]["default"] = (
1 if self.entity == _get_maindomain() else 0
)
# Optimize wether or not to load the DNS section,
# e.g. we don't want to trigger the whole _get_registary_config_section
# when just getting the current value from the feature section
filter_key = self.filter_key.split(".") if self.filter_key != "" else []
if not filter_key or filter_key[0] == "dns":
from yunohost.dns import _get_registrar_config_section
toml["dns"]["registrar"] = _get_registrar_config_section(self.entity)
# FIXME: Ugly hack to save the registar id/value and reinject it in _get_raw_settings ...
self.registar_id = toml["dns"]["registrar"]["registrar"]["value"]
del toml["dns"]["registrar"]["registrar"]["value"]
# Cert stuff
if not filter_key or filter_key[0] == "cert":
from yunohost.certificate import certificate_status
status = certificate_status([self.entity], full=True)["certificates"][
self.entity
portal_options = [
"enable_public_apps_page",
"show_other_domains_apps",
"portal_title",
"portal_logo",
"portal_theme",
"search_engine",
"search_engine_name",
"portal_user_intro",
"portal_public_intro",
]
toml["cert"]["cert"]["cert_summary"]["style"] = status["style"]
if _get_parent_domain_of(self.entity, topest=True) is None and any(
option in next_settings for option in portal_options
):
from yunohost.portal import PORTAL_SETTINGS_DIR
# i18n: domain_config_cert_summary_expired
# i18n: domain_config_cert_summary_selfsigned
# i18n: domain_config_cert_summary_abouttoexpire
# i18n: domain_config_cert_summary_ok
# i18n: domain_config_cert_summary_letsencrypt
toml["cert"]["cert"]["cert_summary"]["ask"] = m18n.n(
f"domain_config_cert_summary_{status['summary']}"
)
# Portal options are also saved in a `domain.portal.yml` file
# that can be read by the portal API.
# FIXME remove those from the config panel saved values?
# FIXME: Ugly hack to save the cert status and reinject it in _get_raw_settings ...
self.cert_status = status
portal_values = form.dict(include=set(portal_options))
# Remove logo from values else filename will replace b64 content
if "portal_logo" in portal_values:
portal_values.pop("portal_logo")
return toml
if "portal_logo" in next_settings:
if previous_settings.get("portal_logo"):
try:
os.remove(previous_settings["portal_logo"])
except FileNotFoundError:
logger.warning(
f"Coulnd't remove previous logo file, maybe the file was already deleted, path: {previous_settings['portal_logo']}"
)
finally:
portal_values["portal_logo"] = ""
def _get_raw_settings(self):
# TODO add mechanism to share some settings with other domains on the same zone
super()._get_raw_settings()
if next_settings["portal_logo"]:
portal_values["portal_logo"] = Path(next_settings["portal_logo"]).name
# FIXME: Ugly hack to save the registar id/value and reinject it in _get_raw_settings ...
filter_key = self.filter_key.split(".") if self.filter_key != "" else []
if not filter_key or filter_key[0] == "dns":
self.values["registrar"] = self.registar_id
portal_settings_path = Path(f"{PORTAL_SETTINGS_DIR}/{self.entity}.json")
portal_settings: dict[str, Any] = {"apps": {}}
# FIXME: Ugly hack to save the cert status and reinject it in _get_raw_settings ...
if not filter_key or filter_key[0] == "cert":
self.values["cert_validity"] = self.cert_status["validity"]
self.values["cert_issuer"] = self.cert_status["CA_type"]
self.values["acme_eligible"] = self.cert_status["ACME_eligible"]
self.values["summary"] = self.cert_status["summary"]
if portal_settings_path.exists():
portal_settings.update(read_json(str(portal_settings_path)))
def _apply(self):
if (
"default_app" in self.future_values
and self.future_values["default_app"] != self.values["default_app"]
):
from yunohost.app import app_ssowatconf, app_map
if "/" in app_map(raw=True).get(self.entity, {}):
raise YunohostValidationError(
"app_make_default_location_already_used",
app=self.future_values["default_app"],
domain=self.entity,
other_app=app_map(raw=True)[self.entity]["/"]["id"],
# Merge settings since this config file is shared with `app_ssowatconf()` which populate the `apps` key.
portal_settings.update(portal_values)
write_to_json(
str(portal_settings_path), portal_settings, sort_keys=True, indent=4
)
if (
"recovery_password" in self.new_values
and self.new_values["recovery_password"]
):
domain_dyndns_set_recovery_password(
self.entity, self.new_values["recovery_password"]
)
# Do not save password in yaml settings
if "recovery_password" in self.values:
del self.values["recovery_password"]
if "recovery_password" in self.new_values:
del self.new_values["recovery_password"]
assert "recovery_password" not in self.future_values
super()._apply()
super()._apply(form, previous_settings, exclude={"recovery_password"})
# Reload ssowat if default app changed
if (
"default_app" in self.future_values
and self.future_values["default_app"] != self.values["default_app"]
):
app_ssowatconf()
# Reload ssowat if default app changed
if "default_app" in next_settings or "enable_public_apps_page" in next_settings:
from yunohost.app import app_ssowatconf
stuff_to_regen_conf = []
if (
"xmpp" in self.future_values
and self.future_values["xmpp"] != self.values["xmpp"]
):
stuff_to_regen_conf.append("nginx")
stuff_to_regen_conf.append("metronome")
app_ssowatconf()
if (
"mail_in" in self.future_values
and self.future_values["mail_in"] != self.values["mail_in"]
) or (
"mail_out" in self.future_values
and self.future_values["mail_out"] != self.values["mail_out"]
):
if "nginx" not in stuff_to_regen_conf:
stuff_to_regen_conf.append("nginx")
stuff_to_regen_conf.append("postfix")
stuff_to_regen_conf.append("dovecot")
stuff_to_regen_conf.append("rspamd")
stuff_to_regen_conf = set()
if "mail_in" in next_settings or "mail_out" in next_settings:
stuff_to_regen_conf.update({"nginx", "postfix", "dovecot"})
if stuff_to_regen_conf:
regen_conf(names=stuff_to_regen_conf)
if stuff_to_regen_conf:
regen_conf(names=list(stuff_to_regen_conf))
return DomainConfigPanel
def domain_action_run(domain, action, args=None):
import urllib.parse
if action == "cert.cert.cert_install":
if action == "cert.cert_.cert_install":
from yunohost.certificate import certificate_install as action_func
elif action == "cert.cert.cert_renew":
elif action == "cert.cert_.cert_renew":
from yunohost.certificate import certificate_renew as action_func
args = dict(urllib.parse.parse_qsl(args or "", keep_blank_values=True))
@ -819,10 +902,6 @@ def domain_cert_renew(domain_list, force=False, no_checks=False, email=False):
return certificate_renew(domain_list, force, no_checks, email)
def domain_dns_conf(domain):
return domain_dns_suggest(domain)
def domain_dns_suggest(domain):
from yunohost.dns import domain_dns_suggest

View file

@ -22,10 +22,10 @@ import glob
import base64
import subprocess
import hashlib
from logging import getLogger
from moulinette import Moulinette, m18n
from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file, rm, chown, chmod
from yunohost.utils.error import YunohostError, YunohostValidationError
@ -35,7 +35,7 @@ from yunohost.utils.dns import dig, is_yunohost_dyndns_domain
from yunohost.log import is_unit_operation
from yunohost.regenconf import regen_conf
logger = getActionLogger("yunohost.dyndns")
logger = getLogger("yunohost.dyndns")
DYNDNS_PROVIDER = "dyndns.yunohost.org"
DYNDNS_DNS_AUTH = ["ns0.yunohost.org", "ns1.yunohost.org"]
@ -471,7 +471,7 @@ def dyndns_update(
# Delete custom DNS records, we don't support them (have to explicitly
# authorize them on dynette)
for category in dns_conf.keys():
if category not in ["basic", "mail", "xmpp", "extra"]:
if category not in ["basic", "mail", "extra"]:
del dns_conf[category]
# Delete the old records for all domain/subdomains

View file

@ -19,16 +19,16 @@
import os
import yaml
import miniupnpc
from logging import getLogger
from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import process
from moulinette.utils.log import getActionLogger
FIREWALL_FILE = "/etc/yunohost/firewall.yml"
UPNP_CRON_JOB = "/etc/cron.d/yunohost-firewall-upnp"
logger = getActionLogger("yunohost.firewall")
logger = getLogger("yunohost.firewall")
def firewall_allow(
@ -402,7 +402,13 @@ def firewall_upnp(action="status", no_refresh=False):
# Discover UPnP device(s)
logger.debug("discovering UPnP devices...")
nb_dev = upnpc.discover()
try:
nb_dev = upnpc.discover()
except Exception:
logger.warning("Failed to find any UPnP device on the network")
nb_dev = -1
enabled = False
logger.debug("found %d UPnP device(s)", int(nb_dev))
if nb_dev < 1:
logger.error(m18n.n("upnp_dev_not_found"))

View file

@ -23,16 +23,16 @@ import tempfile
import mimetypes
from glob import iglob
from importlib import import_module
from logging import getLogger
from moulinette import m18n, Moulinette
from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import log
from moulinette.utils.filesystem import read_yaml, cp
HOOK_FOLDER = "/usr/share/yunohost/hooks/"
CUSTOM_HOOK_FOLDER = "/etc/yunohost/hooks.d/"
logger = log.getActionLogger("yunohost.hook")
logger = getLogger("yunohost.hook")
def hook_add(app, file):
@ -359,6 +359,7 @@ def hook_exec(
r"Removing obsolete dictionary files",
r"Creating new PostgreSQL cluster",
r"/usr/lib/postgresql/13/bin/initdb",
r"/usr/lib/postgresql/15/bin/initdb",
r"The files belonging to this database system will be owned by user",
r"This user must also own the server process.",
r"The database cluster will be initialized with locale",
@ -366,6 +367,7 @@ def hook_exec(
r"The default text search configuration will be set to",
r"Data page checksums are disabled.",
r"fixing permissions on existing directory /var/lib/postgresql/13/main ... ok",
r"fixing permissions on existing directory /var/lib/postgresql/15/main ... ok",
r"creating subdirectories \.\.\. ok",
r"selecting dynamic .* \.\.\. ",
r"selecting default .* \.\.\. ",

View file

@ -1,4 +1,3 @@
#
# Copyright (c) 2024 YunoHost Contributors
#
# This file is part of YunoHost (see https://yunohost.org)
@ -33,13 +32,11 @@ from moulinette import m18n, Moulinette
from moulinette.core import MoulinetteError
from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.system import get_ynh_package_version
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, read_yaml
logger = getActionLogger("yunohost.log")
logger = getLogger("yunohost.log")
CATEGORIES_PATH = "/var/log/yunohost/categories/"
OPERATIONS_PATH = "/var/log/yunohost/categories/operation/"
OPERATIONS_PATH = "/var/log/yunohost/operations/"
METADATA_FILE_EXT = ".yml"
LOG_FILE_EXT = ".log"
@ -288,7 +285,7 @@ def log_show(
infos = {}
# If it's a unit operation, display the name and the description
if base_path.startswith(CATEGORIES_PATH):
if base_path.startswith(OPERATIONS_PATH):
infos["description"] = _get_description_from_name(base_filename)
infos["name"] = base_filename
@ -391,12 +388,17 @@ def log_show(
def log_share(path):
return log_show(path, share=True)
from typing import TypeVar, Callable, Concatenate, ParamSpec
#FuncT = TypeVar("FuncT", bound=Callable[..., Any])
Param = ParamSpec("Param")
RetType = TypeVar("RetType")
def is_unit_operation(
entities=["app", "domain", "group", "service", "user"],
exclude=["password"],
operation_key=None,
):
entities = ["app", "domain", "group", "service", "user"],
exclude = ["password"],
) -> Callable[[Callable[Concatenate["OperationLogger", Param], RetType]], Callable[Param, RetType]]:
"""
Configure quickly a unit operation
@ -413,17 +415,10 @@ def is_unit_operation(
called 'password' are removed. If an argument is an object, you need to
exclude it or create manually the unit operation without this decorator.
operation_key A key to describe the unit operation log used to create the
filename and search a translation. Please ensure that this key prefixed by
'log_' is present in locales/en.json otherwise it won't be translatable.
"""
def decorate(func):
def decorate(func: Callable[Concatenate["OperationLogger", Param], RetType]) -> Callable[Param, RetType]:
def func_wrapper(*args, **kwargs):
op_key = operation_key
if op_key is None:
op_key = func.__name__
# If the function is called directly from an other part of the code
# and not by the moulinette framework, we need to complete kwargs
@ -474,7 +469,7 @@ def is_unit_operation(
context[field] = value.name
except Exception:
context[field] = "IOBase"
operation_logger = OperationLogger(op_key, related_to, args=context)
operation_logger = OperationLogger(func.__name__, related_to, args=context)
try:
# Start the actual function, and give the unit operation
@ -542,7 +537,7 @@ class OperationLogger:
This class record logs and metadata like context or start time/end time.
"""
_instances: List[object] = []
_instances: List["OperationLogger"] = []
def __init__(self, operation, related_to=None, **kwargs):
# TODO add a way to not save password on app installation
@ -818,7 +813,7 @@ class OperationLogger:
# 2019-10-19 16:10:27,611: DEBUG - + mysql -u piwigo --password=********** -B piwigo
# And we just want the part starting by "DEBUG - "
lines = [line for line in lines if ":" in line.strip()]
lines = [line.strip().split(": ", 1)[1] for line in lines]
lines = [line.strip().split(": ", 1)[-1] for line in lines]
# And we ignore boring/irrelevant lines
# Annnnnnd we also ignore lines matching [number] + such as
# 72971 DEBUG 29739 + ynh_exit_properly

View file

@ -1,582 +0,0 @@
import glob
import os
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output, call_async_output
from moulinette.utils.filesystem import read_file, rm, write_to_file
from yunohost.tools import (
Migration,
tools_update,
tools_upgrade,
_apt_log_line_is_relevant,
)
from yunohost.app import unstable_apps
from yunohost.regenconf import manually_modified_files, _force_clear_hashes
from yunohost.utils.system import (
free_space_in_directory,
get_ynh_package_version,
_list_upgradable_apt_packages,
)
from yunohost.service import _get_services, _save_services
logger = getActionLogger("yunohost.migration")
N_CURRENT_DEBIAN = 10
N_CURRENT_YUNOHOST = 4
VENV_REQUIREMENTS_SUFFIX = ".requirements_backup_for_bullseye_upgrade.txt"
def _get_all_venvs(dir, level=0, maxlevel=3):
"""
Returns the list of all python virtual env directories recursively
Arguments:
dir - the directory to scan in
maxlevel - the depth of the recursion
level - do not edit this, used as an iterator
"""
if not os.path.exists(dir):
return []
result = []
# Using os functions instead of glob, because glob doesn't support hidden folders, and we need recursion with a fixed depth
for file in os.listdir(dir):
path = os.path.join(dir, file)
if os.path.isdir(path):
activatepath = os.path.join(path, "bin", "activate")
if os.path.isfile(activatepath):
content = read_file(activatepath)
if ("VIRTUAL_ENV" in content) and ("PYTHONHOME" in content):
result.append(path)
continue
if level < maxlevel:
result += _get_all_venvs(path, level=level + 1)
return result
def _backup_pip_freeze_for_python_app_venvs():
"""
Generate a requirements file for all python virtual env located inside /opt/ and /var/www/
"""
venvs = _get_all_venvs("/opt/") + _get_all_venvs("/var/www/")
for venv in venvs:
# Generate a requirements file from venv
os.system(
f"{venv}/bin/pip freeze > {venv}{VENV_REQUIREMENTS_SUFFIX} 2>/dev/null"
)
class MyMigration(Migration):
"Upgrade the system to Debian Bullseye and Yunohost 11.x"
mode = "manual"
def run(self):
self.check_assertions()
logger.info(m18n.n("migration_0021_start"))
#
# Add new apt .deb signing key
#
new_apt_key = "https://forge.yunohost.org/yunohost_bullseye.asc"
check_output(f"wget -O- {new_apt_key} -q | apt-key add -qq -")
#
# Patch sources.list
#
logger.info(m18n.n("migration_0021_patching_sources_list"))
self.patch_apt_sources_list()
# Stupid OVH has some repo configured which dont work with bullseye and break apt ...
os.system("sudo rm -f /etc/apt/sources.list.d/ovh-*.list")
# Force add sury if it's not there yet
# This is to solve some weird issue with php-common breaking php7.3-common,
# hence breaking many php7.3-deps
# hence triggering some dependency conflict (or foobar-ynh-deps uninstall)
# Adding it there shouldnt be a big deal - Yunohost 11.x does add it
# through its regen conf anyway.
if not os.path.exists("/etc/apt/sources.list.d/extra_php_version.list"):
open("/etc/apt/sources.list.d/extra_php_version.list", "w").write(
"deb https://packages.sury.org/php/ bullseye main"
)
# Add Sury key even if extra_php_version.list was already there,
# because some old system may be using an outdated key not valid for Bullseye
# and that'll block the migration
os.system(
'wget --timeout 900 --quiet "https://packages.sury.org/php/apt.gpg" --output-document=- | gpg --dearmor >"/etc/apt/trusted.gpg.d/extra_php_version.gpg"'
)
# Remove legacy, duplicated sury entry if it exists
if os.path.exists("/etc/apt/sources.list.d/sury.list"):
os.system("rm -rf /etc/apt/sources.list.d/sury.list")
#
# Get requirements of the different venvs from python apps
#
_backup_pip_freeze_for_python_app_venvs()
#
# Run apt update
#
tools_update(target="system")
# Tell libc6 it's okay to restart system stuff during the upgrade
os.system(
"echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections"
)
# Do not restart nginx during the upgrade of nginx-common and nginx-extras ...
# c.f. https://manpages.debian.org/bullseye/init-system-helpers/deb-systemd-invoke.1p.en.html
# and zcat /usr/share/doc/init-system-helpers/README.policy-rc.d.gz
# and the code inside /usr/bin/deb-systemd-invoke to see how it calls /usr/sbin/policy-rc.d ...
# and also invoke-rc.d ...
write_to_file(
"/usr/sbin/policy-rc.d",
'#!/bin/bash\n[[ "$1" =~ "nginx" ]] && [[ "$2" == "restart" ]] && exit 101 || exit 0',
)
os.system("chmod +x /usr/sbin/policy-rc.d")
# Don't send an email to root about the postgresql migration. It should be handled automatically after.
os.system(
"echo 'postgresql-common postgresql-common/obsolete-major seen true' | debconf-set-selections"
)
#
# Patch yunohost conflicts
#
logger.info(m18n.n("migration_0021_patch_yunohost_conflicts"))
self.patch_yunohost_conflicts()
#
# Specific tweaking to get rid of custom my.cnf and use debian's default one
# (my.cnf is actually a symlink to mariadb.cnf)
#
_force_clear_hashes(["/etc/mysql/my.cnf"])
rm("/etc/mysql/mariadb.cnf", force=True)
rm("/etc/mysql/my.cnf", force=True)
ret = self.apt_install(
"mariadb-common --reinstall -o Dpkg::Options::='--force-confmiss'"
)
if ret != 0:
raise YunohostError("Failed to reinstall mariadb-common ?", raw_msg=True)
#
# /usr/share/yunohost/yunohost-config/ssl/yunoCA -> /usr/share/yunohost/ssl
#
if os.path.exists("/usr/share/yunohost/yunohost-config/ssl/yunoCA"):
os.system(
"mv /usr/share/yunohost/yunohost-config/ssl/yunoCA /usr/share/yunohost/ssl"
)
rm("/usr/share/yunohost/yunohost-config", recursive=True, force=True)
#
# /home/yunohost.conf -> /var/cache/yunohost/regenconf
#
if os.path.exists("/home/yunohost.conf"):
os.system("mv /home/yunohost.conf /var/cache/yunohost/regenconf")
rm("/home/yunohost.conf", recursive=True, force=True)
# Remove legacy postgresql service record added by helpers,
# will now be dynamically handled by the core in bullseye
services = _get_services()
if "postgresql" in services:
del services["postgresql"]
_save_services(services)
#
# Critical fix for RPI otherwise network is down after rebooting
# https://forum.yunohost.org/t/20652
#
if os.system("systemctl | grep -q dhcpcd") == 0:
logger.info("Applying fix for DHCPCD ...")
os.system("mkdir -p /etc/systemd/system/dhcpcd.service.d")
write_to_file(
"/etc/systemd/system/dhcpcd.service.d/wait.conf",
"[Service]\nExecStart=\nExecStart=/usr/sbin/dhcpcd -w",
)
#
# Another boring fix for the super annoying libc6-dev: Breaks libgcc-8-dev
# https://forum.yunohost.org/t/20617
#
if (
os.system("dpkg --list | grep '^ii' | grep -q ' libgcc-8-dev'") == 0
and os.system(
"LC_ALL=C apt policy libgcc-8-dev | grep Candidate | grep -q rpi"
)
== 0
):
logger.info(
"Attempting to fix the build-essential / libc6-dev / libgcc-8-dev hell ..."
)
os.system("cp /var/lib/dpkg/status /root/dpkg_status.bkp")
# This removes the dependency to build-essential from $app-ynh-deps
os.system(
"perl -i~ -0777 -pe 's/(Package: .*-ynh-deps\\n(.+:.+\\n)+Depends:.*)(build-essential, ?)(.*)/$1$4/g' /var/lib/dpkg/status"
)
self.apt_install(
"build-essential-"
) # Note the '-' suffix to mean that we actually want to remove the packages
os.system(
"LC_ALL=C DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt autoremove --assume-yes"
)
self.apt_install(
"gcc-8- libgcc-8-dev- equivs"
) # Note the '-' suffix to mean that we actually want to remove the packages .. we also explicitly add 'equivs' to the list because sometimes apt is dumb and will derp about it
#
# Main upgrade
#
logger.info(m18n.n("migration_0021_main_upgrade"))
apps_packages = self.get_apps_equivs_packages()
self.hold(apps_packages)
tools_upgrade(target="system", allow_yunohost_upgrade=False)
if self.debian_major_version() == N_CURRENT_DEBIAN:
raise YunohostError("migration_0021_still_on_buster_after_main_upgrade")
# Force explicit install of php7.4-fpm and other old 'default' dependencies
# that are now only in Recommends
#
# Also, we need to install php7.4 equivalents of other php7.3 dependencies.
# For example, Nextcloud may depend on php7.3-zip, and after the php pool migration
# to autoupgrade Nextcloud to 7.4, it will need the php7.4-zip to work.
# The following list is based on an ad-hoc analysis of php deps found in the
# app ecosystem, with a known equivalent on php7.4.
#
# This is kinda a dirty hack as it doesnt properly update the *-ynh-deps virtual packages
# with the proper list of dependencies, and the dependencies install this way
# will get flagged as 'manually installed'.
#
# We'll probably want to do something during the Bullseye->Bookworm migration to re-flag
# these as 'auto' so they get autoremoved if not needed anymore.
# Also hopefully by then we'll have manifestv2 (maybe) and will be able to use
# the apt resource mecanism to regenerate the *-ynh-deps virtual packages ;)
php73packages_suffixes = [
"apcu",
"bcmath",
"bz2",
"dom",
"gmp",
"igbinary",
"imagick",
"imap",
"mbstring",
"memcached",
"mysqli",
"mysqlnd",
"pgsql",
"redis",
"simplexml",
"soap",
"sqlite3",
"ssh2",
"tidy",
"xml",
"xmlrpc",
"xsl",
"zip",
]
cmd = (
"apt show '*-ynh-deps' 2>/dev/null"
" | grep Depends"
f" | grep -o -E \"php7.3-({'|'.join(php73packages_suffixes)})\""
" | sort | uniq"
" | sed 's/php7.3/php7.4/g'"
" || true"
)
basephp74packages_to_install = [
"php7.4-fpm",
"php7.4-common",
"php7.4-ldap",
"php7.4-intl",
"php7.4-mysql",
"php7.4-gd",
"php7.4-curl",
"php-php-gettext",
]
php74packages_to_install = basephp74packages_to_install + [
f.strip() for f in check_output(cmd).split("\n") if f.strip()
]
ret = self.apt_install(
f"{' '.join(php74packages_to_install)} "
"$(dpkg --list | grep ynh-deps | awk '{print $2}') "
"-o Dpkg::Options::='--force-confmiss'"
)
if ret != 0:
raise YunohostError(
"Failed to force the install of php dependencies ?", raw_msg=True
)
# Clean the mess
logger.info(m18n.n("migration_0021_cleaning_up"))
os.system(
"LC_ALL=C DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt autoremove --assume-yes"
)
os.system("apt clean --assume-yes")
#
# Stupid hack for stupid dnsmasq not picking up its new init.d script then breaking everything ...
# https://forum.yunohost.org/t/20676
#
if os.path.exists("/etc/init.d/dnsmasq.dpkg-dist"):
logger.info("Copying new version for /etc/init.d/dnsmasq ...")
os.system("cp /etc/init.d/dnsmasq.dpkg-dist /etc/init.d/dnsmasq")
#
# Yunohost upgrade
#
logger.info(m18n.n("migration_0021_yunohost_upgrade"))
self.unhold(apps_packages)
cmd = "LC_ALL=C"
cmd += " DEBIAN_FRONTEND=noninteractive"
cmd += " APT_LISTCHANGES_FRONTEND=none"
cmd += " apt dist-upgrade "
cmd += " --quiet -o=Dpkg::Use-Pty=0 --fix-broken --dry-run"
cmd += " | grep -q 'ynh-deps'"
logger.info("Simulating upgrade...")
if os.system(cmd) == 0:
raise YunohostError(
"The upgrade cannot be completed, because some app dependencies would need to be removed?",
raw_msg=True,
)
postupgradecmds = f"apt-mark auto {' '.join(basephp74packages_to_install)}\n"
postupgradecmds += "rm -f /usr/sbin/policy-rc.d\n"
postupgradecmds += "echo 'Restarting nginx...' >&2\n"
postupgradecmds += "systemctl restart nginx\n"
tools_upgrade(target="system", postupgradecmds=postupgradecmds)
def debian_major_version(self):
# The python module "platform" and lsb_release are not reliable because
# on some setup, they may still return Release=9 even after upgrading to
# buster ... (Apparently this is related to OVH overriding some stuff
# with /etc/lsb-release for instance -_-)
# Instead, we rely on /etc/os-release which should be the raw info from
# the distribution...
return int(
check_output(
"grep VERSION_ID /etc/os-release | head -n 1 | tr '\"' ' ' | cut -d ' ' -f2"
)
)
def yunohost_major_version(self):
return int(get_ynh_package_version("yunohost")["version"].split(".")[0])
def check_assertions(self):
# Be on buster (10.x) and yunohost 4.x
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be > 9.x but yunohost package
# would still be in 3.x...
if (
not self.debian_major_version() == N_CURRENT_DEBIAN
and not self.yunohost_major_version() == N_CURRENT_YUNOHOST
):
try:
# Here we try to find the previous migration log, which should be somewhat recent and be at least 10k (we keep the biggest one)
maybe_previous_migration_log_id = check_output(
"cd /var/log/yunohost/categories/operation && find -name '*migrate*.log' -size +10k -mtime -100 -exec ls -s {} \\; | sort -n | tr './' ' ' | awk '{print $2}' | tail -n 1"
)
if maybe_previous_migration_log_id:
logger.info(
f"NB: the previous migration log id seems to be {maybe_previous_migration_log_id}. You can share it with the support team with : sudo yunohost log share {maybe_previous_migration_log_id}"
)
except Exception:
# Yeah it's not that important ... it's to simplify support ...
pass
raise YunohostError("migration_0021_not_buster2")
# Have > 1 Go free space on /var/ ?
if free_space_in_directory("/var/") / (1024**3) < 1.0:
raise YunohostError("migration_0021_not_enough_free_space")
# Have > 70 MB free space on /var/ ?
if free_space_in_directory("/boot/") / (1024**2) < 70.0:
raise YunohostError(
"/boot/ has less than 70MB available. This will probably trigger a crash during the upgrade because a new kernel needs to be installed. Please look for advice on the forum on how to remove old, unused kernels to free up some space in /boot/.",
raw_msg=True,
)
# Check system is up to date
# (but we don't if 'bullseye' is already in the sources.list ...
# which means maybe a previous upgrade crashed and we're re-running it)
if os.path.exists("/etc/apt/sources.list") and " bullseye " not in read_file(
"/etc/apt/sources.list"
):
tools_update(target="system")
upgradable_system_packages = list(_list_upgradable_apt_packages())
upgradable_system_packages = [
package["name"] for package in upgradable_system_packages
]
upgradable_system_packages = set(upgradable_system_packages)
# Lime2 have hold packages to avoid ethernet instability
# See https://github.com/YunoHost/arm-images/commit/b4ef8c99554fd1a122a306db7abacc4e2f2942df
lime2_hold_packages = set(
[
"armbian-firmware",
"armbian-bsp-cli-lime2",
"linux-dtb-current-sunxi",
"linux-image-current-sunxi",
"linux-u-boot-lime2-current",
"linux-image-next-sunxi",
]
)
if upgradable_system_packages - lime2_hold_packages:
raise YunohostError("migration_0021_system_not_fully_up_to_date")
@property
def disclaimer(self):
# Avoid having a super long disclaimer + uncessary check if we ain't
# on buster / yunohost 4.x anymore
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be >= 10.x but yunohost package
# would still be in 4.x...
if (
not self.debian_major_version() == N_CURRENT_DEBIAN
and not self.yunohost_major_version() == N_CURRENT_YUNOHOST
):
return None
# Get list of problematic apps ? I.e. not official or community+working
problematic_apps = unstable_apps()
problematic_apps = "".join(["\n - " + app for app in problematic_apps])
# Manually modified files ? (c.f. yunohost service regen-conf)
modified_files = manually_modified_files()
modified_files = "".join(["\n - " + f for f in modified_files])
message = m18n.n("migration_0021_general_warning")
message = (
"N.B.: This migration has been tested by the community over the last few months but has only been declared stable recently. If your server hosts critical services and if you are not too confident with debugging possible issues, we recommend you to wait a little bit more while we gather more feedback and polish things up. If on the other hand you are relatively confident with debugging small issues that may arise, you are encouraged to run this migration ;)! You can read about remaining known issues and feedback from the community here: https://forum.yunohost.org/t/20590\n\n"
+ message
)
if problematic_apps:
message += "\n\n" + m18n.n(
"migration_0021_problematic_apps_warning",
problematic_apps=problematic_apps,
)
if modified_files:
message += "\n\n" + m18n.n(
"migration_0021_modified_files", manually_modified_files=modified_files
)
return message
def patch_apt_sources_list(self):
sources_list = glob.glob("/etc/apt/sources.list.d/*.list")
if os.path.exists("/etc/apt/sources.list"):
sources_list.append("/etc/apt/sources.list")
# This :
# - replace single 'buster' occurence by 'bulleye'
# - comments lines containing "backports"
# - replace 'buster/updates' by 'bullseye/updates' (or same with -)
# Special note about the security suite:
# https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html#security-archive
for f in sources_list:
command = (
f"sed -i {f} "
"-e 's@ buster @ bullseye @g' "
"-e '/backports/ s@^#*@#@' "
"-e 's@ buster/updates @ bullseye-security @g' "
"-e 's@ buster-@ bullseye-@g' "
)
os.system(command)
def get_apps_equivs_packages(self):
command = (
"dpkg --get-selections"
" | grep -v deinstall"
" | awk '{print $1}'"
" | { grep 'ynh-deps$' || true; }"
)
output = check_output(command)
return output.split("\n") if output else []
def hold(self, packages):
for package in packages:
os.system(f"apt-mark hold {package}")
def unhold(self, packages):
for package in packages:
os.system(f"apt-mark unhold {package}")
def apt_install(self, cmd):
def is_relevant(line):
return "Reading database ..." not in line.rstrip()
callbacks = (
lambda l: (
logger.info("+ " + l.rstrip() + "\r")
if _apt_log_line_is_relevant(l)
else logger.debug(l.rstrip() + "\r")
),
lambda l: (
logger.warning(l.rstrip())
if _apt_log_line_is_relevant(l)
else logger.debug(l.rstrip())
),
)
cmd = (
"LC_ALL=C DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt install --quiet -o=Dpkg::Use-Pty=0 --fix-broken --assume-yes "
+ cmd
)
logger.debug("Running: %s" % cmd)
return call_async_output(cmd, callbacks, shell=True)
def patch_yunohost_conflicts(self):
#
# This is a super dirty hack to remove the conflicts from yunohost's debian/control file
# Those conflicts are there to prevent mistakenly upgrading critical packages
# such as dovecot, postfix, nginx, openssl, etc... usually related to mistakenly
# using backports etc.
#
# The hack consists in savagely removing the conflicts directly in /var/lib/dpkg/status
#
# We only patch the conflict if we're on yunohost 4.x
if self.yunohost_major_version() != N_CURRENT_YUNOHOST:
return
conflicts = check_output("dpkg-query -s yunohost | grep '^Conflicts:'").strip()
if conflicts:
# We want to keep conflicting with apache/bind9 tho
new_conflicts = "Conflicts: apache2, bind9"
command = (
f"sed -i /var/lib/dpkg/status -e 's@{conflicts}@{new_conflicts}@g'"
)
logger.debug(f"Running: {command}")
os.system(command)

View file

@ -1,94 +0,0 @@
import os
import glob
from shutil import copy2
from moulinette.utils.log import getActionLogger
from yunohost.app import _is_installed
from yunohost.utils.legacy import _patch_legacy_php_versions_in_settings
from yunohost.tools import Migration
from yunohost.service import _run_service_command
logger = getActionLogger("yunohost.migration")
OLDPHP_POOLS = "/etc/php/7.3/fpm/pool.d"
NEWPHP_POOLS = "/etc/php/7.4/fpm/pool.d"
OLDPHP_SOCKETS_PREFIX = "/run/php/php7.3-fpm"
NEWPHP_SOCKETS_PREFIX = "/run/php/php7.4-fpm"
# Because of synapse é_è
OLDPHP_SOCKETS_PREFIX2 = "/run/php7.3-fpm"
NEWPHP_SOCKETS_PREFIX2 = "/run/php7.4-fpm"
MIGRATION_COMMENT = (
"; YunoHost note : this file was automatically moved from {}".format(OLDPHP_POOLS)
)
class MyMigration(Migration):
"Migrate php7.3-fpm 'pool' conf files to php7.4"
dependencies = ["migrate_to_bullseye"]
def run(self):
# Get list of php7.3 pool files
oldphp_pool_files = glob.glob("{}/*.conf".format(OLDPHP_POOLS))
# Keep only basenames
oldphp_pool_files = [os.path.basename(f) for f in oldphp_pool_files]
# Ignore the "www.conf" (default stuff, probably don't want to touch it ?)
oldphp_pool_files = [f for f in oldphp_pool_files if f != "www.conf"]
for pf in oldphp_pool_files:
# Copy the files to the php7.3 pool
src = "{}/{}".format(OLDPHP_POOLS, pf)
dest = "{}/{}".format(NEWPHP_POOLS, pf)
copy2(src, dest)
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(
OLDPHP_SOCKETS_PREFIX, NEWPHP_SOCKETS_PREFIX, dest
)
os.system(c)
c = "sed -i -e 's@{}@{}@g' {}".format(
OLDPHP_SOCKETS_PREFIX2, NEWPHP_SOCKETS_PREFIX2, dest
)
os.system(c)
# Also add a comment that it was automatically moved from php7.3
# (for human traceability and backward migration)
c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest)
os.system(c)
app_id = os.path.basename(pf)[: -len(".conf")]
if _is_installed(app_id):
_patch_legacy_php_versions_in_settings(
"/etc/yunohost/apps/%s/" % app_id
)
nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/%s.conf" % app_id)
for nf in nginx_conf_files:
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(
OLDPHP_SOCKETS_PREFIX, NEWPHP_SOCKETS_PREFIX, nf
)
os.system(c)
c = "sed -i -e 's@{}@{}@g' {}".format(
OLDPHP_SOCKETS_PREFIX2, NEWPHP_SOCKETS_PREFIX2, nf
)
os.system(c)
os.system(
"rm /etc/logrotate.d/php7.3-fpm"
) # We remove this otherwise the logrotate cron will be unhappy
# Reload/restart the php pools
os.system("systemctl stop php7.3-fpm")
os.system("systemctl disable php7.3-fpm")
_run_service_command("restart", "php7.4-fpm")
_run_service_command("enable", "php7.4-fpm")
# Reload nginx
_run_service_command("reload", "nginx")

View file

@ -1,42 +0,0 @@
import os
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json, write_to_yaml
from yunohost.tools import Migration
from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings
logger = getActionLogger("yunohost.migration")
SETTINGS_PATH = "/etc/yunohost/settings.yml"
OLD_SETTINGS_PATH = "/etc/yunohost/settings.json"
class MyMigration(Migration):
"Migrate old global settings to the new ConfigPanel global settings"
dependencies = ["migrate_to_bullseye"]
def run(self):
if not os.path.exists(OLD_SETTINGS_PATH):
return
try:
old_settings = read_json(OLD_SETTINGS_PATH)
except Exception as e:
raise YunohostError(f"Can't open setting file : {e}", raw_msg=True)
settings = {
translate_legacy_settings_to_configpanel_settings(k).split(".")[-1]: v[
"value"
]
for k, v in old_settings.items()
}
if settings.get("smtp_relay_host"):
settings["smtp_relay_enabled"] = True
# Here we don't use settings_set() from settings.py to prevent
# Questions to be asked when one run the migration from CLI.
write_to_yaml(SETTINGS_PATH, settings)

View file

@ -1,153 +0,0 @@
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
logger = getActionLogger("yunohost.migration")
###################################################
# Tools used also for restoration
###################################################
class MyMigration(Migration):
"""
Add new permissions around SSH/SFTP features
"""
introduced_in_version = "11.1" # FIXME?
dependencies = []
ldap_migration_started = False
@Migration.ldap_migration
def run(self, *args):
from yunohost.user import (
user_list,
user_info,
user_group_update,
user_update,
user_group_add_mailalias,
ADMIN_ALIASES,
)
from yunohost.utils.ldap import _get_ldap_interface
from yunohost.permission import permission_sync_to_user
from yunohost.domain import _get_maindomain
main_domain = _get_maindomain()
ldap = _get_ldap_interface()
all_users = user_list()["users"].keys()
new_admin_user = None
for user in all_users:
if any(
alias.startswith("root@")
for alias in user_info(user).get("mail-aliases", [])
):
new_admin_user = user
break
# For some reason some system have no user with root@ alias,
# but the user does has admin / postmaster / ... alias
# ... try to find it instead otherwise this creashes the migration
# later because the admin@, postmaster@, .. aliases will already exist
if not new_admin_user:
for user in all_users:
aliases = user_info(user).get("mail-aliases", [])
if any(
alias.startswith(f"admin@{main_domain}") for alias in aliases
) or any(
alias.startswith(f"postmaster@{main_domain}") for alias in aliases
):
new_admin_user = user
break
self.ldap_migration_started = True
if new_admin_user:
aliases = user_info(new_admin_user).get("mail-aliases", [])
old_admin_aliases_to_remove = [
alias
for alias in aliases
if any(
alias.startswith(a)
for a in [
"root@",
"admin@",
"admins@",
"webmaster@",
"postmaster@",
"abuse@",
]
)
]
user_update(new_admin_user, remove_mailalias=old_admin_aliases_to_remove)
admin_hashs = ldap.search("cn=admin", attrs={"userPassword"})[0]["userPassword"]
stuff_to_delete = [
"cn=admin,ou=sudo",
"cn=admin",
"cn=admins,ou=groups",
]
for stuff in stuff_to_delete:
if ldap.search(stuff):
ldap.remove(stuff)
ldap.add(
"cn=admins,ou=sudo",
{
"cn": ["admins"],
"objectClass": ["top", "sudoRole"],
"sudoCommand": ["ALL"],
"sudoUser": ["%admins"],
"sudoHost": ["ALL"],
},
)
ldap.add(
"cn=admins,ou=groups",
{
"cn": ["admins"],
"objectClass": ["top", "posixGroup", "groupOfNamesYnh"],
"gidNumber": ["4001"],
},
)
user_group_add_mailalias(
"admins", [f"{alias}@{main_domain}" for alias in ADMIN_ALIASES]
)
permission_sync_to_user()
if new_admin_user:
user_group_update(groupname="admins", add=new_admin_user, sync_perm=True)
# Re-add admin as a regular user
attr_dict = {
"objectClass": [
"mailAccount",
"inetOrgPerson",
"posixAccount",
"userPermissionYnh",
],
"givenName": ["Admin"],
"sn": ["Admin"],
"displayName": ["Admin"],
"cn": ["Admin"],
"uid": ["admin"],
"mail": "admin_legacy",
"maildrop": ["admin"],
"mailuserquota": ["0"],
"userPassword": admin_hashs,
"gidNumber": ["1007"],
"uidNumber": ["1007"],
"homeDirectory": ["/home/admin"],
"loginShell": ["/bin/bash"],
}
ldap.add("uid=admin,ou=users", attr_dict)
user_group_update(groupname="admins", add="admin", sync_perm=True)
def run_after_system_restore(self):
self.run()

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