From cb185a6e4c98938f850caea51b0137b318c36517 Mon Sep 17 00:00:00 2001 From: orhtej2 <2871798+orhtej2@users.noreply.github.com> Date: Mon, 5 Feb 2024 23:47:54 +0100 Subject: [PATCH] Let's do it --- conf/backend.service | 82 +++++++++++++++++++ conf/config.js | 2 + conf/{systemd.service => exporter.service} | 13 ++- conf/nginx.conf | 94 +++++++++++++++++++--- scripts/install | 76 +++++++++++++---- 5 files changed, 241 insertions(+), 26 deletions(-) create mode 100644 conf/backend.service create mode 100644 conf/config.js rename conf/{systemd.service => exporter.service} (77%) diff --git a/conf/backend.service b/conf/backend.service new file mode 100644 index 0000000..ec4d5d4 --- /dev/null +++ b/conf/backend.service @@ -0,0 +1,82 @@ +[Unit] +Description=Penpot backend service (__APP__) +After=network.target +After=postgresql.service + +[Service] +Type=simple +User=__APP__ +Group=__APP__ + +Environment=PENPOT_FLAGS="enable-login-with-ldap enable-smtp" +Environment=PENPOT_LDAP_HOST=localhost +Environment=PENPOT_LDAP_PORT=389 +Environment=PENPOT_LDAP_SSL=false +Environment=PENPOT_LDAP_STARTTLS=false +Environment=PENPOT_LDAP_BASE_DN=ou=users,dc=yunohost,dc=org +Environment=PENPOT_LDAP_BIND_DN=cn=admin,dc=planetexpress,dc=com +Environment=PENPOT_LDAP_USER_QUERY=(|(uid=:username)(mail=:username)) +Environment=PENPOT_LDAP_ATTRS_USERNAME=uid +Environment=PENPOT_LDAP_ATTRS_EMAIL=mail +Environment=PENPOT_LDAP_ATTRS_FULLNAME=cn +Environment=PENPOT_DATABASE_USERNAME=__APP__ +Environment=PENPOT_DATABASE_PASSWORD=__DB_PWD__ +Environment=PENPOT_DATABASE_URI=postgresql://127.0.0.1/__DB_NAME__ +Environment=PENPOT_SMTP_DEFAULT_REPLY_TO=Penpot <__APP__@__DOMAIN__> +Environment=PENPOT_SMTP_DEFAULT_FROM=Penpot <__APP__@__DOMAIN__> +Environment=PENPOT_SMTP_HOST=localhost +Environment=PENPOT_SMTP_PORT=587 +Environment=PENPOT_SMTP_USERNAME=__MAIL_USER__ +Environment=PENPOT_SMTP_PASSWORD=__MAIL_PWD__ +Environment=PENPOT_SMTP_TLS=true +Environment=PENPOT_ASSETS_STORAGE_BACKEND=assets-fs +Environment=PENPOT_STORAGE_ASSETS_FS_DIRECTORY=__DATA_DIR__/assets +Environment=PENPOT_SECRET_KEY=__SECRET_KEY__ +Environment=PENPOT_PUBLIC_URI=https://__DOMAIN____PATH__ +Environment=PENPOT_REDIS_URI=redis://127.0.0.1:6379:__REDIS_DB__ +Environment=PENPOT_TELEMETRY_ENABLED=false +Environment=JAVA_HOME="__INSTALL_DIR__/jdk" + +WorkingDirectory=__INSTALL_DIR__/backend +ExecStart=__INSTALL_DIR__/jdk/bin/java -jar __INSTALL_DIR__/backend/penpot.jar -m app.main -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dlog4j2.configurationFile=log4j2.xml -XX:-OmitStackTraceInFastThrow --enable-preview +StandardOutput=append:/var/log/__APP__/__APP__-backend.log +StandardError=inherit +Restart=on-failure +RestartSec=10 + +# Sandboxing options to harden security +# Depending on specificities of your service/app, you may need to tweak these +# .. but this should be a good baseline +# 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 for webapps +# 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 \ No newline at end of file diff --git a/conf/config.js b/conf/config.js new file mode 100644 index 0000000..f6f2057 --- /dev/null +++ b/conf/config.js @@ -0,0 +1,2 @@ +// Frontend configuration +var penpotFlags = "disable-registration enable-login-with-ldap"; \ No newline at end of file diff --git a/conf/systemd.service b/conf/exporter.service similarity index 77% rename from conf/systemd.service rename to conf/exporter.service index 26bc490..3882c8c 100644 --- a/conf/systemd.service +++ b/conf/exporter.service @@ -1,15 +1,22 @@ [Unit] -Description=Service for Penpot (__APP__) +Description=Penpot exporter service (__APP__) After=network.target +After=postgresql.service [Service] Type=simple User=__APP__ Group=__APP__ +WorkingDirectory=__INSTALL_DIR__/exporter WorkingDirectory=__INSTALL_DIR__/ -ExecStart= -StandardOutput=append:/var/log/__APP__/__APP__.log +ExecStart=__YNH_NODE__ app.js --http-server-port=__PORT__ +Restart=always +Environment=__YNH_NODE_LOAD_PATH__ +Environment=NODE_ENV=production +Environment=PENPOT_PUBLIC_URI=https://__DOMAIN____PATH__ +Environment=PENPOT_REDIS_URI=redis://127.0.0.1:6379:__REDIS_DB__ +StandardOutput=append:/var/log/__APP__/__APP__-exporter.log StandardError=inherit Restart=on-failure RestartSec=10 diff --git a/conf/nginx.conf b/conf/nginx.conf index 7a94b04..bd65b48 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,16 +1,92 @@ #sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/assets { + proxy_pass http://127.0.0.1:__BACKEND_PORT__/assets; + recursive_error_pages on; + proxy_intercept_errors on; + error_page 301 302 307 = @handle_redirect; +} + +location __PATH__/internal/gfonts/css { + proxy_pass https://fonts.googleapis.com/css?$args; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Cross-Origin-Resource-Policy; + proxy_hide_header Link; + proxy_hide_header Alt-Svc; + proxy_hide_header Cache-Control; + proxy_hide_header Expires; + + proxy_ignore_headers Set-Cookie Vary Cache-Control Expires; + + proxy_set_header User-Agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + proxy_set_header Host "fonts.googleapis.com"; + proxy_set_header Accept "*/*"; + + add_header Access-Control-Allow-Origin $http_origin; + add_header Cache-Control max-age=86400; + add_header X-Cache-Status $upstream_cache_status; +} + +location __PATH__/internal/assets { + internal; + alias __DATA_DIR__/assets; + add_header x-internal-redirect "$upstream_http_x_accel_redirect"; +} + +location __PATH__/api/export { + proxy_pass http://127.0.0.1:__EXPORTER_PORT__; +} + +location __PATH__/api { + proxy_pass http://127.0.0.1:__BACKEND_PORT__/api; +} + +location __PATH__/ws/notifications { + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_pass http://127.0.0.1:__BACKEND_PORT__/ws/notifications; +} + location __PATH__/ { + location ~ ^/internal/gfonts/font/(?.+) { + proxy_pass https://fonts.gstatic.com/s/$font_file; - client_max_body_size 10M; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Cross-Origin-Resource-Policy; + proxy_hide_header Link; + proxy_hide_header Alt-Svc; + proxy_hide_header Cache-Control; + proxy_hide_header Expires; + proxy_hide_header Cross-Origin-Opener-Policy; + proxy_hide_header Report-To; - proxy_pass http://127.0.0.1:__PORT__; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; + proxy_ignore_headers Set-Cookie Vary Cache-Control Expires; - # preserve client IP - proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header User-Agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + proxy_set_header Host "fonts.gstatic.com"; + proxy_set_header Accept "*/*"; - # Include SSOWAT user panel's shortcut tile. - include conf.d/yunohost_panel.conf.inc; + proxy_cache penpot; + + add_header Access-Control-Allow-Origin $http_origin; + add_header Cache-Control max-age=86400; + add_header X-Cache-Status $upstream_cache_status; + } + + location ~* \.(js|css).*$ { + add_header Cache-Control "max-age=86400" always; # 24 hours + } + + location ~* \.(html).*$ { + add_header Cache-Control "no-cache, max-age=0" always; + } + + location ~ ^/(/|css|fonts|images|js|wasm) { + } + + location ~ ^/[^/]+/(.*)$ { + return 301 " __PATH__/404"; + } + + root __INSTALL_DIR/frontend/; + try_files $uri /index.html$is_args$args =404; } \ No newline at end of file diff --git a/scripts/install b/scripts/install index 7f46e04..efd4530 100644 --- a/scripts/install +++ b/scripts/install @@ -8,6 +8,12 @@ source _common.sh source /usr/share/yunohost/helpers +redis_db=$(ynh_redis_get_free_db) +secret_key=$(ynh_string_random --length=40) + +ynh_app_setting_set --app=$app --key=redis_db --value=$redis_db +ynh_app_setting_set --app=$app --key=secret_key --value=$secret_key + #================================================= # INSTALL DEPENDENCIES #================================================= @@ -48,7 +54,7 @@ chown -R $app:$app "$install_dir" #================================================= # INSTALL APP #================================================= -ynh_script_progression --message="Installing app..." --weight=5 +ynh_script_progression --message="Building frontend..." --weight=5 pushd $install_dir/build/frontend ynh_exec_as $app env $ynh_node_load_PATH yarn install --pure-lockfile @@ -63,16 +69,22 @@ pushd $install_dir/build/frontend sed -i -re "s/\%buildDate\%/$(date -R)/g" ./target/dist/index.html; popd +mkdir -p $install_dir/frontend +mv build/frontend/target/dist/* $install_dir/frontend +chown -R $app:www-data $install_dir/frontend +chmod -R 755 $install_dir/frontend + +ynh_script_progression --message="Building backend..." --weight=5 + pushd $install_dir/build/backend mkdir -p target/classes; mkdir -p target/dist; echo "$(ynh_app_upstream_version)" > target/classes/version.txt; + chown -R $app:$app target ynh_exec_as $app env PATH=$PATH JAVA_HOME=$JAVA_HOME clojure -T:build jar; mv target/penpot.jar target/dist/penpot.jar cp resources/log4j2.xml target/dist/log4j2.xml - cp scripts/run.template.sh target/dist/run.sh; - chmod +x target/dist/run.sh; # Prefetch templates mkdir builtin-templates; @@ -80,41 +92,77 @@ pushd $install_dir/build/backend cp -r builtin-templates target/dist/ popd +mkdir -p $install_dir/backend +mv build/backend/target/dist/* $install_dir/backend +chown -R $app:$app $install_dir/backend +chmod -R 700 $install_dir/backend + +ynh_script_progression --message="Building exporter..." --weight=5 + +pushd $install_dir/build/exporter + ynh_exec_as $app env PATH=$PATH JAVA_HOME=$JAVA_HOME NODE_ENV=production clojure -M:dev:shadow-cljs release main;; + cp yarn.lock target/; + cp package.json target/; + + sed -i -re "s/\%version\%/$(ynh_app_upstream_version)/g" ./target/app.js; + + +popd + +mkdir -p $install_dir/exporter +mv build/exporter/target/* $install_dir/exporter +chown -R $app:$app $install_dir/exporter +chmod -R 700 $install_dir/exporter +pushd $install_dir/exporter + ynh_exec_as $app env $ynh_node_load_PATH NODE_ENV=production yarn install --pure-lockfile + ynh_exec_as $app env $ynh_node_load_PATH NODE_ENV=production yarn --network-timeout 1000000 run playwright install chromium +popd + #================================================= # SYSTEM CONFIGURATION #================================================= -#ynh_script_progression --message="Adding system configurations related to $app..." +ynh_script_progression --message="Adding system configurations related to $app..." # Create a dedicated NGINX config using the conf/nginx.conf template -#ynh_add_nginx_config +ynh_add_nginx_config # Create a dedicated systemd config -#ynh_add_systemd_config +ynh_add_systemd_config --template="backend.service" --service="$app-backend" +yunohost service add $app-backend --log="/var/log/$app/$app-backend.log" -#yunohost service add $app --log="/var/log/$app/$app.log" +ynh_add_systemd_config --template="exporter.service" --service="$app-exporter" +yunohost service add $app-exporter --log="/var/log/$app/$app-exporter.log" # Use logrotate to manage application logfile(s) -#ynh_use_logrotate +mkdir -p /var/log/$app +touch /var/log/$app/$app-backend.log +touch /var/log/$app/$app-exporter.log +chown -R $app: /var/log/$app + +ynh_use_logrotate --logfile="/var/log/$app/$app-backend.log" +ynh_use_logrotate --logfile="/var/log/$app/$app-exporter.log" #================================================= # APP INITIAL CONFIGURATION #================================================= # ADD A CONFIGURATION #================================================= -# ynh_script_progression --message="Adding app's configuration file..." +ynh_script_progression --message="Adding app's configuration file..." -# ynh_add_config --template="" --destination="$install_dir/" +ynh_add_config --template="config.js" --destination="$install_dir/frontend/js/config.js" -# chmod 400 "$install_dir/" -# chown $app:$app "$install_dir/" +#chmod 400 "$install_dir/frontend/js/config.js" +#chown $app:$app "$install_dir/" #================================================= # START SYSTEMD SERVICE #================================================= -# ynh_script_progression --message="Starting app's systemd service..." +ynh_script_progression --message="Starting a systemd service..." --weight=1 # Start a systemd service -# ynh_systemd_action --service_name=$app --action="start" --log_path="/var/log/$app/$app.log" +#ynh_systemd_action --service_name=$app-api --action="start" --log_path="/var/log/$app/$app-api.log" --line_match="HTTP Server is listening on" + +#ynh_systemd_action --service_name=$app-proxy --action="start" --log_path="/var/log/$app/$app-proxy.log" --line_match="Running server!" #================================================= # END OF SCRIPT