Merge branch 'stretch-unstable' into permission_protection

This commit is contained in:
Josué Tille 2020-04-09 16:21:48 +02:00
commit 52e2be8fe6
No known key found for this signature in database
GPG key ID: 716A6C99B04194EF
21 changed files with 276 additions and 243 deletions

View file

@ -296,6 +296,13 @@ user:
help: Display all info known about each permission, including the full user list of each group it is granted to.
action: store_true
### user_permission_info()
info:
action_help: Get information about a specific permission
api: GET /users/permissions/<permission>
arguments:
permission:
help: Name of the permission to fetch info about
### user_permission_update()
update:

View file

@ -3,7 +3,7 @@ Simple automated generation of a bash_completion file
for yunohost command from the actionsmap.
Generates a bash completion file assuming the structure
`yunohost domain action`
`yunohost category action`
adds `--help` at the end if one presses [tab] again.
author: Christophe Vuillot
@ -15,18 +15,39 @@ THIS_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
ACTIONSMAP_FILE = THIS_SCRIPT_DIR + '/yunohost.yml'
BASH_COMPLETION_FILE = THIS_SCRIPT_DIR + '/../bash-completion.d/yunohost'
def get_dict_actions(OPTION_SUBTREE, category):
ACTIONS = [action for action in OPTION_SUBTREE[category]["actions"].keys()
if not action.startswith('_')]
ACTIONS_STR = '{}'.format(' '.join(ACTIONS))
DICT = { "actions_str": ACTIONS_STR }
return DICT
with open(ACTIONSMAP_FILE, 'r') as stream:
# Getting the dictionary containning what actions are possible per domain
# Getting the dictionary containning what actions are possible per category
OPTION_TREE = yaml.load(stream)
DOMAINS = [str for str in OPTION_TREE.keys() if not str.startswith('_')]
DOMAINS_STR = '"{}"'.format(' '.join(DOMAINS))
CATEGORY = [category for category in OPTION_TREE.keys() if not category.startswith('_')]
CATEGORY_STR = '{}'.format(' '.join(CATEGORY))
ACTIONS_DICT = {}
for domain in DOMAINS:
ACTIONS = [str for str in OPTION_TREE[domain]['actions'].keys()
if not str.startswith('_')]
ACTIONS_STR = '"{}"'.format(' '.join(ACTIONS))
ACTIONS_DICT[domain] = ACTIONS_STR
for category in CATEGORY:
ACTIONS_DICT[category] = get_dict_actions(OPTION_TREE, category)
ACTIONS_DICT[category]["subcategories"] = {}
ACTIONS_DICT[category]["subcategories_str"] = ""
if "subcategories" in OPTION_TREE[category].keys():
SUBCATEGORIES = [ subcategory for subcategory in OPTION_TREE[category]["subcategories"].keys() ]
SUBCATEGORIES_STR = '{}'.format(' '.join(SUBCATEGORIES))
ACTIONS_DICT[category]["subcategories_str"] = SUBCATEGORIES_STR
for subcategory in SUBCATEGORIES:
ACTIONS_DICT[category]["subcategories"][subcategory] = get_dict_actions(OPTION_TREE[category]["subcategories"], subcategory)
with open(BASH_COMPLETION_FILE, 'w') as generated_file:
@ -47,31 +68,49 @@ with open(ACTIONSMAP_FILE, 'r') as stream:
generated_file.write('\tnarg=${#COMP_WORDS[@]}\n\n')
generated_file.write('\t# the current word being typed\n')
generated_file.write('\tcur="${COMP_WORDS[COMP_CWORD]}"\n\n')
generated_file.write('\t# the last typed word\n')
generated_file.write('\tprev="${COMP_WORDS[COMP_CWORD-1]}"\n\n')
# If one is currently typing a domain then match with the domain list
generated_file.write('\t# If one is currently typing a domain,\n')
generated_file.write('\t# match with domains\n')
# If one is currently typing a category then match with the category list
generated_file.write('\t# If one is currently typing a category,\n')
generated_file.write('\t# match with categorys\n')
generated_file.write('\tif [[ $narg == 2 ]]; then\n')
generated_file.write('\t\topts={}\n'.format(DOMAINS_STR))
generated_file.write('\t\topts="{}"\n'.format(CATEGORY_STR))
generated_file.write('\tfi\n\n')
# If one is currently typing an action then match with the action list
# of the previously typed domain
generated_file.write('\t# If one already typed a domain,\n')
generated_file.write('\t# match the actions of that domain\n')
# of the previously typed category
generated_file.write('\t# If one already typed a category,\n')
generated_file.write('\t# match the actions or the subcategories of that category\n')
generated_file.write('\tif [[ $narg == 3 ]]; then\n')
for domain in DOMAINS:
generated_file.write('\t\tif [[ $prev == "{}" ]]; then\n'.format(domain))
generated_file.write('\t\t\topts={}\n'.format(ACTIONS_DICT[domain]))
generated_file.write('\t\t# the category typed\n')
generated_file.write('\t\tcategory="${COMP_WORDS[1]}"\n\n')
for category in CATEGORY:
generated_file.write('\t\tif [[ $category == "{}" ]]; then\n'.format(category))
generated_file.write('\t\t\topts="{} {}"\n'.format(ACTIONS_DICT[category]["actions_str"], ACTIONS_DICT[category]["subcategories_str"]))
generated_file.write('\t\tfi\n')
generated_file.write('\tfi\n\n')
# If both domain and action have been typed or the domain
generated_file.write('\t# If one already typed an action or a subcategory,\n')
generated_file.write('\t# match the actions of that subcategory\n')
generated_file.write('\tif [[ $narg == 4 ]]; then\n')
generated_file.write('\t\t# the category typed\n')
generated_file.write('\t\tcategory="${COMP_WORDS[1]}"\n\n')
generated_file.write('\t\t# the action or the subcategory typed\n')
generated_file.write('\t\taction_or_subcategory="${COMP_WORDS[2]}"\n\n')
for category in CATEGORY:
if len(ACTIONS_DICT[category]["subcategories"]):
generated_file.write('\t\tif [[ $category == "{}" ]]; then\n'.format(category))
for subcategory in ACTIONS_DICT[category]["subcategories"]:
generated_file.write('\t\t\tif [[ $action_or_subcategory == "{}" ]]; then\n'.format(subcategory))
generated_file.write('\t\t\t\topts="{}"\n'.format(ACTIONS_DICT[category]["subcategories"][subcategory]["actions_str"]))
generated_file.write('\t\t\tfi\n')
generated_file.write('\t\tfi\n')
generated_file.write('\tfi\n\n')
# If both category and action have been typed or the category
# was not recognized propose --help (only once)
generated_file.write('\t# If no options were found propose --help\n')
generated_file.write('\tif [ -z "$opts" ]; then\n')
generated_file.write('\t\tprev="${COMP_WORDS[COMP_CWORD-1]}"\n\n')
generated_file.write('\t\tif [[ $prev != "--help" ]]; then\n')
generated_file.write('\t\t\topts=( --help )\n')
generated_file.write('\t\tfi\n')

View file

@ -94,7 +94,7 @@ ynh_package_version() {
# Requires YunoHost version 2.4.0.3 or higher.
ynh_apt() {
ynh_wait_dpkg_free
DEBIAN_FRONTEND=noninteractive apt-get -y $@
LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get -y $@
}
# Update package index files
@ -184,7 +184,7 @@ ynh_package_install_from_equivs () {
ynh_wait_dpkg_free
cp "$controlfile" "${TMPDIR}/control"
(cd "$TMPDIR"
equivs-build ./control 1> /dev/null
LC_ALL=C equivs-build ./control 1> /dev/null
dpkg --force-depends -i "./${pkgname}_${pkgversion}_all.deb" 2>&1)
# If install fails we use "apt-get check" to try to debug and diagnose possible unmet dependencies
# Note the use of { } which allows to group commands without starting a subshell (otherwise the ynh_die wouldn't exit the current shell).

View file

@ -17,7 +17,7 @@ ynh_find_port () {
ynh_handle_getopts_args "$@"
test -n "$port" || ynh_die --message="The argument of ynh_find_port must be a valid port."
while ss -nltu | grep -q -w :$port # Check if the port is free
while ss -nltu | awk '{print$5}' | grep -q -E ":$port$" # Check if the port is free
do
port=$((port+1)) # Else, pass to next port
done

View file

@ -243,7 +243,6 @@ ynh_psql_remove_db() {
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local psql_root_password=$(cat $PSQL_ROOT_PWD_FILE)
if ynh_psql_database_exists --database=$db_name; then # Check if the database exists
ynh_psql_drop_db $db_name # Remove the database
else
@ -265,7 +264,9 @@ ynh_psql_remove_db() {
ynh_psql_test_if_first_run() {
if [ -f "$PSQL_ROOT_PWD_FILE" ]; then
echo "PostgreSQL is already installed, no need to create master password"
else
return
fi
local psql_root_password="$(ynh_string_random)"
echo "$psql_root_password" >$PSQL_ROOT_PWD_FILE
@ -294,5 +295,4 @@ ynh_psql_test_if_first_run() {
systemctl enable postgresql
ynh_systemd_action --service_name=postgresql --action=reload
fi
}

View file

@ -59,98 +59,6 @@ ynh_app_setting_delete() {
ynh_app_setting "delete" "$app" "$key"
}
# Add skipped_uris urls into the config
#
# usage: ynh_add_skipped_uris [--appid=app] --url=url1,url2 [--regex]
# | arg: -a, --appid - the application id
# | arg: -u, --url - the urls to add to the sso for this app
# | arg: -r, --regex - Use the key 'skipped_regex' instead of 'skipped_uris'
#
# An URL set with 'skipped_uris' key will be totally ignored by the SSO,
# which means that the access will be public and the logged-in user information will not be passed to the app.
#
# Requires YunoHost version 3.6.0 or higher.
ynh_add_skipped_uris() {
# Declare an array to define the options of this helper.
local legacy_args=aur
declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex )
local appid
local url
local regex
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
appid={appid:-$app}
regex={regex:-0}
local key=skipped_uris
if [ $regex -eq 1 ]; then
key=skipped_regex
fi
ynh_app_setting_set --app=$appid --key=$key --value="$url"
}
# Add unprotected_uris urls into the config
#
# usage: ynh_add_unprotected_uris [--appid=app] --url=url1,url2 [--regex]
# | arg: -a, --appid - the application id
# | arg: -u, --url - the urls to add to the sso for this app
# | arg: -r, --regex - Use the key 'unprotected_regex' instead of 'unprotected_uris'
#
# An URL set with unprotected_uris key will be accessible publicly, but if an user is logged in,
# his information will be accessible (through HTTP headers) to the app.
#
# Requires YunoHost version 3.6.0 or higher.
ynh_add_unprotected_uris() {
# Declare an array to define the options of this helper.
local legacy_args=aur
declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex )
local appid
local url
local regex
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
appid={appid:-$app}
regex={regex:-0}
local key=unprotected_uris
if [ $regex -eq 1 ]; then
key=unprotected_regex
fi
ynh_app_setting_set --app=$appid --key=$key --value="$url"
}
# Add protected_uris urls into the config
#
# usage: ynh_add_protected_uris [--appid=app] --url=url1,url2 [--regex]
# | arg: -a, --appid - the application id
# | arg: -u, --url - the urls to add to the sso for this app
# | arg: -r, --regex - Use the key 'protected_regex' instead of 'protected_uris'
#
# An URL set with protected_uris will be blocked by the SSO and accessible only to authenticated and authorized users.
#
# Requires YunoHost version 3.6.0 or higher.
ynh_add_protected_uris() {
# Declare an array to define the options of this helper.
local legacy_args=aur
declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex )
local appid
local url
local regex
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
appid={appid:-$app}
regex={regex:-0}
local key=protected_uris
if [ $regex -eq 1 ]; then
key=protected_regex
fi
ynh_app_setting_set --app=$appid --key=$key --value="$url"
}
# Small "hard-coded" interface to avoid calling "yunohost app" directly each
# time dealing with a setting is needed (which may be so slow on ARM boards)
#
@ -197,7 +105,7 @@ EOF
if [[ "$1" == "set" ]] && [[ "${4:-}" == "/" ]]
then
ynh_permission_update --permission "main" --add "visitors"
elif [[ "$1" == "delete" ]] && [[ "${current_value:-}" == "/" ]]
elif [[ "$1" == "delete" ]] && [[ "${current_value:-}" == "/" ]] && [[ -n "$(ynh_app_setting_get --app=$2 --key='is_public' )" ]]
then
ynh_permission_update --permission "main" --remove "visitors"
fi
@ -279,6 +187,8 @@ ynh_webpath_register () {
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_create() {
# Declare an array to define the options of this helper.
local legacy_args=puAhaltP
declare -Ar args_array=( [p]=permission= [u]=url= [A]=additional_urls= [h]=auth_header= [a]=allowed= [l]=label= [t]=show_tile= [P]=protected= )
local permission
local url
@ -344,6 +254,8 @@ ynh_permission_create() {
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_delete() {
# Declare an array to define the options of this helper.
local legacy_args=p
declare -Ar args_array=( [p]=permission= )
local permission
ynh_handle_getopts_args "$@"
@ -358,6 +270,8 @@ ynh_permission_delete() {
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_exists() {
# Declare an array to define the options of this helper.
local legacy_args=p
declare -Ar args_array=( [p]=permission= )
local permission
ynh_handle_getopts_args "$@"
@ -378,6 +292,8 @@ ynh_permission_exists() {
#
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_url() {
# Declare an array to define the options of this helper.
local legacy_args=puarhc
declare -Ar args_array=([p]=permission= [u]=url= [a]=add_url= [r]=remove_url= [h]=auth_header= [c]=clear_urls)
local permission
local url
@ -430,6 +346,8 @@ ynh_permission_url() {
# example: ynh_permission_update --permission admin --add samdoe --remove all_users
# Requires YunoHost version 3.7.0 or higher.
ynh_permission_update() {
# Declare an array to define the options of this helper.
local legacy_args=parlsp
declare -Ar args_array=( [p]=permission= [a]=add= [r]=remove= [l]=label= [t]=show_tile= [P]=protected= )
local permission
local add
@ -468,3 +386,29 @@ ynh_permission_update() {
yunohost tools shell -c "from yunohost.permission import user_permission_update; user_permission_update('$app.$permission' ${add:-} ${remove:-} ${label:-} ${show_tile:-} ${protected:-} , force=True, sync_perm=False)"
}
# Check if a permission exists
#
# usage: ynh_permission_has_user --permission=permission --user=user
# | arg: -p, --permission - the permission to check
# | arg: -u, --user - the user seek in the permission
#
# example: ynh_permission_has_user --permission=main --user=visitors
#
# Requires YunoHost version 3.7.1 or higher.
ynh_permission_has_user() {
local legacy_args=pu
# Declare an array to define the options of this helper.
declare -Ar args_array=( [p]=permission= [u]=user= )
local permission
local user
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ! ynh_permission_exists --permission=$permission
then
return 1
fi
yunohost user permission info "$app.$permission" | grep -w -q "$user"
}

View file

@ -238,8 +238,13 @@ ynh_local_curl () {
# Wait untils nginx has fully reloaded (avoid curl fail with http2)
sleep 2
local cookiefile=/tmp/ynh-$app-cookie.txt
touch $cookiefile
chown root $cookiefile
chmod 700 $cookiefile
# Curl the URL
curl --silent --show-error -kL -H "Host: $domain" --resolve $domain:443:127.0.0.1 $POST_data "$full_page_url" --cookie-jar /tmp/ynh-$app-cookie.txt --cookie /tmp/ynh-$app-cookie.txt
curl --silent --show-error -kL -H "Host: $domain" --resolve $domain:443:127.0.0.1 $POST_data "$full_page_url" --cookie-jar $cookiefile --cookie $cookiefile
}
# Render templates with Jinja2

View file

@ -23,6 +23,7 @@ do_init_regen() {
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"
# Restart nginx if conf looks good, otherwise display error and exit unhappy

View file

@ -28,21 +28,24 @@ class DNSRecordsDiagnoser(Diagnoser):
all_domains = domain_list()["domains"]
for domain in all_domains:
self.logger_debug("Diagnosing DNS conf for %s" % domain)
for report in self.check_domain(domain, domain == main_domain):
is_subdomain = domain.split(".",1)[1] in all_domains
for report in self.check_domain(domain, domain == main_domain, is_subdomain=is_subdomain):
yield report
# FIXME : somewhere, should implement a check for reverse DNS ...
# FIXME / TODO : somewhere, could also implement a check for domain expiring soon
def check_domain(self, domain, is_main_domain):
def check_domain(self, domain, is_main_domain, is_subdomain):
expected_configuration = _build_dns_conf(domain)
# Here if there are no AAAA record, we should add something to expect "no" AAAA record
# FIXME: Here if there are no AAAA record, we should add something to expect "no" AAAA record
# to properly diagnose situations where people have a AAAA record but no IPv6
categories = ["basic", "mail", "xmpp", "extra"]
if is_subdomain:
categories = ["basic"]
for category in categories:
records = expected_configuration[category]

View file

@ -20,6 +20,9 @@ ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECD
#ssl_dhparam /etc/ssl/private/dh2048.pem;
{% endif %}
# Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners
# https://wiki.mozilla.org/Security/Guidelines/Web_Security
# https://observatory.mozilla.org/
more_set_headers "Content-Security-Policy : upgrade-insecure-requests";
more_set_headers "Content-Security-Policy-Report-Only : default-src https: data: 'unsafe-inline' 'unsafe-eval'";
more_set_headers "X-Content-Type-Options : nosniff";

View file

@ -15,48 +15,14 @@ server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include /etc/nginx/conf.d/security.conf.inc;
ssl_certificate /etc/yunohost/certs/yunohost.org/crt.pem;
ssl_certificate_key /etc/yunohost/certs/yunohost.org/key.pem;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
{% if compatibility == "modern" %}
# Ciphers with modern compatibility
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
# Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
{% else %}
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
ssl_prefer_server_ciphers on;
# Ciphers with intermediate compatibility
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=intermediate
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
# Uncomment the following directive after DH generation
# > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048
#ssl_dhparam /etc/ssl/private/dh2048.pem;
{% endif %}
# Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners
# https://wiki.mozilla.org/Security/Guidelines/Web_Security
# https://observatory.mozilla.org/
more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
more_set_headers "Referrer-Policy : 'same-origin'";
more_set_headers "Content-Security-Policy : upgrade-insecure-requests; object-src 'none'; script-src https: 'unsafe-eval'";
more_set_headers "X-Content-Type-Options : nosniff";
more_set_headers "X-XSS-Protection : 1; mode=block";
more_set_headers "X-Download-Options : noopen";
more_set_headers "X-Permitted-Cross-Domain-Policies : none";
more_set_headers "X-Frame-Options : SAMEORIGIN";
# Disable gzip to protect against BREACH
# Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!)
gzip off;
location / {
return 302 https://$http_host/yunohost/admin;

26
debian/control vendored
View file

@ -15,22 +15,23 @@ Depends: ${python:Depends}, ${misc:Depends}
, python-psutil, python-requests, python-dnspython, python-openssl
, python-apt, python-miniupnpc, python-dbus, python-jinja2
, python-toml
, apt-transport-https
, dnsutils, bind9utils, unzip, git, curl, cron, wget, jq
, ca-certificates, netcat-openbsd, iproute2
, apt, apt-transport-https
, nginx, nginx-extras (>=1.6.2)
, php-fpm, php-ldap, php-intl
, mariadb-server, php-mysql | php-mysqlnd
, openssh-server, iptables, fail2ban, dnsutils, bind9utils
, openssl, ca-certificates, netcat-openbsd, iproute2
, slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd
, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre, procmail, mailutils, postsrsd
, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved
, dovecot-antispam, fail2ban, iptables
, nginx-extras (>=1.6.2), php-fpm, php-ldap, php-intl
, dnsmasq, openssl, avahi-daemon, libnss-mdns, resolvconf, libnss-myhostname
, dnsmasq, avahi-daemon, libnss-mdns, resolvconf, libnss-myhostname
, postfix, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre
, dovecot-core, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved, dovecot-antispam
, rspamd (>= 1.6.0), opendkim-tools, postsrsd, procmail, mailutils
, redis-server
, metronome
, rspamd (>= 1.6.0), redis-server, opendkim-tools
, haveged, fake-hwclock
, equivs, lsof
, git, curl, wget, cron, unzip, jq
, lsb-release, haveged, fake-hwclock, equivs, lsof
Recommends: yunohost-admin
, openssh-server, ntp, inetutils-ping | iputils-ping
, ntp, inetutils-ping | iputils-ping
, bash-completion, rsyslog
, php-gd, php-curl, php-gettext, php-mcrypt
, python-pip
@ -43,6 +44,7 @@ Conflicts: iptables-persistent
, yunohost-config-dovecot, yunohost-config-slapd
, yunohost-config-nginx, yunohost-config-amavis
, yunohost-config-mysql, yunohost-predepends
, apache2, bind9
Replaces: moulinette-yunohost, yunohost-config
, yunohost-config-others, yunohost-config-postfix
, yunohost-config-dovecot, yunohost-config-slapd

View file

@ -112,7 +112,7 @@
"restore_complete": "Restaurada",
"restore_confirm_yunohost_installed": "¿Realmente desea restaurar un sistema ya instalado? [{answers:s}]",
"restore_failed": "No se pudo restaurar el sistema",
"restore_hook_unavailable": "El guión de restauración para «{part:s}» no está disponible en su sistema y tampoco en el archivo",
"restore_hook_unavailable": "El script de restauración para «{part:s}» no está disponible en su sistema y tampoco en el archivo",
"restore_nothings_done": "No se ha restaurado nada",
"restore_running_app_script": "Restaurando la aplicación «{app:s}»…",
"restore_running_hooks": "Ejecutando los ganchos de restauración…",
@ -273,13 +273,14 @@
"restore_system_part_failed": "No se pudo restaurar la parte del sistema «{part:s}»",
"restore_removing_tmp_dir_failed": "No se pudo eliminar un directorio temporal antiguo",
"restore_not_enough_disk_space": "Espacio insuficiente (espacio: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)",
"restore_may_be_not_enough_disk_space": "Parece que su sistema no tiene suficiente espacio (libre: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)",
"restore_mounting_archive": "Montando archivo en «{path:s}»",
"restore_may_be_not_enough_disk_space": "Parece que su sistema no tiene suficiente espacio libre (libre: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)",
"restore_extracting": "Extrayendo los archivos necesarios para el archivo…",
"regenconf_pending_applying": "Aplicando la configuración pendiente para la categoría «{category}»…",
"regenconf_failed": "No se pudo regenerar la configuración para la(s) categoría(s): {categories}",
"regenconf_dry_pending_applying": "Comprobando la configuración pendiente que habría sido aplicada para la categoría «{category}»…",
"regenconf_would_be_updated": "La configuración habría sido actualizada para la categoría «{category}»",
"regenconf_updated": "Actualizada la configuración para la categoría «{category}»",
"regenconf_updated": "Actualizada la configuración para la categoría '{category}'",
"regenconf_up_to_date": "Ya está actualizada la configuración para la categoría «{category}»",
"regenconf_now_managed_by_yunohost": "El archivo de configuración «{conf}» está gestionado ahora por YunoHost (categoría {category}).",
"regenconf_file_updated": "Actualizado el archivo de configuración «{conf}»",
@ -292,7 +293,9 @@
"regenconf_file_backed_up": "Archivo de configuración «{conf}» respaldado en «{backup}»",
"permission_update_nothing_to_do": "No hay permisos para actualizar",
"permission_updated": "Actualizado el permiso «{permission:s}»",
"permission_update_failed": "No se pudo actualizar el permiso «{permission}» : {error}",
"permission_generated": "Actualizada la base de datos de permisos",
"permission_update_failed": "No se pudo actualizar el permiso '{permission}': {error}",
"permission_name_not_valid": "Elija un nombre de permiso permitido para «{permission:s}",
"permission_not_found": "No se encontró el permiso «{permission:s}»",
"permission_deletion_failed": "No se pudo eliminar el permiso «{permission}»: {error}",
"permission_deleted": "Eliminado el permiso «{permission:s}»",
@ -329,7 +332,7 @@
"migration_0011_can_not_backup_before_migration": "El respaldo del sistema no se pudo completar antes de que la migración fallase. Error: {error:s}",
"migration_0011_backup_before_migration": "Creando un respaldo de la base de datos de LDAP y de la configuración de las aplicaciones antes de la migración real.",
"migration_0009_not_needed": "La migración ya ocurrió de algún modo… (?) Omitiendo.",
"migration_0008_no_warning": "Ignorar su configuración SSH debería ser seguro ¡aunque esto no se puede prometer! Ejecute la migración para ignorarla. Por otra parte puede omitir la migración, aunque no se recomienda.",
"migration_0008_no_warning": "Sobre escribir su configuración SSH debería ser seguro ¡aunque esto no se puede prometer! Ejecute la migración para ignorarla. Por otra parte puede omitir la migración, aunque no se recomienda.",
"migration_0008_warning": "Si entiende esos avisos y quiere que YunoHost ignore su configuración actual, ejecute la migración. Por otra parte puede omitir la migración, aunque no se recomienda.",
"migration_0008_dsa": "• Se desactivará la clave DSA. Así que podría tener que anular un aviso espeluznante de su cliente SSH y volver a comprobar la huella de su servidor;",
"migration_0008_root": "• No podrá conectarse como «root» a través de SSH. En su lugar debe usar el usuario «admin»;",
@ -339,7 +342,7 @@
"migration_0007_cancelled": "No se pudo mejorar el modo en el que se gestiona su configuración de SSH.",
"migration_0006_disclaimer": "YunoHost espera ahora que las contraseñas de «admin» y «root» estén sincronizadas. Esta migración reemplaza su contraseña de «root» por la contraseña de «admin».",
"migration_0005_not_enough_space": "Tenga suficiente espacio libre disponible en {path} para ejecutar la migración.",
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 está instalado pero no PostgreSQL 9.6 Algo raro podría haber ocurrido en su sistema:(…",
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 está instalado pero no PostgreSQL 9.6. Algo raro podría haber ocurrido en su sistema:(…",
"migration_0005_postgresql_94_not_installed": "PostgreSQL no estaba instalado en su sistema. Nada que hacer.",
"migration_0003_modified_files": "Tenga en cuenta que se encontró que los siguientes archivos fueron modificados manualmente y podrían ser sobrescritos después de la actualización: {manually_modified_files}",
"migration_0003_problematic_apps_warning": "Tenga en cuenta que las aplicaciones listadas mas abajo fueron detectadas como 'posiblemente problemáticas'. Parece que no fueron instaladas desde una lista de aplicaciones o no estaban etiquetadas como 'funcional'. Así que no hay garantía de que aún funcionen después de la actualización: {problematic_apps}",

View file

@ -381,7 +381,8 @@
"migration_0008_dsa": "- La clé DSA sera désactivée. Par conséquent, il se peut que vous ayez besoin d'invalider un avertissement effrayant de votre client SSH afin de revérifier l'empreinte de votre serveur ;",
"migration_0008_warning": "Si vous comprenez ces avertissements et souhaitez que YunoHost écrase votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également ignorer la migration, bien que cela ne soit pas recommandé.",
"migration_0008_no_warning": "Remplacer votre configuration SSH devrait être sûr, bien que cela ne puisse être promis! Exécutez la migration pour la remplacer. Sinon, vous pouvez également ignorer la migration, bien que cela ne soit pas recommandé.",
"pattern_password_app": "Désolé, les mots de passe ne doivent pas contenir les caractères suivants : {forbidden_chars}",
"migrations_success": "Migration {number} {name} réussie !",
"pattern_password_app": "Désolé, les mots de passe ne peuvent pas contenir les caractères suivants : {forbidden_chars}",
"root_password_replaced_by_admin_password": "Votre mot de passe root a été remplacé par votre mot de passe administrateur.",
"service_reload_failed": "Impossible de recharger le service '{service:s}'.\n\nJournaux historisés récents de ce service : {logs:s}",
"service_reloaded": "Le service « {service:s} » a été rechargé",
@ -594,5 +595,8 @@
"diagnosis_never_ran_yet": "Il apparaît que le serveur a été installé récemment et qu'il n'y a pas encore eu de diagnostic. Vous devriez en lancer un depuis le webmin ou en utilisant 'yunohost diagnosis run' depuis la ligne de commande.",
"diagnosis_description_web": "Web",
"diagnosis_basesystem_hardware_board": "Le modèle de carte du serveur est {model}",
"diagnosis_basesystem_hardware": "L'architecture du serveur est {virt} {arch}"
"diagnosis_basesystem_hardware": "L'architecture du serveur est {virt} {arch}",
"group_already_exist_on_system_but_removing_it": "Le groupe {group} est déjà présent dans les groupes du système, mais Yuhonost va le supprimer…",
"certmanager_warning_subdomain_dns_record": "Le sous-domaine '{subdomain:s}' ne résout pas vers la même adresse IP que '{domain:s}'. Certaines fonctionnalités seront indisponibles tant que vous naurez pas corrigé cela et regénéré le certificat.",
"domain_cannot_add_xmpp_upload": "Vous ne pouvez pas ajouter de domaine commençant par 'xmpp-upload.'. Ce type de nom est réservé à la fonctionnalité dupload XMPP intégrée dans Yunohost."
}

View file

@ -1575,6 +1575,7 @@ def app_config_apply(operation_logger, app, args):
logger.success("Config updated as expected")
return {
"app": app,
"logs": operation_logger.success(),
}

View file

@ -236,8 +236,7 @@ def domain_dns_conf(domain, ttl=None):
for record in record_list:
result += "\n{name} {ttl} IN {type} {value}".format(**record)
is_cli = True if msettings.get('interface') == 'cli' else False
if is_cli:
if msettings.get('interface') == 'cli':
logger.info(m18n.n("domain_dns_conf_is_just_a_recommendation"))
return result
@ -471,10 +470,8 @@ def _build_dns_conf(domain, ttl=3600):
"basic": [
# if ipv4 available
{"type": "A", "name": "@", "value": "123.123.123.123", "ttl": 3600},
{"type": "A", "name": "*", "value": "123.123.123.123", "ttl": 3600},
# if ipv6 available
{"type": "AAAA", "name": "@", "value": "valid-ipv6", "ttl": 3600},
{"type": "AAAA", "name": "*", "value": "valid-ipv6", "ttl": 3600},
],
"xmpp": [
{"type": "SRV", "name": "_xmpp-client._tcp", "value": "0 5 5222 domain.tld.", "ttl": 3600},
@ -491,6 +488,10 @@ def _build_dns_conf(domain, ttl=3600):
{"type": "TXT", "name": "_dmarc", "value": "\"v=DMARC1; p=none\"", "ttl": 3600}
],
"extra": [
# if ipv4 available
{"type": "A", "name": "*", "value": "123.123.123.123", "ttl": 3600},
# if ipv6 available
{"type": "AAAA", "name": "*", "value": "valid-ipv6", "ttl": 3600},
{"type": "CAA", "name": "@", "value": "128 issue \"letsencrypt.org\"", "ttl": 3600},
],
"example_of_a_custom_rule": [
@ -502,32 +503,21 @@ def _build_dns_conf(domain, ttl=3600):
ipv4 = get_public_ip()
ipv6 = get_public_ip(6)
basic = []
###########################
# Basic ipv4/ipv6 records #
###########################
# Basic ipv4/ipv6 records
basic = []
if ipv4:
basic += [
["@", ttl, "A", ipv4],
["*", ttl, "A", ipv4],
]
basic.append(["@", ttl, "A", ipv4])
if ipv6:
basic += [
["@", ttl, "AAAA", ipv6],
["*", ttl, "AAAA", ipv6],
]
basic.append(["@", ttl, "AAAA", ipv6])
# XMPP
xmpp = [
["_xmpp-client._tcp", ttl, "SRV", "0 5 5222 %s." % domain],
["_xmpp-server._tcp", ttl, "SRV", "0 5 5269 %s." % domain],
["muc", ttl, "CNAME", "@"],
["pubsub", ttl, "CNAME", "@"],
["vjud", ttl, "CNAME", "@"],
["xmpp-upload", ttl, "CNAME", "@"],
]
#########
# Email #
#########
# SPF record
spf_record = '"v=spf1 a mx'
if ipv4:
spf_record += ' ip4:{ip4}'.format(ip4=ipv4)
@ -535,7 +525,6 @@ def _build_dns_conf(domain, ttl=3600):
spf_record += ' ip6:{ip6}'.format(ip6=ipv6)
spf_record += ' -all"'
# Email
mail = [
["@", ttl, "MX", "10 %s." % domain],
["@", ttl, "TXT", spf_record],
@ -550,12 +539,36 @@ def _build_dns_conf(domain, ttl=3600):
["_dmarc", ttl, "TXT", '"v=DMARC1; p=none"'],
]
# Extra
extra = [
["@", ttl, "CAA", '128 issue "letsencrypt.org"']
########
# XMPP #
########
xmpp = [
["_xmpp-client._tcp", ttl, "SRV", "0 5 5222 %s." % domain],
["_xmpp-server._tcp", ttl, "SRV", "0 5 5269 %s." % domain],
["muc", ttl, "CNAME", "@"],
["pubsub", ttl, "CNAME", "@"],
["vjud", ttl, "CNAME", "@"],
["xmpp-upload", ttl, "CNAME", "@"],
]
# Official record
#########
# Extra #
#########
extra = []
if ipv4:
extra.append(["*", ttl, "A", ipv4])
if ipv6:
extra.append(["*", ttl, "AAAA", ipv6])
extra.append(["@", ttl, "CAA", '128 issue "letsencrypt.org"'])
####################
# Standard records #
####################
records = {
"basic": [{"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],
@ -563,7 +576,12 @@ def _build_dns_conf(domain, ttl=3600):
"extra": [{"name": name, "ttl": ttl, "type": type_, "value": value} for name, ttl, type_, value in extra],
}
# Custom records
##################
# Custom records #
##################
# Defined by custom hooks ships in apps for example ...
hook_results = hook_callback('custom_dns_rules', args=[domain])
for hook_name, results in hook_results.items():
#

View file

@ -258,7 +258,17 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
logger.info("Updated needed, going on...")
dns_conf = _build_dns_conf(domain)
del dns_conf["extra"] # Ignore records from the 'extra' category
for i, record in enumerate(dns_conf["extra"]):
# Ignore CAA record ... not sure why, we could probably enforce it...
if record[3] == "CAA":
del dns_conf["extra"][i]
# 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"]:
del dns_conf[category]
# Delete the old records for all domain/subdomains

View file

@ -315,9 +315,9 @@ class RedactingFormatter(Formatter):
try:
# This matches stuff like db_pwd=the_secret or admin_password=other_secret
# (the secret part being at least 3 chars to avoid catching some lines like just "db_pwd=")
# For 'key', we require to at least have one word char [a-zA-Z0-9_] before it to avoid catching "--key" used in many helpers
match = re.search(r'(pwd|pass|password|secret|\wkey|token)=(\S{3,})$', record.strip())
if match and match.group(2) not in self.data_to_redact:
# Some names like "key" or "manifest_key" are ignored, used in helpers like ynh_app_setting_set or ynh_read_manifest
match = re.search(r'(pwd|pass|password|secret|\w+key|token)=(\S{3,})$', record.strip())
if match and match.group(2) not in self.data_to_redact and match.group(1) not in ["key", "manifest_key"]:
self.data_to_redact.append(match.group(2))
except Exception as e:
logger.warning("Failed to parse line to try to identify data to redact ... : %s" % e)

View file

@ -225,6 +225,28 @@ def user_permission_reset(operation_logger, permission, sync_perm=True):
return new_permission
def user_permission_info(permission):
"""
Return informations about a specific permission
Keyword argument:
permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors)
"""
# By default, manipulate main permission
if "." not in permission:
permission = permission + ".main"
# Fetch existing permission
existing_permission = user_permission_list(full=True)["permissions"].get(permission, None)
if existing_permission is None:
raise YunohostError('permission_not_found', permission=permission)
return existing_permission
#
#
# The followings methods are *not* directly exposed.

View file

@ -316,7 +316,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
'touch %s/index.txt' % ssl_dir,
'cp %s/openssl.cnf %s/openssl.ca.cnf' % (ssl_dir, ssl_dir),
'sed -i s/yunohost.org/%s/g %s/openssl.ca.cnf ' % (domain, ssl_dir),
'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch' % (ssl_dir, ssl_dir, ssl_dir),
'openssl req -x509 -new -config %s/openssl.ca.cnf -days 3650 -out %s/ca/cacert.pem -keyout %s/ca/cakey.pem -nodes -batch -subj /CN=%s/O=%s' % (ssl_dir, ssl_dir, ssl_dir, domain, os.path.splitext(domain)[0]),
'cp %s/ca/cacert.pem /etc/ssl/certs/ca-yunohost_crt.pem' % ssl_dir,
'update-ca-certificates'
]

View file

@ -165,8 +165,8 @@ def user_create(operation_logger, username, firstname, lastname, mail, password,
operation_logger.start()
# Get random UID/GID
all_uid = {x.pw_uid for x in pwd.getpwall()}
all_gid = {x.gr_gid for x in grp.getgrall()}
all_uid = {str(x.pw_uid) for x in pwd.getpwall()}
all_gid = {str(x.gr_gid) for x in grp.getgrall()}
uid_guid_found = False
while not uid_guid_found:
@ -781,6 +781,11 @@ def user_permission_reset(permission, sync_perm=True):
sync_perm=sync_perm)
def user_permission_info(permission):
import yunohost.permission
return yunohost.permission.user_permission_info(permission)
#
# SSH subcategory
#