#!/bin/bash # _____ _ _ # | __|___ ___ ___ _| |___ _____| |_ ___ ___ ___ # | __| _| -_| -_| . | . | | . | . | | -_| # |__| |_| |___|___|___|___|_|_|_|___|___|_|_|___| # # Freedom in the Cloud # # Web based administration user interface # typically abailanle on http://freedombone.local # # A background daemon running the freedombone-installer # command is used to add and remove apps and handle # initial setup of the box # # License # ======= # # Copyright © 2014-2018 Bob Mottram <bob@freedombone.net> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # 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/>. function web_admin_configure_installer_daemon { if [ -f /etc/systemd/system/webadmin.service ]; then return fi { echo '[Unit]'; echo 'Description=Installer daemon for web admin'; echo 'After=network.target'; echo ''; echo '[Service]'; echo 'User=root'; echo "ExecStart=/usr/local/bin/${PROJECT_NAME}-installer"; echo "ExecReload=/bin/kill \$MAINPID"; echo 'KillMode=process'; echo 'Restart=always'; echo ''; echo '[Install]'; echo 'WantedBy=multi-user.target'; echo 'Alias=pleroma.service'; } > /etc/systemd/system/webadmin.service systemctl enable webadmin systemctl daemon-reload systemctl start webadmin } function web_admin_avahi { if [ -f /etc/avahi/services/webadmin.service ]; then return fi { echo '<?xml version="1.0" standalone="no"?><!--*-nxml-*-->'; echo '<!DOCTYPE service-group SYSTEM "avahi-service.dtd">'; echo '<service-group>'; echo ' <name replace-wildcards="yes">%h http</name>'; echo ' <service>'; echo ' <type>_http._tcp</type>'; echo ' <port>80</port>'; echo ' </service>'; echo ' <service>'; echo ' <type>_https._tcp</type>'; echo ' <port>443</port>'; echo ' </service>'; echo '</service-group>'; } > /etc/avahi/services/webadmin.service systemctl restart avahi-daemon } function web_admin_create_users { ADMIN_USER=$(get_completion_param "Admin user") local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local users_file="/var/www/${local_hostname}/htdocs/admin/users.html" cp "/var/www/${local_hostname}/htdocs/admin/users_template.html" "$users_file" sed -i '/users list/,/end of users/d' "$users_file" sed -i '/<\/body>/d' "$users_file" sed -i '/<\/html>/d' "$users_file" echo " <div class=\"row\">" >> "$users_file" echo " <div class=\"column\">" >> "$users_file" rm "/var/www/${local_hostname}/htdocs/admin/userprofile_"* for d in /home/*/ ; do USERNAME=$(echo "$d" | awk -F '/' '{print $3}') if [[ $(is_valid_user "$USERNAME") == "1" ]]; then userfile="/var/www/${local_hostname}/htdocs/admin/userprofile_${USERNAME}.html" useremail=${USERNAME}@${DEFAULT_DOMAIN_NAME} GPG_ID=$(su -c "gpg --list-keys '$useremail'" - "$USERNAME" | sed -n '2p' | sed 's/^[ \t]*//') if [ -f "/home/$USERNAME/.gnupg/gpg.conf" ]; then if grep -q "default-key" "/home/$USERNAME/.gnupg/gpg.conf"; then default_gpg_key=$(grep "default-key" "/home/$USERNAME/.gnupg/gpg.conf") if [[ "$default_gpg_key" != *'#'* ]]; then default_gpg_key=$(grep "default-key" "/home/$USERNAME/.gnupg/gpg.conf" | awk -F ' ' '{print $2}') if [ ${#default_gpg_key} -gt 3 ]; then GPG_ID=$(su -c "gpg --list-keys '$default_gpg_key'" - "$USERNAME" | sed -n '2p' | sed 's/^[ \t]*//') fi fi fi fi pubkey_qrcode="/var/www/${local_hostname}/htdocs/admin/userprofile_${USERNAME}.png" su -c "gpg --armor --export \"$GPG_ID\"" - "$USERNAME" | qrencode -t PNG -o "$pubkey_qrcode" { echo ' <div class="chip">'; echo " <a href=\"userprofile_${USERNAME}.html\">"; echo ' <img src="admin_users.png" alt="Person" width="96" height="96">'; echo " $USERNAME"; echo ' </a>'; echo ' </div>'; } >> "$users_file" cp "/var/www/${local_hostname}/htdocs/admin/userprofile.html" "$userfile" sed -i "s|USERNAME|${USERNAME}|g" "$userfile" if [[ "$USERNAME" == "$ADMIN_USER" ]]; then sed -i "s|USERTYPE|Admin|g" "$userfile" sed -i '/remove user button/,/end of remove/d' "$userfile" else sed -i "s|USERTYPE|User|g" "$userfile" fi sed -i "s|USEREMAIL|${useremail}|g" "$userfile" sed -i "s|USERGPG|${GPG_ID}|g" "$userfile" chown www-data:www-data "$userfile" chown www-data:www-data "$pubkey_qrcode" fi done { echo ' <div class="chip">'; echo " <a href=\"newuser.html\">"; echo ' <img src="admin_users.png" alt="Person" width="96" height="96">'; echo ' +'; echo ' </a>'; echo ' </div>'; echo ' </div>'; echo ' </div>'; echo ' </body>'; echo '</html>'; } >> "$users_file" chown www-data:www-data "$users_file" } function web_admin_create_add_apps { read_config_param DDNS_PROVIDER local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local apps_add_template_filename="/var/www/${local_hostname}/htdocs/admin/apps_add_template.html" appslist_add_filename="/var/www/${local_hostname}/htdocs/admin/apps_add.html" pending_installs="/var/www/${local_hostname}/htdocs/admin/pending_installs.txt" icons_dir="/var/www/${local_hostname}/htdocs/admin/icons" app_add_template_filename="/var/www/${local_hostname}/htdocs/admin/app_add_template.html" FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*" if [ ! -d "$icons_dir" ]; then mkdir -p "$icons_dir" fi if [ ! -f "$apps_add_template_filename" ]; then return fi cp "$apps_add_template_filename" "$appslist_add_filename" sed -i '/<\/body>/d' "$appslist_add_filename" sed -i '/<\/html>/d' "$appslist_add_filename" available_apps_ctr=0 app_index=0 for filename in $FILES do app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}') app_index=0 app_is_installed= # shellcheck disable=SC2068 for a in ${APPS_INSTALLED[@]} do installed_app_name=${APPS_INSTALLED_NAMES[$app_index]} if [[ "$installed_app_name" == "$app_name" ]]; then app_is_installed=1 break fi app_index=$((app_index+1)) done # check if the app is pending installation if [ -f "$pending_installs" ]; then if grep -q "install_${app_name}" "$pending_installs"; then app_is_installed=1 fi fi if [ ! $app_is_installed ]; then app_filename="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}" if [ -f "$app_filename" ]; then # get the icon for the app icon_filename="/usr/share/${PROJECT_NAME}/android-app/${app_name}.png" if [ -f "$icon_filename" ]; then cp "$icon_filename" "/var/www/${local_hostname}/htdocs/admin/icons/${app_name}.png" else icon_filename= continue fi app_name_upper=$(echo "$app_name" | awk '{print toupper($0)}') SHORT_DESCRIPTION= DESCRIPTION= if ! grep -q "${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename"; then continue fi if grep -q "#${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename"; then continue fi SHORT_DESCRIPTION="$(grep "${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename" | head -n 1 | sed 's|\$||g' | sed "s|'||g" | sed 's|\"||g' | awk -F '=' '{print $2}')" if grep -q "${app_name_upper}_DESCRIPTION=" "$app_filename"; then DESCRIPTION="$(grep "${app_name_upper}_DESCRIPTION=" "$app_filename" | head -n 1 | sed 's|\$||g' | sed "s|'||g" | sed 's|\"||g' | awk -F '=' '{print $2}')" fi if [ $available_apps_ctr -eq 0 ]; then echo ' <div class="row">' >> "$appslist_add_filename" fi filename="/var/www/${local_hostname}/htdocs/admin/app_${app_name}.html" if [ -f "$filename" ]; then rm "$filename" fi filename="/var/www/${local_hostname}/htdocs/admin/app_add_${app_name}.html" { echo ' <div class="column">'; echo ' <div>'; echo " <a href=\"app_add_${app_name}.html\">"; echo " <img src=\"icons/${app_name}.png\" style=\"width:100%\">"; echo " <center>${app_name}</center>"; echo ' </a>'; echo ' </div>'; echo ' </div>'; } >> "$appslist_add_filename" cp "$app_add_template_filename" "$filename" # Replace app variables sed -i "s|HOSTNAME|$(hostname)|g" "$filename" sed -i "s|APPNAME|${app_name}|g" "$filename" sed -i "s|APPDESCRIPTION|${DESCRIPTION}|g" "$filename" # remove freedns if necessary if [[ "$DDNS_PROVIDER" != *"freedns"* ]]; then sed -i '/freedns_code/d' "$filename" fi available_apps_ctr=$((available_apps_ctr+1)) # four columns per row if [ $available_apps_ctr -eq 4 ]; then echo ' </div>' >> "$appslist_add_filename" available_apps_ctr=0 fi fi fi done # Complete the rest of the four column row # shellcheck disable=SC2034 for i in $(seq ${available_apps_ctr} 3) do { echo ' <div class="column">'; echo ' <div>'; echo ' </div>'; echo ' </div>'; } >> "$appslist_add_filename" done echo ' </body>' >> "$appslist_add_filename" echo '</html>' >> "$appslist_add_filename" chown -R www-data:www-data "/var/www/${local_hostname}/htdocs/admin" } function web_admin_create_installed_apps { local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local appslist_template_filename="/var/www/${local_hostname}/htdocs/admin/apps_template.html" app_template_filename="/var/www/${local_hostname}/htdocs/admin/app_template.html" appslist_filename="/var/www/${local_hostname}/htdocs/admin/apps.html" icons_dir="/var/www/${local_hostname}/htdocs/admin/icons" pending_removes="/var/www/${local_hostname}/htdocs/admin/pending_removes.txt" if [ ! -d "$icons_dir" ]; then mkdir -p "$icons_dir" fi if [ ! -f "$appslist_template_filename" ]; then return fi cp "$appslist_template_filename" "$appslist_filename" sed -i '/<\/body>/d' "$appslist_filename" sed -i '/<\/html>/d' "$appslist_filename" installed_apps_ctr=0 app_index=0 # shellcheck disable=SC2068,SC2034 for a in ${APPS_INSTALLED[@]} do app_name=${APPS_INSTALLED_NAMES[$app_index]} if [ "$app_name" ]; then app_filename="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}" if [ -f "$app_filename" ]; then # check if the app is being removed app_being_removed= if [ -f "$pending_removes" ]; then if grep -q "remove_${app_name}" "$pending_removes"; then app_being_removed=1 fi fi if [ ! $app_being_removed ]; then # get the icon for the app icon_filename="/usr/share/${PROJECT_NAME}/android-app/${app_name}.png" if [ -f "$icon_filename" ]; then cp "$icon_filename" "/var/www/${local_hostname}/htdocs/admin/icons/${app_name}.png" else icon_filename= fi app_name_upper=$(echo "$app_name" | awk '{print toupper($0)}') SHORT_DESCRIPTION= DESCRIPTION= if ! grep -q "${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename"; then app_index=$((app_index+1)) continue fi if grep -q "#${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename"; then app_index=$((app_index+1)) continue fi SHORT_DESCRIPTION="$(grep "${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename" | head -n 1 | sed 's|\$||g' | sed "s|'||g" | sed 's|\"||g' | awk -F '=' '{print $2}')" if grep -q "${app_name_upper}_DESCRIPTION=" "$app_filename"; then DESCRIPTION="$(grep "${app_name_upper}_DESCRIPTION=" "$app_filename" | head -n 1 | sed 's|\$||g' | sed "s|'||g" | sed 's|\"||g' | awk -F '=' '{print $2}')" fi read_config_param "${app_name_upper}_DOMAIN_NAME" test_domain_name="${app_name_upper}_DOMAIN_NAME" domain_name=${!test_domain_name} if [ "$domain_name" ]; then if [[ "$domain_name" != *'.onion' ]]; then domain_name="https://${!test_domain_name}" else domain_name="http://${!test_domain_name}" fi fi if [ $installed_apps_ctr -eq 0 ]; then echo ' <div class="row">' >> "$appslist_filename" fi filename="/var/www/${local_hostname}/htdocs/admin/app_add_${app_name}.html" if [ -f "$filename" ]; then rm "$filename" fi filename="/var/www/${local_hostname}/htdocs/admin/app_${app_name}.html" { echo ' <div class="column">'; echo ' <div>'; echo " <a href=\"app_${app_name}.html\">"; echo " <img src=\"icons/${app_name}.png\" style=\"width:100%\">"; echo " <center>${app_name}</center>"; echo ' </a>'; echo ' </div>'; echo ' </div>'; } >> "$appslist_filename" cp "$app_template_filename" "$filename" # Replace app variables sed -i "s|HOSTNAME|$(hostname)|g" "$filename" sed -i "s|APPNAME|${app_name}|g" "$filename" sed -i "s|APPURL|${domain_name}|g" "$filename" sed -i "s|APPDESCRIPTION|${DESCRIPTION}|g" "$filename" installed_apps_ctr=$((installed_apps_ctr+1)) # four columns per row if [ $installed_apps_ctr -eq 4 ]; then echo ' </div>' >> "$appslist_filename" installed_apps_ctr=0 fi fi fi fi app_index=$((app_index+1)) done # Complete the rest of the four column row # shellcheck disable=SC2034 for i in $(seq ${installed_apps_ctr} 3) do { echo ' <div class="column">'; echo ' <div>'; echo ' </div>'; echo ' </div>'; } >> "$appslist_filename" done echo ' </body>' >> "$appslist_filename" echo '</html>' >> "$appslist_filename" chown -R www-data:www-data "/var/www/${local_hostname}/htdocs/admin" } function install_web_admin { # This is intended as an admin web user interface # similar to Plinth or the yunohost local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local if [ ! -d "/var/www/${local_hostname}/htdocs/admin" ]; then mkdir -p "/var/www/${local_hostname}/htdocs/admin" fi if [ -d "/usr/share/${PROJECT_NAME}/webadmin" ]; then cp -r "/usr/share/${PROJECT_NAME}/webadmin"/* "/var/www/${local_hostname}/htdocs/admin" else echo "install_web_admin 5" > /tmp/debugweb { echo '<html>'; echo ' <body>'; echo " This is a placeholder for the web admin panel on ${local_hostname}"; echo ' </body>'; echo '</html>'; } > "/var/www/${local_hostname}/htdocs/admin/index.html" fi nginx_file=/etc/nginx/sites-available/$local_hostname { echo 'server {'; echo ' listen 80 default_server;'; echo ' #listen [::]:80;'; echo " server_name ${local_hostname};"; echo " root /var/www/${local_hostname}/htdocs;"; echo ' index index.html;'; echo ''; echo ' access_log /dev/null;'; echo ' error_log /dev/null;'; echo ''; echo ' location ^~ /admin {'; echo " root /var/www/${local_hostname}/htdocs;"; echo ' index index.html;'; echo " error_page 405 = \$uri;"; echo ' location ~ \.php {'; echo ' include snippets/fastcgi-php.conf;'; echo ' fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;'; echo ' fastcgi_read_timeout 30;'; echo ' }'; echo ' }'; echo ''; echo ' location /icons {'; echo ' autoindex on;'; echo ' break;'; echo ' }'; echo ''; echo ' rewrite ^/plinth/(.*)$ /api.json last;'; echo ''; echo ' location / {'; echo " root /var/www/${local_hostname}/htdocs/plinth;"; echo ' index api.json /api.json;'; echo " error_page 405 = \$uri;"; echo ' }'; echo '}'; echo ''; echo 'server {'; echo ' listen 443 default_server ssl;'; echo ' #listen [::]:443 ssl;'; echo " server_name ${local_hostname};"; echo " root /var/www/${local_hostname}/htdocs;"; echo ' index index.html;'; echo ''; echo ' access_log /dev/null;'; echo ' error_log /dev/null;'; echo ''; } > "$nginx_file" nginx_ssl "${local_hostname}" nginx_security_options "${local_hostname}" { echo ' add_header Strict-Transport-Security max-age=0;'; echo ''; echo ' location ^~ /admin {'; echo " root /var/www/${local_hostname}/htdocs;"; echo ' index index.html;'; echo " error_page 405 = \$uri;"; echo ' location ~ \.php {'; echo ' include snippets/fastcgi-php.conf;'; echo ' fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;'; echo ' fastcgi_read_timeout 30;'; echo ' }'; echo ' }'; echo ''; echo ' location /icons {'; echo ' autoindex on;'; echo ' break;'; echo ' }'; echo ''; echo ' rewrite ^/plinth/(.*)$ /api.json last;'; echo ''; echo ' location / {'; echo " root /var/www/${local_hostname}/htdocs/plinth;"; echo ' index api.json /api.json;'; echo " error_page 405 = \$uri;"; echo ' }'; echo '}'; } >> "$nginx_file" if [ ! -f "/etc/ssl/certs/${local_hostname}.crt" ]; then "${PROJECT_NAME}-addcert" -h "${local_hostname}" --dhkey "${DH_KEYLENGTH}" fi sed -i "s|ssl_certificate .*|ssl_certificate /etc/ssl/certs/${local_hostname}.crt;|g" "$nginx_file" sed -i "s|ssl_certificate_key .*|ssl_certificate_key /etc/ssl/private/${local_hostname}.key;|g" "$nginx_file" nginx_ensite "${local_hostname}" # Compatibility with FreedomBox android app # The installed apps get published to a json file called api.json # in this directory if [ ! -d "/var/www/${local_hostname}/htdocs/plinth" ]; then mkdir -p "/var/www/${local_hostname}/htdocs/plinth" fi web_admin_create_users # make list of apps which can be added web_admin_create_add_apps # make the list of apps web_admin_create_installed_apps chown -R www-data:www-data "/var/www/${local_hostname}/htdocs" web_admin_avahi web_admin_configure_installer_daemon } # NOTE: deliberately no exit 0